11.3. Managing Document Creation

In any document management solution, it's important that each new document adhere to organizational standards. This is especially true when metadata is being used to drive business processes. Even with a well-defined set of metadata fields and established guidelines for filling them out, busy knowledge workers often forget to provide this information because they are too focused on the document content. If the metadata is incomplete or inconsistent, then automated business processes that depend on it will fail. This leads to inefficiency and additional costs associated with finding and correcting the missing data.

Windows SharePoint Services 3.0 provides an enhanced callback mechanism that enables solution developers to control the document creation process and take prescriptive action based on the current state of the document. By implementing event receivers, you can write custom code that is called during document creation.

Document creation occurs in three stages:

  1. The document template is opened in the appropriate editor program. The user edits the content and metadata and then saves the document into the library,

  2. SharePoint retrieves the metadata and places it in a property bag, which it passes to the ItemAdding event receivers that have been registered for the library.

  3. Unless one of the ItemAdding receivers cancels the document as noted below, SharePoint uses the properties to construct a new document list item, which is then added to the library. Next it calls the ItemAdded event receivers that have been registered for the library.

The ItemAdding event is called synchronously, suspending the document creation process until the event receiver returns control to SharePoint. By contrast, the ItemAdded event is called asynchronously after the document has been added to the library. Using this architecture, you can implement an ItemAdding event receiver to cancel the Add operation if the document metadata does not meet your requirements.

The key issues to address are:

  1. Have all required metadata fields been supplied with the document?

  2. Are the values of the required metadata fields consistent with each other?

  3. Is the metadata consistent with our document management policy?

11.3.1. Checking for Required Metadata

First, ensure that all of the required metadata fields have been supplied. For the project proposal, you want to ensure that the user has selected a project type, because the other business rules depend on this value. If it is not supplied, then you reject the document and notify the user.

The usual way to ensure that required metadata fields have been filled out is to set the Required attribute to "TRUE" when defining the content type. This works well for simple document types but not in more complex scenarios, where the set of required fields may change depending on an externally defined policy. It may still be necessary to use the Required attribute for documents that are created on the client. For instance, Microsoft Word will throw a generic exception instead of displaying a user-friendly error message when the ItemAdding event is canceled. Using the Required attribute forces the user to enter the item and also enables Word to display a visual cue indicating required fields, as shown in Figure 11-7.

When the ItemAdding event receiver is called, SharePoint passes a SPItemEventProperties object as a parameter. This object holds the property values for all of the metadata supplied by the user when creating the document. Using this object is a bit tricky because the same type of object is also passed to the other event receiver methods. However, different fields are supplied at different stages of the document lifecycle. The following table shows the relationship between selected fields of the SPItemEventProperties object and the Add/Update pairs of event receiver methods.

Figure 11.7. Figure 11-7

MethodDescriptionFieldAvailable?Comments
ItemAddingCalled before an item is added to the list.   
  ListIdYesIdentifies the list that will contain the new item.
  ListItemNoThe list item has not yet been created.
  BeforePropertiesNoItem properties are only available after the item is created.
  AfterPropertiesYesItem properties that will be used to populate the new item.
ItemAddedCalled after an item is added to the list.   
  ListIdYesIdentifies the containing list.
  ListItemYesIdentifies the new list item.
  BeforePropertiesNoNo item properties existed prior to item creation.
  AfterPropertiesYesItem properties that were used to populate the new item.
ItemUpdatingCalled before an item is updated.   
  ListIdYesIdentifies the containing list.
  ListItemYesIdentifies the list item.
  BeforePropertiesYesHolds a hashtable of item properties before the update.
  AfterPropertiesYesHolds a hashtable of item properties that will be applied when the update is processed.
ItemUpdatedCalled after an item is updated.   
  ListIdYesIdentifies the containing list.
  ListItemYesIdentifies the list item.
  BeforePropertiesYesHolds a hashtable of item properties before the update.
  AfterPropertiesYesHolds a hashtable of item properties after the update.

To make things easier and to simplify the code, you can declare a static class that takes these dependencies into account. Listing 11-9 shows how to implement the ItemAdding event receiver to check for the required metadata using a static wrapper class to process the raw SPItemEventProperties object.

