Monthly Archives: February 2013

BackChannel Chat– Office 365

I’ve been working on a real time chat tool called BackChannel Chat, its designed to work with facilitators (educators) and contributors (students). It has a number of moderation features, can embed media and is generally pretty easy to use. Check out the comparison page if you want some more info.

Voting

Anyway as it’s a collaboration tool and I thought it might be fun to see how it could be integrated into Office 365 and SharePoint 2013. The good part about doing this kind of integration is that I could make the application ‘Provider hosted’ and not have to work in the normal constrained SharePoint development model.

If your not familiar with various hosting models in SharePoint 2013, the Provider hosted model is where the application is not hosted on the same server / infrastructure as the SharePoint instance.

Since my application is an MVC 4 application and I’m not that keen to setup a full blown SharePoint develop environment and revisit all my horrible past sins, I was pleasantly surprised to find that I only needed to install a SharePoint client install package and that I could create a free office 365 developer portal. So no messing around with SharePoint, my kind of SharePoint development.

My application suits the App Part model nicely, so here SharePoint is providing me with an iFrame, but it’s a little bit more than an iFrame. If it was just an iFrame, then authentication might be a problem, rather SharePoint does provide an iFrame, but it also has a component that performs a POST operation with the details that I need to work with the client object model.

Once I get the reference to the CSOM I can look at the user, get things like a profile picture, their name, even the permissions the user has on the site where my app part is placed. So now I can hook in my application and provide it as a full experience:

BCCTeamSite

So if your interested in a full real time chat solution for SharePoint 2013, have a look at BackChannel Chat, all of the premium features are available in the SharePoint 2013 version plus some SharePoint  specific features.

I haven’t put the application in the SharePoint app store yet, still not sure how that will play out, but I’m certainly happy to explore this some more.

Mobile Anaylitcs

Most of us are aware of website analytics, specifically Google Analytics, which is an incredible piece of software.  I just love watching the real time view of the visitors on your site:

GARealTime

(The above image was from when Reddit broke the 100k user mark).

Did you also know that Google Analytics can be used for mobile applications?

Well it turns out that you have access to many of the same features. You can view users in real time,track exceptions and if you set up your tracking properly you can get some nice insights into how people use your application.

The SDK documentation show how easy it is to work with, but its also just as easy to work with the SDK with Phonegap (Cordova) apps.

I won’t go into the details on how to compile the SDK into your project, I’d rather show what some phonegap plugin code might look like.

For iOS:

- (void) start:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
	[GAI sharedInstance].trackUncaughtExceptions = YES;
    // Optional: set Google Analytics dispatch interval to e.g. 20 seconds.
    [GAI sharedInstance].dispatchInterval = 20;
    // Optional: set debug to YES for extra debugging information.
    [GAI sharedInstance].debug = YES;
    // Create tracker instance.
    id tracker = [[GAI sharedInstance] trackerWithTrackingId:@"UA-XXXX-1"];
    NSLog(@"GA Loaded and running");
    tracker.sessionStart = YES;
}

- (void) trackEvent:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
   
    id tracker = [[GAI sharedInstance] trackerWithTrackingId:@"UA-XXX-1"];
    
	NSString* category = [arguments objectAtIndex:1];
	NSString* action = [arguments objectAtIndex:2];
	NSString* label = [arguments objectAtIndex:3];
	int value = [[options valueForKey:@"value"] intValue];
    
    [tracker trackEventWithCategory:category
                         withAction:action
                          withLabel:label
                          withValue:[NSNumber numberWithInt:100]];
	
	NSLog(@"GoogleAnalyticsPlugin.trackEvent::%@, %@, %@, %d",category,action,label,value);
    
}


- (void) trackPageView:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
    id tracker = [[GAI sharedInstance] trackerWithTrackingId:@"UA-XXX-1"];
    NSString* pageUri = [arguments objectAtIndex:0];
	
    [tracker trackView:pageUri];
  
}

For Andriod:

public class GoogleAnalyticsTracker extends CordovaPlugin {
	public static final String START = "start";
	public static final String TRACK_PAGE_VIEW = "trackPageView";
	public static final String TRACK_EVENT = "trackEvent";
	public static final String SET_CUSTOM_VARIABLE = "setCustomVariable";
    
	public static final int DISPATCH_INTERVAL = 20;
	
	private Tracker tracker;
	private GoogleAnalytics instance;
	
	public GoogleAnalyticsTracker() {
		
	}
	
	@Override
	public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException 
	{
		if(instance == null){
			instance = GoogleAnalytics.getInstance(cordova.getActivity().getBaseContext());
			
		}
		if(instance != null){
			tracker = instance.getTracker("UA-XXX-1");
		}
		
		
		LOG.d("Analytics", "Analytics Loading ... " + action);
		if (START.equals(action)) {
			try {
				LOG.d("Analytics", "Start..." + data.getString(0));
				start(data.getString(0));
				callbackContext.success("true");
				return true;
			} catch (JSONException e) {
				
				return false;
			}
		} else if (TRACK_PAGE_VIEW.equals(action)) {
			try {
				LOG.d("Analytics", "Track Page view... " + data.getString(0));
				trackPageView(data.getString(0));
				callbackContext.success("true");
				return true;
			} catch (JSONException e) {
				
				return false;
			}
		} else if (TRACK_EVENT.equals(action)) {
			try {
				trackEvent(data.getString(0), data.getString(1), data.getString(2), data.getLong(3));
				callbackContext.success("true");
				return true;
			} catch (JSONException e) {
				
				return false;
			}
		} else if (SET_CUSTOM_VARIABLE.equals(action)){
			try {
				setCustomVar(data.getInt(0), data.getString(1), data.getString(2), data.getInt(3));
				callbackContext.success("true");
				return true;
			} catch (JSONException e) {
				
				return false;
			}
		} 
		callbackContext.success("true");
			return true;
		
		
	}
	
	private void start(String accountId) {
		if(tracker != null){
			
			tracker.setStartSession(true);
		}
		
		
	}
	
	private void trackPageView(String key) {
		
		if(tracker != null){
			tracker.trackView(key);
		}
	}

	private void trackEvent(String category, String action, String label, Long value){
		if(tracker != null){
			tracker.trackEvent(category, action, label, value);
		}
	}

Then the JavaScript that is called from your app might look like

Analytics.prototype.start = function(accountId, successCallback, failureCallback) {
	return cordova.exec(
				successCallback,			 
				failureCallback,						
				'GoogleAnalytics',				
				'start',								
				[accountId]);
};

The other methods for pageview and event tracking follow much the same format, the key is to call the cordova.exec method.