Monthly Archives: February 2011

SharePoint Event Handling – Property Promotion in Office

One of the big selling points for SharePoint 2007 is the ability to have MS Word automatically detect the fields that are set up in Sharepoint and make those values available within the document. This feature is called property promotion when SharePoint pulls the values from the Word doc and fills in the list fields with those values. The reverse, when SharePoint pushes the values defined in the list into the word document, is called property demotion. This works quite well, until you have an event handler that automatically sets the value of a field in SharePoint.  Some problems you might encounter are…

  1. Property promotion sometimes occurs after you automatically set the field value, overwriting your value with a blank or whatever value was set in the document.
  2. Property demotion doesn’t include your auto-setting value.

I have only found one solution that works in all cases:

//Global variables to store values for the worker thread
private Guid ListID;
private Guid ItemID;
private Guid SiteID;
private String FieldValue;

public override void ItemUpdated(SPItemEventProperties properties){
    DisableEventFiring();

    try{
        //Store the values our thread is going to need to get a
        //handle on this list item.
        FieldValue = GetColumnValueForItem(properties.ListItem);
        ListID = properties.ListId;
        ItemID = properties.ListItem.UniqueId;
        SiteID = properties.SiteId;

        //Only start the new thread if it has NOT been set.
        if (uFieldValue != (string)properties.ListItem[COLUMN_NAME]){
             //Create the worker process
             Thread workerThread = new Thread(SetCustomValue);
             workerThread.Start();
        }
    }catch(){
        //Log error message.
    }

    EnableEventFiring();
}

public override void ItemAdded(SPItemEventProperties properties){
    DisableEventFiring();

    try(){
        //Store the values our thread is going to need to get a
        //handle on this list item.
        FieldValue = GetColumnValueForItem(properties.ListItem);
        ListID = properties.ListId;
        ItemID = properties.ListItem.UniqueId;
        SiteID = properties.SiteId;

        //Create the worker process
        Thread workerThread = new Thread(SetCustomValue);
        workerThread.Start();
    }catch(){
        //Log information about the error.
    }

    EnableEventFiring();
}

private void SetCustomValue(){
    try{
        //Wait for 5 seconds to allow all of the SharePoint
        //events to finish working.
        Thread.Sleep(5000);
        using (SPSite site = new SPSite(SiteID)){
            using (SPWeb web = site.OpenWeb()){
                //Get the list item using the global variables
                SPList list = web.Lists[ListID];
                SPListItem item = list.GetItemByUniqueId(ItemID);

                //Set the custom field to the proper value
                item[COLUMN_NAME] = uFieldValue;
                //Perform a SystemUpdate on the item to save
                //the changes to the field.
                item.SystemUpdate(false);
            }
        }
    }catch (Exception ee){
        //Log information about the error.
    }
}

The logic is fairly simple. Just spin up a thread that waits for SharePoint to be finished modifying the file and list item, then sets the auto field value. When this tactic is used, the auto value gets set in SharePoint and in the document for all reasonable test cases.  (i.e. when you copy using Word SaveAs to add a new document, when you use the Windows Explorer View, or when you use the standard SharePoint Web UI to Add or Edit the document.)  

One thing to keep in mind is that you can’t determine how many times the code will be run.  ItemAdded and Item Updating may each be called once, multiple times, or not at all, depending on what method was used to update the document.  So when the value of the custom field is calculated, you must use a formula that gives you the same result every time.

For more information, see the other posts in this series:

SharePoint 2010 – Activating the Document ID Feature

SharePoint 2010 has now included an excellent out-of-the-box feature that allows you to automatically assign a unique document ID to all of your documents that are uploaded to your SharePoint site. This is a site-collection scoped feature, so the document IDs are guaranteed to be unique across the site collection that the feature has been activated for. In order to ensure that the document ID is unique across your entire farm, they have included a configuration setting that sets a specific prefix for all of the document IDs assigned in your site collection. By setting a different prefix for each site collection that uses this feature, you can ensure that each document ID is truly unique across your farm.

