Pages

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.

Thursday, September 23, 2010

SharePoint 2010: Add custom menuitem to Welcome/My Settings (top-right) menu

To add a custom menu or hide an existing menu in SharePoint you need associate a element.xml file with a feature. My pervious blog post descries how to add an element.xml file and associate it with a feature. The element.xml file has the following structure:

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction
  Id="myCustomAction"
  GroupId="GROUP_ID"
  Location="MENU_LOCATION"
  Sequence="1000"
  Title="Open Your Page"
  Description="Open Your custom page">
    <UrlAction Url="~site/SitePages/mypage.aspx"/>
  </CustomAction>
</Elements>

As shown in the code snippet above, Id is the name of the action. GroupId is the sharepoint defined GroupId and Location is also sharePoint defined location. Sequence will define the sequence number of the menu item. Url Action is the page where user will be navigated when user will click the menu.

To add custom menu item on the welcome or my settings menu, you need to add an element.xml file (shown below) in the project and need to associate the element.xml file with a feature. In my previous blog post I have described how to add an Element.xml file and attach the xml file with a feature.

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction
  Id="myCustomAction"
  GroupId="PersonalActions"
  Location="Microsoft.SharePoint.StandardMenu"
  Sequence="1000"
  Title="Open Your Page"
  Description="Open Your custom page">
    <UrlAction Url="~site/SitePages/MyPage.aspx"/>
  </CustomAction>
</Elements>

After using the above Element.xml file you can get the following menu item added on the Welcome/My settings menu.

image

You can get a set of groupid and location values for SharePoint from this MSDN link.

Wednesday, September 22, 2010

SharePoint 2010 Development with Visual Studio 2010: Add Resources (like Elements.xml) to Feature

We all SharePoint developers are familiar with Feature.xml and Elements.xml files. We usually defines feature’s resources in Element files. However, in Visual Studio 2010 to add Elements file to Feature we need to do few extra works. You can’t just add the Elements files directly in a feature folder in Visual Studio. You may still need to add the Feature element manifest file, for example in cases where you want to add a custom menu or hide existing menu for site actions. The following steps show how to add Elements.xml file in a SharePoint Feature from Visual Studio 2010.

Step 1: Create a new Feature

Create a feature for which you want to add the Element manifest file. If you have the feature already, then you can the feature to be used for attaching Element.xml file.

Step 2: Add Empty Element File

You need to add an empty element file in the project. To add Element.xml file, create a Empty Elements file in the project from ‘Add new Item’ window as shown below:

image

For manageability, you can keep all Element files in a single folder, say “FeatureElements” folder.

 

Step 3: Add ElementFile to elements.xml

Now, let’s say you want to add a configuration xml file (say MyConfig.xml) to element file. To do so right click on the newly created empty element and add a new item or existing item. Then open the properties of the new/existing item (so, MyConfig.xml) and set the ‘Deployment Type’ to ElementFile.

image

 

Step 4: Attach Elements file to Feature

Now you need to attach the Element file to a feature. To do so open the feature designer by double clicking on the feature name from Visual Studio’s solution explorer. Then make sure your Element file is on the right side (Items in the feature) of the designer. The following figure shows the Element file MyFeatureElement is attached with the feature whereas YourFeatureElemet is not attached.

image

Tuesday, September 21, 2010

SharePoint 2010: Change the default Feature installation location from Visual Studio

In SharePoint 2007, we used to put the custom features in folder “12\TEMPLATE\FEATURES”. In SharePoint 2010, feature management is mostly done by Visual Studio 2010 UI. We mostly don’t care where the features are installed. However, with surprise I noticed that the feature is installed in the Features folder but not directly under the Features folder. Visual Studio creates a new folder under “14\TEMPLATE\FEATURES” folder and copy the features under that new folder.The name is in the format “VisaulStudioProjectName_FeatureName”. The name comes from feature’s properties and you can change the format. The following image shows the feature properties defining the naming format.

image

Figure 1: Feature deployment path in properties window

Now if you want to put the features just directly under the ‘14\Template\Feature’ folder, then just the set the ‘Deployment Path’ property value to '

$SharePoint.Feature.FileNameWithoutExtension$

 

Maybe sometimes you want to install the feature just under Features folder and in that case changing the deployment path will be helpful.

Move SharePoint Site/Web From One Development Server to Another

Sometimes, we need to move the site from one server to another, usually in development environment. In that case its required few steps to make sure you have moved your site in new server. I have successfully move my sites and I’m pointing here the exact steps that worked for me.

  • Backup the site or export the web

First of all you need to take backup of the site (if you want to move site collection). If you want to move a single site then export the web contents. For site backup use the following command:

Backup-SPSite -Identity http://MySiteColllection.com -Path "C:\Backup\BackupFile.bak" -Confirm:$false

For exporting web use the following command.

Export-SPWeb –Identity http://MySiteCollection.com/myweb –Path “C:\Backup\BackupFile.cmp” –Confirm:$False

