You are here: Home » Display Version and Other SharePoint Metadata in Word 2003 Documents

Display Version and Other SharePoint Metadata in Word 2003 Documents

Posted by johnwpowell
No Comments »

You can view and edit text columns from a document library from a Word 2003 document. For example: 

This is a great integration point between Office and SharePoint, but there are a couple a gotcha's you should be aware of, and the following very reasonable and realistic requirements for a document management solution will highlight them.

  1. The version and author from SharePoint should be displayed in the header of Word 2003 documents in the document library
  2. The version and author from SharePoint should be updated automatically when users open or print documents from the document library

The first issue is that only SharePoint text columns can be synchronized into Word document properties.  Version is not a text column.  The second issue is that the fields used to display the metadata in Word are not updated automatically when opening or printing the document.  I will present a solution (or workaround) for both issues, but first let's examine how the integration works.

When a Word document is created from or uploaded to a SharePoint document library, text columns are copied to Word document properties.  SharePoint will create the document properties for you when the document is first added, but not when it is updated.  You can always manually add a document property if you need to–just make sure the name exactly matches the SharePoint text column name.

Here's a simple example to illustrate the integration.

Add a text column to your document library:

For testing purposes, edit one of the document library items and enter some text in the custom column:

Now open the document in Word 2003 and examine the document properties.  Select FileProperties.  If you see the following dialog, click on File Properties.

Click on the Custom tab and note that the MyCustomColumn document property exists and contains the text from the SharePoint list item.

To demonstrate the metadata is two-way, edit the value of MyCustomColumn (the UI is slightly cloogy here).  Click on the MyCustomColumn in the bottom list.  Enter a value in the Value field and then click the Modify button:

Save the document back to SharePoint and notice the column is updated.  This is great if want two-way updates, but bad if you want it to be read-only in Word.

In order to display the version number, we need a text column that contains the version number.  One way to accomplish this is to use an event handler to copy the version number to a read-only text column.  Here is the code for the event receiver.  I'll leave it to you how you want to deploy the event receiver but a simple way is write a program to register the event receiver on the document library.

    //****************************************************************
    /// DocumentVersionEventReciever
    /// <summary>
    /// An event receiver to provide a custom version number
    /// text field that can be displayed in Word 2003.
    /// </summary>
    //****************************************************************
    public class DocumentVersionEventReciever : SPItemEventReceiver
    {
        #region Constants

        private const string FIELD_DOC_VERS = "DocumentVersion";
        private const string FIELD_ITEM_VERS = "Version";

        #endregion Constants

        #region Methods

        //****************************************************************
        /// SetDocumentVersion
        /// <summary>
        /// Copies the document version to a text field so it can be
        /// sychronized with a Word property.
        /// </summary>
        /// <param name="item"></param>
        //****************************************************************
        private void SetDocumentVersion(SPListItem item)
        {
            item[FIELD_DOC_VERS] = item[FIELD_ITEM_VERS];
            item.SystemUpdate(false);

        } // end SetDocumentVersion()

        //****************************************************************
        /// DeleteDocumentVersionField
        /// <summary>
        /// Deletes the document version field.
        /// </summary>
        /// <param name="list"></param>
        //****************************************************************
        private void DeleteDocumentVersionField(SPList list)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                list.Fields[FIELD_DOC_VERS].ReadOnlyField = false;
                list.Fields[FIELD_DOC_VERS].Update();
                list.Fields[FIELD_DOC_VERS].Delete();

                // Check
                if (list.Fields.ContainsField(FIELD_DOC_VERS))
                    throw new ApplicationException("Failed to delete field '" + FIELD_DOC_VERS + "'.");

            }); // end run with elevated

        } // end DeleteDocumentVersionField()

        //****************************************************************
        /// CreateDocumentVersionField
        /// <summary>
        /// Creates the document Version field.
        /// </summary>
        /// <param name="list"></param>
        //****************************************************************
        private void CreateDocumentVersionField(SPList list)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                list.Fields.Add(FIELD_DOC_VERS, SPFieldType.Text, false);
                SPField fld = list.Fields[FIELD_DOC_VERS];
                fld.ReadOnlyField = true;
                fld.Update();
                list.Update();

                // Check
                if (!list.Fields.ContainsField(FIELD_DOC_VERS))
                    throw new ApplicationException("Failed to create field '" + FIELD_DOC_VERS + "'.");

                // Populate existing Versions
                foreach (SPListItem liExisting in list.Items)
                {
                    SetDocumentVersion(liExisting);
                }

            }); // end run with elevated

        } // end CreateDocumentVersionField()

        //****************************************************************
        /// SetDocumentMetaData
        /// <summary>
        /// Sets document metadata.
        /// </summary>
        /// <param name="properties"></param>
        //****************************************************************
        private void SetDocumentMetaData(SPItemEventProperties properties)
        {
            try
            {
                // Prevent triggering of other events
                this.DisableEventFiring();

                SPListItem li = properties.ListItem;
                SPList list = properties.ListItem.ParentList;

                //DeleteDocumentVersionField(list);

                // Add field if doesn't exist
                if (!list.Fields.ContainsField(FIELD_DOC_VERS))
                {
                    CreateDocumentVersionField(list);
                }
                else
                {
                    SetDocumentVersion(li);
                }
            }
            catch (Exception ex)
            {
                properties.ErrorMessage = ex.Message;
            }
            finally
            {
                // Enable triggering of other events
                this.EnableEventFiring();
            }

        } // end SetDocumentMetaData()

        #endregion Methods

        #region Event Handlers

        //****************************************************************
        /// ItemUncheckedOut
        /// <summary>
        /// The event that occurs when an item is checked out.
        /// </summary>
        /// <param name="properties"></param>
        //****************************************************************
        public override void ItemUncheckedOut(SPItemEventProperties properties)
        {
            SetDocumentMetaData(properties);

        } // end ItemUncheckedOut()

        //****************************************************************
        /// ItemCheckedOut
        /// <summary>
        /// The event that occurs when an item is checked out.
        /// </summary>
        /// <param name="properties"></param>
        //****************************************************************
        public override void ItemCheckedOut(SPItemEventProperties properties)
        {
            SetDocumentMetaData(properties);

        } // end ItemCheckedOut()

        //****************************************************************
        /// ItemCheckedIn
        /// <summary>
        /// The event that occurs when an item is checked in.
        /// </summary>
        /// <param name="properties"></param>
        //****************************************************************
        public override void ItemCheckedIn(SPItemEventProperties properties)
        {
            SetDocumentMetaData(properties);

        } // end ItemCheckedIn()

        //****************************************************************
        /// ItemUpdated
        /// <summary>
        /// The event that occurs when an item is updated.
        /// </summary>
        /// <param name="properties"></param>
        //****************************************************************
        public override void ItemUpdated(SPItemEventProperties properties)
        {
            SetDocumentMetaData(properties);

        } // end ItemUpdated()

        //****************************************************************
        /// ItemAdded
        /// <summary>
        /// The event that occurs when an item is added to the list.
        /// </summary>
        /// <param name="properties">Properties about the item</param>
        //****************************************************************
        public override void ItemAdded(SPItemEventProperties properties)
        {
            SetDocumentMetaData(properties);

        } // end ItemAdded()

        #endregion EventHandlers
    }