Example 11.9. ItemAdding event receiver
/// <summary>
/// A helper class for testing various conditions on SharePoint list items.
/// </summary>
public class ItemCondition
{
    public static bool HasProposalType(SPItemEventProperties properties)
    {
        object value = properties.AfterProperties["Proposal Type"];
        return value != null && value.ToString() != string.Empty;
    }
}

/// <summary>
/// Synchronous before event that occurs when a new item is added
/// to its containing object.
/// </summary>
/// <param name="properties">
/// A Microsoft.SharePoint.SPItemEventProperties object
/// that represents properties of the event handler.
/// </param>
[SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
public override void ItemAdding(SPItemEventProperties properties)
{
    try {
        ValidateItemProperties(properties);
    } catch (Exception x) {
        properties.ErrorMessage = x.Message;
        properties.Cancel = true;
    }
}

/// <summary>
/// Helper method to determine whether an item meets validation requirements.
/// </summary>
/// <param name="properties"></param>
private void ValidateItemProperties(SPItemEventProperties properties)
{
    if (!ItemCondition.HasProposalType(properties))
        throw new SPItemValidationException(properties, "You must select a proposal type.");
}

11.3.2. Checking Metadata Consistency

In the case of the project proposal, you check for different field values depending on the type of project that was selected. For instance, time-and-material projects require the user to provide an initial estimate of the hours needed to complete the project, while fixed-bid projects require an estimate of the total bid amount. You will use these values later when implementing other business rules, so it is vital that the appropriate values are entered correctly.

When an item is added to a list, SharePoint calls the ItemAdding event receiver, but it does not call the ItemUpdating event receiver. Conversely, when an item is edited, SharePoint calls only the ItemUpdating event receiver. Therefore, any consistency checking code should be called from both the ItemAdding and the ItemUpdating event receivers.

11.3.3. Postprocessing of Metadata

After a document has been added to the library, it is often useful to perform postcreation tasks such as making entries into a tracking log or notifying users that a new document has been created. In the proposal-tracking system, you want to keep a running record of the progress each proposal makes throughout its lifecycle. You can do this easily by setting up a SharePoint list to record the date and time a given event occurs.

You begin by defining a second content type called a ProposalTrackingRecord and enable it for a custom list called "Proposal History." The ProposalTrackingRecord is derived from the built-in Item content type, and consists of only the title field. Use the title to display the text of the tracking event.

<ContentType ID="0x0100a0cada319e714c1fab64c519c065d421"
        Name="Proposal Tracking Record"
        Group="ProSharePoint2007"
        Description="A list item for tracking proposal-specific events."
        Version="0">
        <FieldRefs/>
</ContentType>

You can use a ProposalTrackingEvent enumeration to model the different kinds of events you wish to track. In addition to the standard document lifecycle events, you can also capture important proposal-specific milestones such as when a proposal is approved for submission to the client, or when a proposal is accepted by the client.

enum ProposalTrackingEvent
    {
        Created,
        Modified,
        MajorRevision,
        MinorRevision,
        CheckedOut,
        CheckedIn,
        ApprovedForSubmission,
        AcceptedByClient,
        Published,
        Deleted,
        Archived,
    }

Finally, in the same way that you created the Proposals document library, you now create the Proposal History custom list and an associated ListView in the right Web Part zone.

// Create a custom list to hold the proposal history and
// add a default list view to the right Web Part zone.

AddListViewWebPart(web, new ProposalHistoryList(web),
            "Right", PartChromeType.TitleOnly);

To capture postcreation events, implement an ItemAdded event receiver for the ProjectProposal content type. This event is fired after the metadata has been validated and the new document has been added to the library. When the event receiver is called, you obtain a reference to the Proposal History list and make the appropriate entries using the AddRecord helper method:

[SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
public override void ItemAdded(SPItemEventProperties properties)
{
    // Add an entry to the proposal history list.
    SPWeb web = properties.ListItem.ParentList.ParentWeb;
    ProposalHistoryList history = ProposalHistoryList.FromWeb(web);
    history.AddRecord(ProposalTrackingEvent.Created,
        properties.AfterProperties);
}

Now, whenever a project proposal is added to the proposals library, a new tracking record is created based on the type of event (in this case, ProposalTrackingEvent.Created) and the properties stored in the new item. Depending on the event type, you can use these properties to capture a more detailed picture of the context surrounding the event. This analysis could be recorded separately or could cause a custom workflow to be initiated. Figure 11-8 shows the project history list with a tracking record entry.

Figure 11.8. Figure 11-8

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

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