Monday, September 24, 2012

Android Experience

Nowadays electronic gadgets are so pervasive. I have iPhone, iPad, multiple laptops and desktops at home. Recently I got a Nexus 7 tablet, my first Android device. What really impressed me is its easiness of development process.

Apple products are cool, no doubt about it. But you have to have a physical Mac machine to work on iOS/Mac apps. In addition you need to register the developer license which cost $99/year. Apple defines quite a bit of restriction and rules to mark sure they have full control of the Apple nation. Apple's main focus is user experience, not much for developers. Many people claim Object C and XCode the worst development environment comparing with other popular languages and IDE in the market. However, due to Apple's huge crowd of so called high-end and loyal customers, more and more developers are joining this not so friendly platform.

On the other hand, Windows phone as a late-catch-up follower, surprisingly, still requires the same $99/year fee too. With registration done, you need to install the commercial Visual Studio IDE, Windows phone SDK and that notorious Zune package to develop a simple app to run on a Windows phone handset. Should they have made all free on the first day when realizing they were way behind Apple and Google? I love C# and .NET, but I feel sad about those dump decisions made by Microsoft.

In the Android world things are very different. You only need to pay one-time $25 registration fee to publish your app to the public. It's totally free for you to do any coding experience on your own Android devices. Unlike proprietary object C and C#, you will use Java and eclipse to program the Android apps. Java is the most used object-oriented programming language (source from Tiobe) in the past few years, and open-source eclipse is a popular IDE for many programming languages for years. All Java developers would easily switch to Android development.

Yesterday I downloaded the free Android SDK, unzipped it (no need to install anything), and I was able to open eclipse IDE to create a new Android project. After installed the USB driver for Nexus 7 I could connect the tablet to my development machine using USB cable, within 30 minutes, my first "Hello World" Android app was running in the Nexus 7. The only problem I had was the Android emulator which is super slow and basically useless. But running and debugging the code via a physical device is easy and fast.

Android devices become so popular in just a few years. Will Android rules the mobile world as Microsoft dominates PC world in the past 30 years? No one knows, but people like Android for reasons. From a developer point of view, it's free, open and easy to work with. All that is a nice invitation to developers to join the Android world.

Monday, September 17, 2012

SharePoint 2010 Email Notification With Workflow

Here's a common scenario in SharePoint environment: you have a SharePoint custom list to store and trace some tasks or jobs, you want the system automatically send out email notification based on some conditions. Let's say there's a SharePoint List with following two columns:

1. Tickler Date: a DateTime field that triggers the workflow to send out the email notification
2. Tickler Contacts: a People field (multiple section) where the notification email will be sent to

The requirement is simple:

1. Email notification sent to "Tickler Contacts" whenever the "Tickler Date" is reached, if both fields are defined.
2. Notification should be aware of the change on those two fields, and updated "Tickler Date" and "Tickler Contacts" should be applied.

There are a few options to accomplish such task. We can set a Windows Scheduled Task to run a console app which scans the whole list daily and send the notification, or use SharePoint Timer Jobs to do similar things, or implement SharePoint Workflow. SharePoint Workflow is very powerful and capable of handling many SharePoint tasks easily. With SharePoint Designer, you can even visually work on workflow's conditions, steps and actions, and apply the Workflow directly to the server without any code deployment (arguably good and bad on this). Workflow approach will be discussed in this post.

SharePoint Designer Workflow has a built-in "Send an email" action. We use that as the only one action step in our first Workflow for notification. Because the recipients come from a multi-user People field, we need to set Person field returned as delimited email addresses:


By default the BCC is not available on the email form. However, when you can highlight the send email action and click the "Advanced Properties" button on the top menu you will see the BCC field. You could even fine tune the formatting of the mail content since the original body HTML can be seen and updated there:


The next step is to figure out how to start the Workflow. A list-associated Workflow can be set to start manually, or automatically start when a list item is created or updated. For the simple Workflow we defined above is not going to start automatically because we haven't defined the condition yet.

If the trigger condition is based on the date time field, then we can also utilize retention policy to start a Workflow. Go to List's settings page and click "Information management policy settings" you will see following settings (the top "List Based Retention Schedule" won't be available if "Library and Folder Based Retention" Site Collection Feature is not activated):


We are interested in the item properties, so click the Item and go to Item policy editing page, check "Enable Retention", and then add a retention stage:


The condition here is that when "Tickler Date" is passed by 0 day, or "Tickler Date" is today, then workflow will start and send the email notification. All look good except that requirement #2 can not be satisfied. The "Tickler Date" is fixed, and Retention policy won't recalculate the condition when the "Tickler Date" is changed. There's no workaround on that as far as I know. So Retention policy is not an option in our case.

The solution is let Workflow deals with the conditions by itself, and make it start automatically when a List item is created or updated:


The Workflow quite explains itself. First it checks if "Tickler Contacts" and "Tickler Date" are both valid. It would simply "quit" (WF completes) if any of those two empty or "Tickler Date" has passed. Otherwise it continues to further logic. A Workflow variable is set to the "Tickler Date" so later it has a reference to compare if that has been changed. Then two parallel branches are split to run simultaneously. The first branch is waiting until the "Tickler Date" reaches so email notification will be sent out, and the workflow completes its work; the second branch is pending and looking for any change on the "Tickler Date" field, when that occurs the workflow stops and a new workflow instance will be created by SharePoint using the updated "Tickler Date", because the workflow had set to auto-start when list item is updated.