You can see that the event receiver is ensuring the text column DocumentVersion always has the latest copy of the version number.  I do have a criticism of this code, and that is the creation of the column by the event receiver.  In a production envrionment, I would recommend creating a feature that creates a content type and hooks an event receiver to that content type.

Insert the DocumentVersion field in the document header (or wherever you want the version to appear).  From the menu, select Insert..Field.  Find the DocProperty field name and then select the DocumentVersion field property.

The version will appear in the document, and that leads into the next issue.  The field won't update until the user clicks on the field and selects Update Field.  (There is also an option you can set to update fields before printing, but as you recall, the requirement is to display the current version number when the document opens).

To illustrate the issue, check out the document in SharePoint and check it back in (or publish a major version).  Then open the document in Word.  You will see the field displays the old version number.  If you access the DocumentVersion property, you will see it has the correct value and the field just needs to be updated.  We are going to solve that issue with a Macro.

In Word, select ToolsMacrosMacro.  Then click on Create to create a new Macro.  Name the macro AutoOpen, and place the following code in the editor:

Sub AutoOpen()
    With Options
        .UpdateFieldsAtPrint = True
        .UpdateLinksAtPrint = True
    End With
    ActiveDocument.Fields.Update
End Sub

This code is setting the option to update fields and links before printing.  More importantly, it is updating all fields when the document opens.  You have a couple of choices for deploying this.  I'd recommend making this document the template for the document library.  That way, if users create documents from the document library, the properties, fields and macro is all setup.

I would caution you to set users expectations with this solution / workaround.  If they upload documents from outside SharePoint they obviously will not have the fields set up in the Word document to display the version number.  They also will not have the macro. 

I hope you find this solution helpful for your Office 2003 users.  If Office 2007 is an option, I recommend investigating the labeling and bar coding features of SharePoint 2007 first.

 

