Sunday, October 24, 2010

SharePoint: StaticName, InternalName, DisplayName. Which one to use in case of Multilingual supported site?

SharePoint provides three different names for referring a field: DisplayName, Intername and StaticName. You can get more details on these three properties in the MSDN post. In short, Display name is what you see in the site. Internal name is the readonly name which is created when the filed in created and can’t be modified i.e., readonly and the name should be unique. Static name doesn’t need to be unique and can be changed. If you don’t change the static name, internal name and static name are same.

 

Ok, let’s come to point. We used to access list item’s value by using display name as shown below:

var value = ListItem[DisplayName]

The problem in this approach is that if display name can be different for different language. So using display name can work in a language but may not work in different language.

 

Problem I experienced in using Display name

I was working on content approval related stuffs. And I had found the approval status is kept in a field whose display name is ‘Approval Status’. So I had used code like shown below to get the approval status field value.

ListItem[‘Approval Status’]

Unfortunately, my client was using German version and when I deployed I found my code is not working. After spending some time I had figured out the column ‘Approval Status’ is missing in the list. And I finally figured out the column name is not ‘approval status’ but it’s German equivalent.

 

So, which one to use from InternalName, StaticName, DisplayName?

So which field to use when? If you need to access custom field created by you or your team, you can use display name (considering you are using different display name for different language) . If you are using any SharePoint provided field then better to use StaticName or Intername, since you are not sure if the display name will be changed or not.

Saturday, October 23, 2010

SharePoint: Active/Deactivate Feature programmatically

Sometimes you may need to activate/deactivate features programmatically. In my case, I needed to do some synchronization from a Synchronization feature. As part of the synchronization, I needed to move data from one list to another. However the destination list had event receiver attached. The event receiver was attached using another feature (say ListItemEventReceiverAttacher). So I needed to deactivate the feature (i.e., ListItemEventReceiverAttacher) to make sure that event receiver associated to the list is not active. So let’s come to the point rather than beating around the bush.

First of all, your feature can have scope of web, site or webapplication or Farm. so you need to get the feature first. You can do so by accessing Features properties of Site or web as shown below:

1. Get the activated Features available in the WebApplication/Site/Web level

var webApplicationFeatures = webapplication.Features
var webFeatures = web.Features;
var siteFeatures = site.Features;

2. once you have got the features you can find your feature by feature id or name, if the feature is activated, as shown below:

var feature = site.Features.SingleOrDefault(sf => sf.DefinitionId == myFeatureDefinitionId);

var feature = site.Features.SingleOrDefault(sf => sf.Definition.DisplayName.Equals("Feature Title"));

If the feature is null then the feature is not activated. Only activated features will be available through site.Features/web.Features property.

3. If feature is not activated, you can activate the feature as shown below:

if(feature==null)//if feature is not activated
{
site.Features.Add(featureId);//activate feature
}

4. If the feature is activated, you can deactivate the feature as shown below:

if(feature!=null)//if feature is activated
{
site.Features.Remove(feature.DefinitionId);//deactivate feature
}

Important: One important thing to notice that once you activate/deactivate a feature against an object (SPWebApplication/SPSite/SPWeb), you need to open a new SPWebApplication/SPSite/SPWeb object to get the changes you made. The scenario shown below:

using (SPSite site=new SPSite("http://myserver"))
{
    //Activate or deactivate feature
    //the changes will not be available in this scope.
}
using (SPSite site=new SPSite("http://myserver"))
{
    //Changes made to the feature in upper using block will be effeftive here
}

Monday, October 18, 2010

SharePoint 2010: Get Current page’s Url. Request.Url vs SPUtility.GetPageUrlPath

