In SharePoint 2010 new
improvements are made for better event receiving management. Few new event
handlers are added for site, web, list, listitems etc. However, One thing that
I think badly needed was the content approval events for list items.
Nowadays content approval
has become an integral part of content management system. Requirements has come
up to do more work on content approval/reject. But unfortunately, SharePoint
list/library doesn’t have events like ContentApproved, ContentRejected,
ContentRequestedForReview so that user can tap the events to do their own work
on content approval status changes. So it seems we need to do a lot of works
manually to send notifications on content approval status changes.
Problem:
Approving Status change events Missing
One of my client wanted to
get notification on the following scenarios:
1. On
Item Add/Update: If a user
edit an item and item goes to pending status as the item needs approval, the
approving teams need to be notified that an item is waiting for their approval.
2. On
Item Approved: If the approving team approve the item,the user who
added/updated the item needs to be notified.
3. On
Item rejected: If the
approving team reject the item, the user who added/updated the item needs to be
notified with reasons why the item rejected.
But the SharePoint Object
Model doesn’t have the extensibility at this point where approving status
changes.
Why
Approval Status change event missing?
The best solution would be
if SharePoint team would provide us with out-of-box events for content
approval. In that case, two events would be suffice. The events might be :
ContentApprovingStatusChanging and ContentApprovingStatusChanged and the event
argument’s AfterProperties and BeforeProperties values could be filled with the
the old value and new value of Content Approving Status field value. However,
one may argue that ItemAdded/ItemUpdate events are similar like Content
Approval events. So when user add/edit an item and as part of the add/edit if
approval status field get updated then which events to fire?
ItemAdded/ItemUpdate or content approval events. Hmm.. maybe there’s
complexities with the new content approval events and SharePoint team has not
added the new content approval events.
Resolution:
Use ItemAdded, ItemUpdating and ItemUpdated events to keep track of approval
status changing
So consider now the problem
we’re going to talk about. We need a notification system where we need to send
notification to the approver or user (who is waiting for approval) on approval
status change. We’ll develop a list item event receiver for ItemAdded and
ItemUpdated events. When a new item will be added it’s easy to identify item
status and if the status is pending then we can send notification to all people
in the approving team. But when an Item is updated, you need to keep track of
if the Approving status field value is changed, if so then u need to send
notification. However, you can only get the old approval status field value in
ItemUpdating event, but you don’t want to send notification in ItemUpdating. So
it’s safe to send notification in ItemUdated event but in ItemUpdated event
you’ll not get the old value. You can access the old value in ItemUpdating. So
here’s the deal:
- Create
a new field say OldStatus in the list. This field will be used to keep
track of if the approval status field value has been changed.
- In
ItemUpdating event, set the current approval status (before updating) to
OldStutus field.
- In
ItemUpdated, compare the current status to OldStatus field value and if
they are not same then it’s for sure that the approval status has been
changed. So send notification.
So let’s go with the steps.
First we need an List Event Receiver that will listen three events of the list:
ItemAdded, ItemUpating and ItemUpdated. You need to send notification on two
events: ItemAdded and ItemUpdated. However, we need to hook the event
ItemUpdating to know whether the approval status is going to be changed
Create a
List Event Receiver to send notification
- Send
notification on Item Added
On Item added event, check
if the item status is pending. If so then send notification. The following code
snippet may give you the gist.
public override void
ItemAdded(SPItemEventProperties properties)
{
const string approvalStatusFieldInternalName = "_ModerationStatus";
var list =
properties.List;
var approvalStatuField =
list.Fields.GetFieldByInternalName(approvalStatusFieldInternalName);
var
approvalStatusFieldValue = properties.ListItem[approvalStatuField.Id];
var approvalStatus =
(approvalStatusFieldValue == null) ? string.Empty :
approvalStatusFieldValue.ToString();
if (approvalStatus == "Pending")
{
//SendNotification()
}
}
- Keep track of
the approval status field value (before updated) on Item Updating event
I’m assuming that you have
a field OldStatus where I’ll keep the approval status field value which is
going to be changed. I’ll explain later in this post how to automatically add
the field in list. But for now just take for granted that you have a field
OldStatus in your list of type string. The following code show how to keep the
approval status (before update) value in OldStatus field in ItemUpdating Event.
public override void ItemUpdating(SPItemEventProperties
properties)
{
const string approvalStatusFieldInternalName = "_ModerationStatus";
var list =
properties.List;
var approvalStatuField =
list.Fields.GetFieldByInternalName(approvalStatusFieldInternalName);
var approvalStatusFieldValue
= properties.ListItem[approvalStatuField.Id];
var approvalStatusValue
= (approvalStatusFieldValue == null) ? string.Empty :
approvalStatusFieldValue.ToString();
if (string.IsNullOrEmpty(approvalStatusValue)) return;
EventFiringEnabled = false;
properties.ListItem["OldStatus"] = approvalStatusValue;
properties.ListItem.SystemUpdate(false);
EventFiringEnabled = true;
}
- Check the
OldStatus field value and current approval status value to know if the
approval status changed.
Item updated is fried once
the update is done. So we’ll get the updated value of Approval Status. But
fortunately, we have kept the old value of Approval Status field in OldStatus
field during ItemUpdating event as shown in step 2.
public override void
ItemUpdated(SPItemEventProperties properties)
{
const string approvalStatusFieldInternalName = "_ModerationStatus";
var list =
properties.List;
var approvalStatusField
= list.Fields.GetFieldByInternalName(approvalStatusFieldInternalName);
var
currentStatuFieldValue = properties.ListItem[approvalStatusField.Id];
var currentStatus =
(currentStatuFieldValue == null) ? string.Empty :
currentStatuFieldValue.ToString();
var oldStatusFieldValue
= properties.ListItem["OldStatus"];
var oldStatus =
(oldStatusFieldValue == null) ? string.Empty : oldStatusFieldValue.ToString();
if (string.IsNullOrEmpty(oldStatus) && oldStatus !=
currentStatus)
{
//SendNotification();
}
}
Create a
feature receiver to attached List Event Receiver and to create field OldStatus
Finally We need an feature
receiver (not list event receiver) which will do two works: Attached our list
event receiver to a list and create a field OldStatus in the list.
- FeatureActivating
Event
In FeatureActivating you
need to check first if the event is already registered. If not registered then
register the event. Also make sure the list has OldStatus field. In the code
below, listNeedsToAttachedNotitificationReceivers is array of list names which
needs to attach the event receivers.
public override void
FeatureActivated(SPFeatureReceiverProperties properties)
{
string[] listNeedsToAttachedNotitificationReceivers = { "Product", "Order" };
var myAssemblyName = "MyProject.SharePoint";
foreach (var listName in
listNeedsToAttachedNotitificationReceivers)
{
var web =
properties.Feature.Parent as SPWeb;
var list =
web.Lists[listName];
SPEventReceiverDefinitionCollection spEventReceiverDefinitionCollection
= list.EventReceivers;
if
(!IsEventReceiverAlreadyAttached(spEventReceiverDefinitionCollection,
myAssemblyName))
{
//Attach three ItemAdded, ItemUpdating and itemUpdated event
receivers
SPEventReceiverType eventReceiverType = SPEventReceiverType.ItemAdded;
spEventReceiverDefinitionCollection.Add(eventReceiverType,
Assembly.GetExecutingAssembly().FullName,
"MyProject.SharePoint.Receivers.ListItem.ContentApprovalEventHandler");
eventReceiverType = SPEventReceiverType.ItemUpdated;
spEventReceiverDefinitionCollection.Add(eventReceiverType,
Assembly.GetExecutingAssembly().FullName,
"MyProject.SharePoint.Receivers.ListItem.ContentApprovalEventHandler");
eventReceiverType = SPEventReceiverType.ItemUpdating;
spEventReceiverDefinitionCollection.Add(eventReceiverType,
Assembly.GetExecutingAssembly().FullName,
"MyProject.SharePoint.Receivers.ListItem.ContentApprovalEventHandler");
list.Update();
}
EnusureOldStatusFieldExists(list);
}
}
private static bool
IsEventReceiverAlreadyAttached(SPEventReceiverDefinitionCollection
spEventReceiverDefinitionCollection, string myAssemblyName)
{
bool eventReceiverAttached = false;
for (int i = 0; i < spEventReceiverDefinitionCollection.Count; i++)
{
if (spEventReceiverDefinitionCollection[i].Assembly.Contains(myAssemblyName))
{
eventReceiverAttached = true;
break;
}
}
return eventReceiverAttached;
}
private static void EnusureOldStatusFieldExists(SPList
list)
{
var field =
list.Fields.TryGetFieldByStaticName("OldStatus");
if (field == null)
{
list.Fields.Add("OldStatus", SPFieldType.Text, false);
list.Update();
}
}
- Feature
Deactivating Event
In feature deactivating
event, unregister the list event receivers. If you want you can delete the
OldStatus field. However I have not deleted the field in the code below:
public override void
FeatureDeactivating(SPFeatureReceiverProperties properties)
{
string[] listNeedsToAttachedNotitificationReceivers = { "Product", "Order" };
var myAssemblyName = "MyProject.SharePoint";
foreach (var listName in
listNeedsToAttachedNotitificationReceivers)
{
var
receiversToRemove = new List<Guid>();
var web =
properties.Feature.Parent as SPWeb;
var list =
web.Lists[listName];
SPEventReceiverDefinitionCollection spEventReceiverDefinitionCollection
= list.EventReceivers;
for (int i = 0; i < spEventReceiverDefinitionCollection.Count; i++)
{
if
(spEventReceiverDefinitionCollection[i].Assembly.Contains(myAssemblyName))
{
receiversToRemove.Add(spEventReceiverDefinitionCollection[i].Id);
}
}
if (receiversToRemove.Count > 0)
{
foreach (var guid in receiversToRemove)
{
list.EventReceivers[guid].Delete();
}
list.Update();
}
}
}
How it
works all together?
It’s a bit complex huh? oK,
let’s me explain how it works.
- The
feature receiver needs to be activated first. The feature receiver
attached the event receiver to list and create a string field OldStatus in
the list.
- Next
if an item is added to the list, the listItem event gets fired and if the
item status is pending (means needs approval) then send notification.
- If
an existing item is edited and saved then ItemUpdating event is fired.
This is the event where the item is not yet saved. So I have put the
current approval status in the OldStatus field. In ItemUpated event I have
compared the OldStatus and current status field value. If the valued
doesn’t match then the approval status is changed and we need to send the notification.
No comments:
Post a Comment