Activating the Feature

Since the document ID feature is scoped for an entire site collection you need to be the site collection administrator. If you go to any site in the site collection and go to the Site Settings, you should see a Site Collection Administration section. Under this section click on Site Collection Features. In there, you will see the Document ID Service feature.

Document ID Feature

This is the service that you need to activate to enable this feature. Once this feature is activated you can edit the view of any document library (through the Library Settings) to include the new Document ID column. When you first activate the feature, you will notice that there are no document IDs assigned to the documents that were already in your document libraries. This is because the task of assigning document IDs to existing documents is performed by a timer job on the server. I will explain how this timer job works later in this post. First, I will explain how to configure the document ID prefix settings.

Setting the Document ID Prefix

Once you’ve activated the Document ID Service, you will see a new entry under the Site Collection Administration section called Document ID Settings. This is where you can configure your document ID prefix.

Document ID Settings

By default, SharePoint will assign a non-sensical hash as the Document ID prefix, so for usability purposes I would highly suggest changing this value before applying document IDs to all of your existing documents. The settings contained in this page are pretty self-explanatory… the “Assign Document IDs” checkbox is used to turn the document ID assignment ON and OFF, the text box just below that is used to set the prefix for all of your document IDs, and the checkbox just below that is used when you wish to propagate the changes you made to the prefix to all existing documents in your site collection.

One important thing to note (as is indicated by the red text) is that any changes made to these settings will not be applied to existing documents until the timer job on the server is run. This brings me to the next step in activating the Document ID feature: Running the timer jobs.

Running the Timer Jobs

Any changes you make to the prefix, in the settings above, will be applied immediately to any new document that is uploaded to your document library. However, changing the document ID of existing documents is done by a timer job on the server. In fact, there are two timer jobs that are set to automatically apply your configuration settings for the Document ID feature: the Document ID assignment job and the Document ID enable/disable job. You can find these timer jobs on the Review Job Definitions settings page in Central Administration, under Monitoring –> Timer Jobs. Once here, look for the jobs that are registered to your web application.

Review Timer Jobs

The “Document ID assignment job” is the job that takes your configured prefix, and assigns a document ID to all of your existing documents. By default, this job is run daily between 10:00PM and 10:30PM. If you checked the checkbox in the Document ID settings named “Reset all Document IDs in this Site Collection to begin with these characters”, then this job will go to all of your existing documents and change their prefix to the new one that you have set. The “Document ID enable/disable job” is the job that essentially “saves” any newly configured settings for the Document ID feature across all sites. By default, this job is run daily between 9:30PM and 9:45PM. Sometimes, it may be necessary to see your changes immediately. In order to do this, you simply need to click the link in the Job Definitions settings page and then click the Run Now button.

Timer Job Settings

* * * * *
Important: If you have made changes to the Document ID prefix, you may need to run the “Document ID enable/disable job” first before you run the “Document ID assignment job” in order to see your changes.
* * * * *

Once you have run these two jobs, you should now see the Document ID column filled out with your newly configured Document ID:

Document ID

Accessing Documents

Once all of your documents have IDs assigned to them, you can then reap the benefits of the Document ID feature. There are two ways that you can access documents using their Document ID: (1) Through a well-formed URL, or (2) through the “Find by Document ID” web part.

To access Documents through a well-formed URL, you simply need to type a URL in to the address box of your browser that is of the form: http://<Server_Name>/_layouts/DocIdRedir.aspx?ID=<Document_ID> . Or alternatively, you can copy a document’s URL directly from the Document Library by right-clicking on the link under the Document ID column and clicking “Copy Shortcut” (in IE).

In order to use the “Find by Document ID” web part, you simply need to go to any editable page in your site, and add the web part to the page. The web part can be added through the Insert –> Web Part ribbon menu, under the Search category. Once the web part is on a page, you can simply enter a Document ID in to the text box and click on the arrow button. This will find the document you are looking for and automatically download it for you.