Your email is never shared.
Required fields are marked *




Allowed tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>


Display Version and Other SharePoint Metadata in Word 2003 Documents

Posted by johnwpowell
No Comments »

You can view and edit text columns from a document library from a Word 2003 document. For example: 

This is a great integration point between Office and SharePoint, but there are a couple a gotcha's you should be aware of, and the following very reasonable and realistic requirements for a document management solution will highlight them.

  1. The version and author from SharePoint should be displayed in the header of Word 2003 documents in the document library
  2. The version and author from SharePoint should be updated automatically when users open or print documents from the document library

The first issue is that only SharePoint text columns can be synchronized into Word document properties.  Version is not a text column.  The second issue is that the fields used to display the metadata in Word are not updated automatically when opening or printing the document.  I will present a solution (or workaround) for both issues, but first let's examine how the integration works.

When a Word document is created from or uploaded to a SharePoint document library, text columns are copied to Word document properties.  SharePoint will create the document properties for you when the document is first added, but not when it is updated.  You can always manually add a document property if you need to–just make sure the name exactly matches the SharePoint text column name.

Here's a simple example to illustrate the integration.

Add a text column to your document library:

For testing purposes, edit one of the document library items and enter some text in the custom column:

Now open the document in Word 2003 and examine the document properties.  Select FileProperties.  If you see the following dialog, click on File Properties.

Click on the Custom tab and note that the MyCustomColumn document property exists and contains the text from the SharePoint list item.

To demonstrate the metadata is two-way, edit the value of MyCustomColumn (the UI is slightly cloogy here).  Click on the MyCustomColumn in the bottom list.  Enter a value in the Value field and then click the Modify button:

Save the document back to SharePoint and notice the column is updated.  This is great if want two-way updates, but bad if you want it to be read-only in Word.

In order to display the version number, we need a text column that contains the version number.  One way to accomplish this is to use an event handler to copy the version number to a read-only text column.  Here is the code for the event receiver.  I'll leave it to you how you want to deploy the event receiver but a simple way is write a program to register the event receiver on the document library.

    //****************************************************************
    /// DocumentVersionEventReciever
    /// <summary>
    /// An event receiver to provide a custom version number
    /// text field that can be displayed in Word 2003.
    /// </summary>
    //****************************************************************
    public class DocumentVersionEventReciever : SPItemEventReceiver
    {
        #region Constants

        private const string FIELD_DOC_VERS = "DocumentVersion";
        private const string FIELD_ITEM_VERS = "Version";

        #endregion Constants

        #region Methods

        //****************************************************************
        /// SetDocumentVersion
        /// <summary>
        /// Copies the document version to a text field so it can be
        /// sychronized with a Word property.
        /// </summary>
        /// <param name="item"></param>
        //****************************************************************
        private void SetDocumentVersion(SPListItem item)
        {
            item[FIELD_DOC_VERS] = item[FIELD_ITEM_VERS];
            item.SystemUpdate(false);

        } // end SetDocumentVersion()

        //****************************************************************
        /// DeleteDocumentVersionField
        /// <summary>
        /// Deletes the document version field.
        /// </summary>
        /// <param name="list"></param>
        //****************************************************************
        private void DeleteDocumentVersionField(SPList list)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                list.Fields[FIELD_DOC_VERS].ReadOnlyField = false;
                list.Fields[FIELD_DOC_VERS].Update();
                list.Fields[FIELD_DOC_VERS].Delete();

                // Check
                if (list.Fields.ContainsField(FIELD_DOC_VERS))
                    throw new ApplicationException("Failed to delete field '" + FIELD_DOC_VERS + "'.");

            }); // end run with elevated

        } // end DeleteDocumentVersionField()

        //****************************************************************
        /// CreateDocumentVersionField
        /// <summary>
        /// Creates the document Version field.
        /// </summary>
        /// <param name="list"></param>
        //****************************************************************
        private void CreateDocumentVersionField(SPList list)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                list.Fields.Add(FIELD_DOC_VERS, SPFieldType.Text, false);
                SPField fld = list.Fields[FIELD_DOC_VERS];
                fld.ReadOnlyField = true;
                fld.Update();
                list.Update();

                // Check
                if (!list.Fields.ContainsField(FIELD_DOC_VERS))
                    throw new ApplicationException("Failed to create field '" + FIELD_DOC_VERS + "'.");

                // Populate existing Versions
                foreach (SPListItem liExisting in list.Items)
                {
                    SetDocumentVersion(liExisting);
                }

            }); // end run with elevated

        } // end CreateDocumentVersionField()

        //****************************************************************
        /// SetDocumentMetaData
        /// <summary>
        /// Sets document metadata.
        /// </summary>
        /// <param name="properties"></param>
        //****************************************************************
        private void SetDocumentMetaData(SPItemEventProperties properties)
        {
            try
            {
                // Prevent triggering of other events
                this.DisableEventFiring();

                SPListItem li = properties.ListItem;
                SPList list = properties.ListItem.ParentList;

                //DeleteDocumentVersionField(list);

                // Add field if doesn't exist
                if (!list.Fields.ContainsField(FIELD_DOC_VERS))
                {
                    CreateDocumentVersionField(list);
                }
                else
                {
                    SetDocumentVersion(li);
                }
            }
            catch (Exception ex)
            {
                properties.ErrorMessage = ex.Message;
            }
            finally
            {
                // Enable triggering of other events
                this.EnableEventFiring();
            }

        } // end SetDocumentMetaData()

        #endregion Methods

        #region Event Handlers

        //****************************************************************
        /// ItemUncheckedOut
        /// <summary>
        /// The event that occurs when an item is checked out.
        /// </summary>
        /// <param name="properties"></param>
        //****************************************************************
        public override void ItemUncheckedOut(SPItemEventProperties properties)
        {
            SetDocumentMetaData(properties);

        } // end ItemUncheckedOut()

        //****************************************************************
        /// ItemCheckedOut
        /// <summary>
        /// The event that occurs when an item is checked out.
        /// </summary>
        /// <param name="properties"></param>
        //****************************************************************
        public override void ItemCheckedOut(SPItemEventProperties properties)
        {
            SetDocumentMetaData(properties);

        } // end ItemCheckedOut()

        //****************************************************************
        /// ItemCheckedIn
        /// <summary>
        /// The event that occurs when an item is checked in.
        /// </summary>
        /// <param name="properties"></param>
        //****************************************************************
        public override void ItemCheckedIn(SPItemEventProperties properties)
        {
            SetDocumentMetaData(properties);

        } // end ItemCheckedIn()

        //****************************************************************
        /// ItemUpdated
        /// <summary>
        /// The event that occurs when an item is updated.
        /// </summary>
        /// <param name="properties"></param>
        //****************************************************************
        public override void ItemUpdated(SPItemEventProperties properties)
        {
            SetDocumentMetaData(properties);

        } // end ItemUpdated()

        //****************************************************************
        /// ItemAdded
        /// <summary>
        /// The event that occurs when an item is added to the list.
        /// </summary>
        /// <param name="properties">Properties about the item</param>
        //****************************************************************
        public override void ItemAdded(SPItemEventProperties properties)
        {
            SetDocumentMetaData(properties);

        } // end ItemAdded()

        #endregion EventHandlers
    }