You may ponder what happens if the fields other than "Tickler Date" have been changed, would a new Workflow instance will be created? The answer is no. SharePoint only allows one instance of Workflow for each version to run. That's why we cancel the old Workflow instance when the "Tickler Date" is changed, so a new Workflow instance can be created and start.

You may also wonder why not just simply using the general "Modified" field instead of "Tickler Date": cancel the old Workflow instance whenever the List item is changed, then there’s room for a new WF instance with updated data. You will see that won't work if you try it out. The reason is that SharePoint does modify the List item silently for some background process such as updating List item's Workflow status. That causes time change on List item's "Modified" field which stops old Workflow, but SharePoint won't start a Workflow for such internal processing.

A final note: don't use system account to test your Workflow. It won't work! Creating or updating a list item using system account won't trigger associated Workflow even although the Workflow is configured to auto-start. Under the hood List Workflow was implemented using List Item Event Receivers, and system account is just ignored there.
Reference: Create a Detailed Custom Task Notification with a SharePoint Designer Workflow: http://sharepoint-videos.com/sp10creating-a-workflow-on-a-list-using-sharepoint-designer-2010

Tuesday, September 04, 2012

An "Unsocial" Bug in Socail NewsGator

NewsGator is a Social Networking Solution for SharePoint environment. Recently our company utilizes NewsGator to make our SharePoint 2010 portal more social and Facebookish. I found an issue related to NewsGator Community API (Social Sites 2010 Suite V2.6.615) today, and missing communication between different dev teams inside NewsGator may be the culprit.

Newsgator heavily uses Feeds and Communities concepts to connect people together and encourages employees participation. Overall those NewsGator features are great. The problem is that, as most third-party components, it's very hard to do some customization on top of NewsGator Components. The NewsGator API and its documentation are so limited that I have to use reflector to figure out some issues to do my work.

Here is the secnario: for security and a few other considerations we don't allow end users to create NewsGator communities directly. Instead, a custom WebPart is built for creating a new Community programmatically so you have full control of that. One of the business requirement is that you can set the NewsGator Community as Public or Private in the custom WebPart.

It sounds straightforward, just setting Community's properties right? But I just couldn't find out the proper way to do that (the formal NewsGator API documentation has less than 20-page with just a few simple code snippets showing how to create Feeds). Thanks to reflector I found there's a SocialContextClient class often used to get/set SocialGroup (Community) inside backend web service calls. So I tried the following code:
    SocialSites.ApplicationServices.SocialContextClient contexClient = 
      new SocialSites.ApplicationServices.SocialContextClient();
    NewsGator.Social.Data.SocialGroup community = contexClient.GetCommunityByName("CommunityName");
    community.PrivacyLevel = NewsGator.Social.Data.SocialGroupPrivacyLevel.Private;
    community.Discoverable = false;
    contexClient.UpdateSocialGroup(community);
The PrivacyLevel and Discoverable properties are responsible for handling the public-visibility, and their values are stored in a SocialGroup table in backend database (NewsGator doesn't use SharePoint lists and has its own database). I could see that the code has successfully updated their values in SocialGroup table, however the created Community is still public. It's very confusing. I checked all possibilities including permission, caching, synchronization, etc. but no luck.

It turns out there’s another method to get the SocialGroup (Community) using SharePointConextManagerBase:
    using (SPSite site = new SPSite(SiteUrl))
    {
        using (SPWeb web = site.OpenWeb())
        {
            SocialGroup community = 
          new NewsGator.Social.Library.Client.SharePointContextManagerBase().GetCommunity(web);
            SocialGroupPrivacyLevel privacyLevel = community.PrivacyLevel;
            bool discoverable = community.Discoverable;
        }
    }
I found that the community’s settings in above method are not the same as what we get from the first method using SocialContextClient. By reflector I traced out that SharePointContextManagerBase actually checks web.AllProperties[NewsGator.Social.Library.SocialGroups.WebKeys.PrivacyLevel] and web.AllProperties[NewsGator.Social.Library.SocialGroups.WebKeys.Discoverable] values to populate the privacy and discoverable values, and it’s not related to any NewsGator backend database.

Apparently different teams from NewsGator had built these two methods with their own purpose. Method SharePointContextManagerBase is used by UI WebParts so updating the backend database by SocialContextClient class won’t have any effect. To workaround this problem simply assign the web’s properties which is checked by SharePointContextManagerBase mthod, then everything works as expected:
    private void UpdateNewsGatorCommunity(SPWeb web, bool isPrivateCommunity)
    {
        if (isPrivateCommunity) // Set NewsGator Community as private
        {
            try
            {
                // Direct value assignment to avoid referecing NewsGator dlls
                web.AllProperties["ng-community-privacy"] = "2";
                web.AllProperties["ng-community-discoverable"] = "False";
                web.Update();
            }
            catch (Exception ex)
            {
                LoggingService.LogError("UpdateNewsGatorCommunity error: " + ex.Message, ex.StackTrace);
            }
        }
    }
It's hacking not an elegant solution and we hope NewsGator could fix this issue in the future.

This is a perfect example of a problem causing by one piece of information defined/stored in multiple places, and reason for that is most likely missing communication between people and teams in a company.