Find by Document ID

By using the Document ID to access your documents, you are ensuring that your users can always access frequently used documents through a single URL, even when a document is moved to another location. This can save a lot of time and questions whenever your document libraries are undergoing some refactoring. If you are using the Foundation edition of SharePoint 2010, unfortunately you do not have access to this feature… however, if you are using the Standard or Enterprise editions of SharePoint 2010, then you can (and should) take full advantage of this new feature.

SharePoint Events – Setting a Column Value in the ItemUpdating Event

I was recently tasked to automatically set a document ID value in a SharePoint 2007 document library. (In SharePoint 2010, an excellent document ID feature is included out of the box)  I figured it would be easy since SharePoint supports calculated columns.  However, I needed to be able to insert the document ID value into a Word document as a field.  Sharepoint does not include calculated fields as part of the document metadata so using calculated columns was not a good solution for me.  An event handler seemed like the best way to go.

There are a lot of actions a user could take that should trigger my code.  See my previous post SharePoint Event Handling – When do events fire?  I needed my code to be triggered no matter which action the user took.  The best approach I found was to use the ItemUpdating event and then force ItemUpdating to be called by triggering an update in the ItemAdded event.

My first attempt at the code looked like this:

public override void ItemAdded(SPItemEventProperties properties)
{
    base.ItemAdded(properties);
    //Make sure the ItemUpdating method gets called even when using SaveAs from MSWord.
    properties.ListItem.SystemUpdate(false);
}

public override void ItemUpdating(SPItemEventProperties properties)
{
	try
	{
		string newFieldValue = GetNewFieldValue();
		object oldFieldValue = properties.AfterProperties[COLUMN_NAME];
		if (oldFieldValue == null || oldFieldValue.ToString() != newFieldValue)
		{
			properties.AfterProperties[COLUMN_NAME] = newFieldValue;
		}
	}
	catch
	{
	    //Log the error, or take another appropriate action.
	}
	finally
	{
		base.ItemUpdating(properties);
	}
}

I found that there were circumstances when the column value would not get set or would be cleared and I would get the default value for the column instead.  After hours of debugging and testing, I discovered that it was only clearing my column when ItemUpdating was triggered and the value of my column had already been set properly and hadn’t changed.  So I made the obvious change and removed the piece of my code that skips explicitly setting the column value if it has already been set.

public override void ItemUpdating(SPItemEventProperties properties)
{
	try
	{
		string newFieldValue = GetNewFieldValue();
		object oldFieldValue = properties.AfterProperties[COLUMN_NAME];
		//if (oldFieldValue == null || oldFieldValue.ToString() != newFieldValue)
		//{
			properties.AfterProperties[COLUMN_NAME] = newFieldValue;
		//}
	}
	catch
	{
	    //Log the error, or take another appropriate action.
	}
	finally
	{
		base.ItemUpdating(properties);
	}
}

This fixed the problem … Now I needed to figure out why.  I stepped through the code with the VS debugger and found that the properties.AfterProperties[COLUMN_NAME] had the correct value for the entire method, but some time after the ItemUpdating method finished  the value was cleared, unless I had explicitly set the value during that pass.  I did some research, but wasn’t able to find a reasonable explanation for this behaviour.  I will add to this post once I find one.  In the meantime, I hope this quick fix will help some of you avoid the same type of problem.

Building a PerformancePoint Dashboard

PerformancePoint services provides you with all of the tools to create a fully-functioning business intelligence dashboard. Right out of the box you have everything you need from analytic reports to KPI scorecards. In order to build these PerformancePoint objects, Microsoft has provided you with the PerformancePoint Dashboard Designer. This dashboard designer gives you a visual interface to build all of your PerformancePoint objects, as well as an interface to put these objects in to a fully-functioning dashboard. This blog entry will cover the latter of these two tasks: Building a PerformancePoint dashboard. The interface provided by Dashboard Designer is sometimes a little bit cumbersome to use, but if used right, it is a powerful tool for creating dashboards.