Few days back while I was working on SharePoint application page, I had needed to get the current page’s url. I’m used to using Request.Url to get the current page url and used Request.Url in several places. But interestingly, when I tried to get the current page url from application page, it was giving me the root web’s url (like http://mysharepoint/_layouts/pages/mypage.aspx) whereas my actual page’s url was something like http://mysharepoint/subsite/_layouts/pages/mypage.aspx. The Request.Url was just skipping the SPWeb name.

Then I tried the SharePoint’s SPUtility.GetPageUrlPath and found the actual url. Though the Request.RawUrl returns the relative url which includes the subsite name but it seems safe to use SPUtility.GetPageUrlPath method to get current page’s url to make sure the page is valid and we can trust SharePoint API more in SharePoint context rather than fully relaying on asp.net built-ins.

Wednesday, October 13, 2010

SharePoint 2010: Delete webpart Title (remove untitled title)

When you add a webpart in a page, the webpart title is shown. But sometimes we want to delete/hide the webpart. To remove the webpart title, we can edit the webpart and remove the title from webpart edit toolbox. Unfortunately, if you remove the webpart title, you’ll find a default webpart title ‘Untitled’ is added.

To remove ‘untitled’ title, edit the title of the webpart and put an empty string (or or more spaces) in the tile. This will show the title but as empty string is shown, it can’t be seen.

image
Figure 1: Deleting Title
image
Figure 2: empty title (space)

 

As shown in the image above, if you put empty space in title the title can’t be seen and it seems there’s no title.

Tuesday, October 12, 2010

SharePoint Long running tasks/Operations: Timer Job or SPLongOperation

When you need to perform some operations in a webpart and the operation may take few minutes, you have two options to choose from. One might be Timer job. Clicking on a button in the webpart will start the timer job and you can check the job status periodically and show the status on the page. Timer job is perfect for long running operation but showing status in UI while the job is running might be a bit hard.

Another option might be to use SPLongOperation class of SharePoint to run long operation while to show user meaningful message. SPLongOperation is a good choice when you want to run an operation that takes few minutes only and when the user is trusted ( I mean the user is trusted to wait while the operation in progress by seeing the wait screen rather than refreshing the page). As shown in the following code snippet, you can create a SPLongOperation instance and put the long running code between SPLogOperation.Begin() and SPLongOperaiton.End() method invocation. In the end operation you need to specify the url the page will be redirected to once the operation is done. This might be the same page url or different page’s url.

var longOperation=new SPLongOperation(this.Page);
longOperation.LeadingHTML = "Please wait while the operation is running";
longOperation.TrailingHTML = "Once the operation is finished you will be redirected to result page";
longOperation.Begin();

//Do long operation here
Thread.Sleep(10000);

longOperation.End("Result.aspx");

As as result of the above code you can see the following screen during the operation is progress.

image

 

Even when you are not sure how much time a page takes to load you can use the SPLongOperation interface which may provide better user experience. So if your operation needs several minutes or more your choice might be Timer job. But the operation takes few minutes and the operation needs user interaction then you can opt to use SPLongOperation.

Monday, October 11, 2010

SharePoint 2010: Send Notification on Item Approved/Rejected (when Content Approval Status is changed)

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

  1. 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()
        }
    }
  2. 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;
    }
  3. 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.

Saturday, October 2, 2010

SharePoint Designer 2010: Crash frequently, not a product for public release

I can remember the old days of using SharePoint Designer 2007. SharePoint Designer 2007 used to crash so frequently that before pressing Ctrl + S  (for saving content) in SharePoint designer I prayed to God not to crash the designer. I can still fell the pain and danger I used to face in those old days. I couldn’t understand how a public released product can crash so frequently! Even a beta product can’t be that much unstable nowadays. However SharePoint designer 2007 was made a bit stable later with service pack releases.

SharePoint 2010 is out there for few months and people are rushing for it (as it seems). My expectation was not to face the same buggy designer this time. But I’m disappointed and frustrated as like others who have experience the same problems. I’ve been using SharePoint Designer for few weeks and I’ve found it’s crashing  frequently (but not that much as was in first release of of Designer 2007). And the most interesting is that there’s no error reporting option in SharePoint Designer. It just crashes and when I open the designer again it opens as it nothing unusual happened before.

Since there’s no alternative to SharePoint designer, so we are bound to use the designer risking the chance of losing the content and valuable time. We don’t expect this to be happen again and again.There’s lot of reports on SP designer crash on web. We SharePoint community expect reasonable steps from SharePoint team to resolve the issue. Now either Microsoft can make the project open source or they can fix it by themselves. When it continue crashing again and again It seems like the product was developed by some fresh university students for their final year projects.

 

Working with SharePoint Designer is too much frustrating, disgusting experience. SharePoint designer should be part of SharePoint but it seems SharePoint Team doesn’t think like that way. They always care less about SharePoint designer than the original product and getting less care from the team SharePoint designer becomes a playing tools that can be used to demonstrate how a product can be crashed shamelessly. SharePoint Designer is less cared by-product from SharePoint team. But SharePoint Team should know, the designer is the product that can be used to edit content in SharePoint and we are using SharePoint Designer to add/edit enterprise content not some testing stuffs. So I loose my contents or my contents get distorted then they are responsible for this.