You can see that the event receiver is ensuring the text column DocumentVersion always has the latest copy of the version number.  I do have a criticism of this code, and that is the creation of the column by the event receiver.  In a production envrionment, I would recommend creating a feature that creates a content type and hooks an event receiver to that content type.

Insert the DocumentVersion field in the document header (or wherever you want the version to appear).  From the menu, select Insert..Field.  Find the DocProperty field name and then select the DocumentVersion field property.

The version will appear in the document, and that leads into the next issue.  The field won't update until the user clicks on the field and selects Update Field.  (There is also an option you can set to update fields before printing, but as you recall, the requirement is to display the current version number when the document opens).

To illustrate the issue, check out the document in SharePoint and check it back in (or publish a major version).  Then open the document in Word.  You will see the field displays the old version number.  If you access the DocumentVersion property, you will see it has the correct value and the field just needs to be updated.  We are going to solve that issue with a Macro.

In Word, select ToolsMacrosMacro.  Then click on Create to create a new Macro.  Name the macro AutoOpen, and place the following code in the editor:

Sub AutoOpen()
    With Options
        .UpdateFieldsAtPrint = True
        .UpdateLinksAtPrint = True
    End With
    ActiveDocument.Fields.Update
End Sub

This code is setting the option to update fields and links before printing.  More importantly, it is updating all fields when the document opens.  You have a couple of choices for deploying this.  I'd recommend making this document the template for the document library.  That way, if users create documents from the document library, the properties, fields and macro is all setup.

I would caution you to set users expectations with this solution / workaround.  If they upload documents from outside SharePoint they obviously will not have the fields set up in the Word document to display the version number.  They also will not have the macro. 

I hope you find this solution helpful for your Office 2003 users.  If Office 2007 is an option, I recommend investigating the labeling and bar coding features of SharePoint 2007 first.

 

Your email is never shared.
Required fields are marked *




Allowed tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>