There are 4 basic tasks to creating a fully-functioning PerformancePoint dashboard: (1) Laying out the wireframe for the dashboard, (2) adding the PerformancePoint objects to the dashboard, (3) creating connections between the PerformancePoint objects, and (4) deploying the dashboard to SharePoint. In this post I will walk through the steps of accomplishing these tasks, and I will also provide some tricks and design tips to building a user-friendly dashboard.

Laying Out the Zones

The first thing that Dashboard Designer asks you to do once you create a new dashboard is to pick a page template for your dashboard. These page templates contain web part zones that are made specifically for PerformancePoint objects. The web part zones determine the size and location of the PerformancePoint objects that you put in them, so it is important that you have an idea about what your dashboard will look like before you actually create the dashboard. It is important to remember to keep the number of parts on your dashboard to a minimum. Each time you add something to the dashboard certain parts resize in an attempt to fit the entire view in the user’s browser without forcing him or her to scroll around. Once the objects get too small the user will start to lose legends and subtitles from the reports on the dashboard, which decreases the overall usability. So it is important to design a dashboard that will give plenty of space to each of the objects on it. If you find that you don’t have enough space for everything, consider creating a separate page for certain parts of the dashboard.

Once you have a design in mind, it is time to create the dashboard. On the page template selection dialog, choose the layout that is the closest to your design. The template will be completely customizable after, so don’t worry about it being exactly like your design. Once you’ve chosen a template and clicked “Ok”, you will be brought to the main interface for designing your dashboard. On this interface you will see a Dashboard Content panel. This will be the main panel used for laying out your dashboard. In order to customize the layout we will be using the context menu that comes up when you right-click on one of the zones in your dashboard. This context menu is shown in the image below.

Context Menu

All of the options in the context menu are pretty self-explanatory; however the action that happens after you click each option may take some getting used to. The first options that you will probably make use of are the “Add ***” options. These options work pretty much the way you would expect them too: clicking the “Add Left” option will add a zone to the left of the zone you’ve selected, clicking “Add Below” will add a zone below the one you’ve selected, and so on. There are a few variations on the size of the new zone depending on which zone you click on, but I will explain that a little later. The next option that you will use a lot is the “Split Zone” option. Even though this option is pretty self-explanatory, you may begin to ask yourself: What determines the orientation of the split? At first it seems like Dashboard Designer just takes a guess at which way you would like to split the zone, but this is not true. The magic lies in the “Orientation” setting in the Zone Settings dialog.

Orientation

If you split a zone that is set to “Horizontal”, it will split from top to bottom. Otherwise, if you split a zone that is set to “Vertical” or “Stacked”, it will split the zone from left to right. Once you start splitting zones, the “Add ***” options will start behaving differently. For example, if you split a vertical zone that is currently one column on your dashboard (say, the left column), and then add a zone in between the two cells, the new zone will be added with the same width as the rest of the column. As is shown below:

Add Zone Center

However, if you add a zone above the top cell of a column, or below the bottom cell of a column, then you will get a new zone that spans the whole page. As is shown below:

Add Zone Above

By using only the Add and Split options, you should be able to tailor your dashboard to the desired layout. If you need to tweak the size of your zones you can use the settings under the “Size” tab of the Zone Settings dialog. Something to keep in mind though, is that when you specify a height or width in percentage, Dashboard Designer will always try to keep the total width and height as 100%. This means that increasing the size on one zone will most likely change the size of other zones.

Add PerformancePoint Objects

Once you have the wireframe of your dashboard the next step is dead simple. You simply need to click and drag all of the objects that you want on your dashboard in to their respective zones. This is literally all there is to it. You will find all of the available objects from your workspace in the Details panel on the right:

Details Panel