For more on these commands follow MSDN link.

 

  • Create Dummy site or web on the destination server to overwrite

Before restoring or importing site/web you need to create a dummy site/web on the destination server. In case of site restore, create a new site collection. In case of web import, create a site with the same name and template used in source server’s web.

 

  • Deploy code on the server

If possible, then deploy the sharepoint solution (generated from your developer code in Visual Studio) on the destination server. In some case the restore/import operation looks for features/resources on the site/web that is referenced in the backup/export file. So deploying the code on the server make sure the code (that will be finally used in the restored site/web) is on the destination server. If your code has web level feature/resources then use the root web as the default one to deploy code.

 

  • Restore or import the site/web

Finally you are ready to restore the site. Run the following command to restore a site collection.

Restore-SPSite -Identity http://MyNewSiteCollection.com -Path "C:\Backup\BackupFile.bak" -Confirm:$false -Force

Run the following import command to import site.

Import-SPWeb –Identity http://MyNewSieCollection.com/MyNewWeb –Path “C:\Backup\BackupFile.cmp” –Overwrite –Confirm:$False

For more on these commands follow MSDN link.

 

  • Check user Permissions

If you restored a site collection then you need to change site collection administrator as the restored site is still pointing to the old site collection administrators (and maybe the administrator is invalid in the new server). From SharePoint central administration site change the site collection administrator to proper one.

 

Few points to notice

When you move the site/web few points need to remember:

  • If you restore just a web then your import operation may fail saying a feature is not installed on the web/site. This may happen in case where the source site /web’s backup file is referring a resource (say feature) that is not available on the destination server. In this case, make sure your code is deployed properly on the destination server. If you get the error for a particular web, then create a dummy web with the same name as on the source server, on the destination server and then deploy the code on the web and finally try to restore the web.
  • In case of web import, make sure you have created the new web on the destination server with the same template. You can’t overwrite a web on destination server with a source one that is using different template.

Sunday, September 19, 2010

SharePoint Error: The exported site is based on the template STS#1 but the destination site is based on the template STS#0

Today I was trying to export one of my site from one Virtual machine to another. First I took a export of the web from my source server and then I tried to import the content in the destination server. And then the import failed with the error “The exported site is based on the template STS#1 but the destination site is based on the template STS#0”. I couldn’t remember the template with which I created the source site but I took for granted from the error message that the source and destination web are not created from the same template. After googling I finally found import will only work if source and destination web are created from the same template. In my case the source site was created from Blank Site template (STS#1) but the destination site was created from Team site template (STS#0).

So I deleted the destination site and recreated with the source template and the import was successful. For full lists of web site templates you can follow Aravindhan’s  posts.

Sunday, September 12, 2010

SharePoint 2010 Visual WebPart in Visual Studio: Best practices

When we develop visual WebPart in Visual Studio, we may follow some best practices for better management and organization of WebParts. Here are few good practices to follow:

  • 1. Group your web parts: Set the group property in the Elements.xml file of WebPart to make sure the webparts comes under a specific group. This will make easier to find the webpart from sharepoint site. The following image shows how to modify group property:

    image

    Figure 1: WebPart’s group property in Visual Studio

    Setting up the group property will show the webparts under ‘Test Group’ as shown below:

    image

    Figure 2: Webparts under Group.

  • Organize webparts under folder in Visual Studio: Often we are uses to create webparts in the root folder of the Visual Studio Projects. As the number of webparts grow, the folder starts to increase and navigability/manageability problem comes to light. One solution is to keep all webparts under a different folder. Another option is to keep related items under a particular folder. For example all items for navigation can be kept under a root folder “Navigation”. The following image shows all webparts are  kept under ‘webparts’ folder.

    image

Figure 3: Webparts organized under folders

  • Enter descriptive name and description for your webparts: As soon you create an webpart, enter descriptive name and descrition. To do so open the file with .webpart extension and modify Title and Description property as shown below:

image

Figure 4: Title and description of an WebPart.

Friday, September 3, 2010

SharePoint 2010: Rename Site Url

Sometimes in development environment and even in production you may need to rename a site url. For example you have planned a site with URL www.mysite.com in Development or in Production. Later you have decided to rename the site url to www.yoursite.com. There’s no shortcut way to do that in SharePoint. You need to delete the existing web application for url www.mysite.com and create a new one with url www.yoursite.com.

1. Backup the site collection first

Run the following powershell command to backup site.

Backup-SPSite –Identity SiteUrl –Path BackupLocation

2. Delete the existing site from central admin.

Navigate to central admin and delete the web application including database and IIS site as shown below:

image

3. Create the a new site with new Url (say http://www.yoursite.com)

Now create a new web application with the new url in host header as shown below:

image

4. Restore the backup on the new site

Finally restore the backup taken on step 1 overwriting the new site collection. Run the following command on powershell command

Restore-SPSite –Identity SiteUrl –Path BackupFilePath -Force