Once you drag and drop an object in to a zone you will see a list of properties displayed in the zone. This list of properties is what you use to create connections between the PerformancePoint objects. Creating these connections will be discussed in the next section. Once you’ve placed all of your objects in to their zones, you will have something that looks like this:

Objects Placed

Creating Connections

Creating connections between PerformancePoint objects is what makes it possible to have asynchronous interaction between the objects on your dashboard. Creating a connection allows one object to send information to another object in order to apply a filter to it. It is important to note that not all objects can send and receive information from other objects. In the above image you may have noticed that two of the PerformancePoint objects don’t have a list of properties. This is because analytic reports can only consume connections; they cannot provide connections. The chart below shows what kind of connections can be made with each PerformancePoint object.

Connection Chart

In order to create a connection from one object to another, you can use one of two methods: (1) you can click on the arrow at the top-right of an object and click “Create Connection”, or (2) you can drag one of the available properties from one object and drop it on another. For the sake of simplicity, I’ve always found it easier to use the second method, because when you drag a property from one object to another it makes it clear that you are sending information from the first object to the second object. In order to be complete however, I will cover both methods.

When you use the first method, clicking on the “Create Connections” menu item will bring up the Connection dialog. When the dialog comes up, you will automatically be within the context of the Items tab. This is the part of the dialog where you choose your source object, or your destination object depending on what kind of object you clicked on. If you clicked on a filter, you will be asked for a destination object, chosen from the “Send values to” drop-down. If you clicked on an analytic report or a scorecard, you will be asked for a source object, chosen from the “Get values from” drop-down. Once you’ve done this you can move on to the Values tab in the connection dialog. In this part of the dialog you will be able to choose the two end-points for your connection. In the “Connect to” drop-down you can choose which part of the destination object you want connect to, and in the “Source value” drop-down you can choose which value you would like to send from the source object.

When you use the second method, dragging a connection property from one object and dropping it on another will bring up the same dialog as before. However, this time you will automatically be in the context of the Values tab. This is because, by dragging and dropping, you have already defined your source object and your destination object. So the values in the Items tab will be filled out for you. You can then configure the settings under the Values tab in the same way that you did when using the first method.

Figuring out the proper end-points for the connections between objects is a more complex topic that is worthy of its own post, so I won’t get into any details here. For a good overview of what can be passed through connections between various PerformancePoint objects, refer to this MSDN blog. If you are looking for how to connect a time intelligence filter to other PerformancePoint objects, refer to my previous blog entry. Once you have created all of your connections, your dashboard content panel should look something like this:

Complete Connections

As you can see, each of the objects on the dashboard is showing a list of connections. Each connection listed shows the name of the object that that object is getting a value from. In other words, it is listing all of the source objects for the connections. Once this step is done, you are ready to deploy your dashboard to SharePoint.

Deploying to SharePoint

The task of deploying a dashboard to SharePoint doesn’t really take too much explanation. The only thing you have to do is right-click the dashboard object in the Workspace Browser panel on the left, and click “Deploy to SharePoint”. You can then choose a list to add the dashboard to, choose a master page to apply to the dashboard, and finally, decide whether or not you would like a page list displayed at the top of your dashboard for navigation. In most cases, all of the default selections will be sufficient. However, if you have added multiple pages to your dashboard, then I would highly recommend checking the “Include page list for navigation” checkbox. This will give your users a quick and easy way to switch back and forth between the pages that you have created.

One thing to note about dashboard deployment is that once you’ve deployed your dashboard to SharePoint, you can continue to edit the underlying objects without having to re-deploy the dashboard to SharePoint. Any changes you make to the underlying objects will automatically be reflected on the dashboard the next time you visit the page. The only time you will need to re-deploy is when you add objects, remove objects, change any of the connections, or tweak the layout on your dashboard. After you’ve deployed your dashboard to SharePoint you will automatically be taken to the first page of your dashboard. Success!