Salesforce sending SMS through FrontlineSMS

Last Updated on Thursday, 27 May 2010 07:15 Written by Steve Thursday, 27 May 2010 07:13

FrontlineSMS is an open source application used primarily in the developing world for communicating with large numbers of cell phone users. It’s a locally installed application that pushes SMS messages out through your tethered cell phone. It’s a really cool app with all sorts of great real-world usage. My friend Dale Zak just built an awesome reminders module for it, and that got me looking at it again.

The newest version of FrontlineSMS (1.6) has a very simple API for sending SMS messages. I have been successful in getting Salesforce.com to use this API to send SMS messages to single Contacts and to Leads and Contacts who are members of a Campaign. Here’s a short video of it in action:

Here’s what’s going on in the one-off send:

  1. I’m using Visualforce to create UI that is called by the Send SMS buttons
  2. The Visualforce page accepts the SMS text input
  3. Clicking send shows an iFrame that calls the FrontlineSMS API correctly: http://localhost:<port>/sms_number/sms_message
  4. A Task is related to the Contact with the data and content of the SMS message

Why use the iFrame? Since FrontlineSMS is locally installed, we have to communicate with it from the browser. A more robust option would be to create Javascript that makes the localhost calls and listens for the success or failure and proceeds accordingly.

To all the members of a Campaign, we do the following:

  1. The Visualforce page accepts the SMS text input
  2. Clicking Send exposes an Action Poller, which calls code every 5 seconds
  3. When the Action Poller calls code, 10 Campaign Members of the correct Status are queried
  4. 10 iFrames are populated with the FrontlineSMS API URLs for the messages
  5. The Campaign Member records have their Status changed so they won’t be queried again
  6. Tasks are related to the Campaign and the Contact or Lead

Here are the Salesforce code bits.

Visualforce page for one-off sends:

<apex:page standardController="Contact" extensions="extSendSMS" >
	<apex:form >
		<apex:sectionHeader title="FrontlineSMS" subtitle="Send SMS via FrontlineSMS">
			<description>
				Send an SMS using FrontlineSMS:
				<ul>
					<li>Install FrontlineSMS on your computer</li>
					<li>Make sure FrontlineSMS is running</li>
					<li>Make sure your phone is connected to FrontlineSMS</li>
				</ul>

			</description>

		</apex:sectionHeader>
		<apex:pageMessages id="pageMessages"/>
		<apex:pageBlock id="smsblock" title="">
			<apex:pageBlockSection id="smssection">
				<apex:outputField value="{!contact.MobilePhone}"/>
				<apex:inputField value="{!task.Description}"/>
			</apex:pageBlockSection>
			<apex:pageBlockButtons id="smsbuttons">
				<apex:commandButton value="Send SMS" action="{!sendSMS}" rerender="smsiframe"/> 

			</apex:pageBlockButtons>
		</apex:pageBlock>
		<apex:pageBlock id="smsiframe" title="">
		<apex:iframe id="frontlinesmsIframe" src="{!endpoint}" scrolling="no"  rendered="{!showFrontlineSMSiframe}"/>
		</apex:pageBlock>
	</apex:form>
</apex:page>

Apex controller for one-off sends:

public with sharing class extSendSMS {
	public Task task {get;set;}
	public String endpoint {get;set;}
	public Boolean showFrontlineSMSiframe {get;set;}
	private Contact contact;
	private Lead lead;
	public extSendSMS(ApexPages.StandardController stdController) {
		this.contact = (Contact)stdController.getRecord();
		this.contact = [select id, MobilePhone from Contact where Id=:contact.id];
		this.task = new Task();
		this.task.WhoId = contact.Id;
		this.task.type = 'SMS';
		this.task.ActivityDate = Date.Today();

		this.task.status = 'Completed';
		showFrontlineSMSiframe = false;
    }
    public PageReference sendSMS(){
    	this.task.subject = 'SMS: ' + this.task.Description;
    	insert task;
    	String strippedMobilePhone = '';
    	strippedMobilePhone = this.contact.mobilePhone;
    	strippedMobilePhone = strippedMobilePhone.replaceAll('[^0-9]', '');

    	endpoint = 'http://localhost:8565/'+ strippedMobilePhone +'/'+ this.task.Description;
    	showFrontlineSMSiframe = true;
    	return null;
    }
}

Visualforce page for Campaign sends:

<apex:page standardController="Campaign" extensions="extSendSMSCampaign" >
<apex:form >
		<apex:sectionHeader title="FrontlineSMS" subtitle="Send SMS via FrontlineSMS">
			<description>
				Send an SMS using FrontlineSMS:
				<ul>
					<li>Install FrontlineSMS on your computer</li>
					<li>Make sure FrontlineSMS is running</li>
					<li>Make sure your phone is connected to FrontlineSMS</li>
				</ul>

			</description>

		</apex:sectionHeader>
		<apex:pageMessages id="pageMessages"/>
		<apex:pageBlock id="smsblock" title="">
			<apex:pageBlockSection id="headersection" >
				<apex:outputLabel for="membercount">Member Count</apex:outputLabel><apex:outputText id="membercount" value="{!memberCount}"/>
				<apex:outputLabel for="numbersent">Number Sent</apex:outputLabel><apex:outputText id="numbersent" value="{!numberSent}"/>
				<apex:inputField value="{!dummyTask.Description}"/>

			</apex:pageBlockSection>
			<apex:pageBlockSection id="smssection" rendered="{!showFrontlineSMSiframe}">
				<apex:outputLabel for="endpoint1">SMS 1 (debug)</apex:outputLabel><apex:outputText id="endpoint1" value="{!endpoint1}"/>
				<apex:outputLabel for="endpoint2">SMS 2 (debug)</apex:outputLabel><apex:outputText id="endpoint2" value="{!endpoint2}"/>
				<apex:outputLabel for="endpoint3">SMS 3 (debug)</apex:outputLabel><apex:outputText id="endpoint3" value="{!endpoint3}"/>
				<apex:outputLabel for="endpoint4">SMS 4 (debug)</apex:outputLabel><apex:outputText id="endpoint4" value="{!endpoint4}"/>
				<apex:outputLabel for="endpoint5">SMS 5 (debug)</apex:outputLabel><apex:outputText id="endpoint5" value="{!endpoint5}"/>
				<apex:outputLabel for="endpoint6">SMS 6 (debug)</apex:outputLabel><apex:outputText id="endpoint6" value="{!endpoint6}"/>
				<apex:outputLabel for="endpoint7">SMS 7 (debug)</apex:outputLabel><apex:outputText id="endpoint7" value="{!endpoint7}"/>
				<apex:outputLabel for="endpoint8">SMS 8 (debug)</apex:outputLabel><apex:outputText id="endpoint8" value="{!endpoint8}"/>
				<apex:outputLabel for="endpoint9">SMS 9 (debug)</apex:outputLabel><apex:outputText id="endpoint9" value="{!endpoint9}"/>
				<apex:outputLabel for="endpoint10">SMS 10 (debug)</apex:outputLabel><apex:outputText id="endpoint10" value="{!endpoint10}"/>
			</apex:pageBlockSection>
			<apex:pageBlockButtons id="smsbuttons">
				<apex:commandButton value="Send SMS" action="{!sendSMS}" rerender="smsblock,smsiframe,actionblock"/>
			</apex:pageBlockButtons>
		</apex:pageBlock>
		<apex:pageBlock id="actionblock" title="">
			<apex:actionPoller action="{!sendSMSToMembers}" rerender="smsblock,smsiframe,actionblock" interval="5" rendered="{!showActionBlock}"/>
		</apex:pageBlock>
		<apex:pageBlock id="smsiframe" title="" >
			<apex:pageBlockSection id="smsisection" rendered="{!showFrontlineSMSiframe}">
				SMS 1 (debug)
				<apex:iframe id="frontlinesmsIframe1" height="30" src="{!endpoint1}" scrolling="no"  />
				SMS 2 (debug)
				<apex:iframe id="frontlinesmsIframe2" height="30" src="{!endpoint2}" scrolling="no"  />
				SMS 3 (debug)
				<apex:iframe id="frontlinesmsIframe3" height="30" src="{!endpoint3}" scrolling="no"  />
				SMS 4 (debug)
				<apex:iframe id="frontlinesmsIframe4" height="30" src="{!endpoint4}" scrolling="no"  />
				SMS 5 (debug)
				<apex:iframe id="frontlinesmsIframe5" height="30" src="{!endpoint5}" scrolling="no"  />
				SMS 6 (debug)
				<apex:iframe id="frontlinesmsIframe6" height="30" src="{!endpoint6}" scrolling="no"  />
				SMS 7 (debug)
				<apex:iframe id="frontlinesmsIframe7" height="30" src="{!endpoint7}" scrolling="no"  />
				SMS 8 (debug)
				<apex:iframe id="frontlinesmsIframe8" height="30" src="{!endpoint8}" scrolling="no"  />
				SMS 9 (debug)
				<apex:iframe id="frontlinesmsIframe9" height="30" src="{!endpoint9}" scrolling="no"  />
				SMS 10 (debug)
				<apex:iframe id="frontlinesmsIframe10" height="30" src="{!endpoint10}" scrolling="no"  />
			</apex:pageBlockSection>
		</apex:pageBlock>
	</apex:form>
</apex:page>

Apex controller for campaign sends:

public with sharing class extSendSMSCampaign {
	public Campaign campaign {get;set;}
	public Task dummyTask {get;set;}
	public String SMSmessage {get;set;}
	public String endpoint1 {get;set;}
	public String endpoint2 {get;set;}
	public String endpoint3 {get;set;}
	public String endpoint4 {get;set;}
	public String endpoint5 {get;set;}
	public String endpoint6 {get;set;}
	public String endpoint7 {get;set;}
	public String endpoint8 {get;set;}
	public String endpoint9 {get;set;}
	public String endpoint10 {get;set;}
	public Integer memberCount {get;set;}
	public Integer numberSent {get;set;}
	private List<String> endpoints = new List<String>();
	private List<Task> SMSTasks = new List<Task>();
	private Id whoId;
	public Boolean showActionBlock {get;set;}
	public Boolean showFrontlineSMSiframe {get;set;}
	private Contact contact;
	private Lead lead;
	private CampaignMember campaignMember;

	public extSendSMSCampaign(ApexPages.StandardController stdController) {
		this.campaign = (campaign)stdController.getRecord();
		memberCount = [select count() from CampaignMember where CampaignId=:this.campaign.id AND status='sent' and (Contact.MobilePhone <> null OR Lead.MobilePhone <> null)];
		showFrontlineSMSiframe = false;
		showActionBlock=false;
		SMSmessage = 'test message';
		numberSent =0;
		dummyTask = new Task();
    }

    public PageReference sendSMS(){
    	showActionBlock = true;
    	showFrontlineSMSiframe = true;
    	return null;
    }

     public PageReference sendSMSToMembers(){
     	endpoints = new List<String>();
     	endpoint1 = '';
		endpoint2 = '';
		endpoint3 = '';
		endpoint4 = '';
		endpoint5 = '';
		endpoint6 = '';
		endpoint7 = '';
		endpoint8 = '';
		endpoint9 = '';
		endpoint10 = '';
		SMSTasks = new List<Task>();
    	List<CampaignMember> myCampaignMembers = new List<CampaignMember>();
    	myCampaignMembers = [select id, Contact.MobilePhone,Contact.Id,Lead.Id, Lead.MobilePhone, status from CampaignMember where CampaignId=:this.campaign.id AND status='sent' and (Contact.MobilePhone <> null OR Lead.MobilePhone <> null) limit 10];
		if(myCampaignMembers.size()>0){
	    	for(Integer i=0;i<myCampaignMembers.size();i++){
	    		CampaignMember thisMember = myCampaignMembers[i];
		    	String strippedMobilePhone = '';
		    	if(thisMember.Contact.MobilePhone!=null){
		    		strippedMobilePhone = thisMember.contact.mobilePhone;
		    		whoId = thisMember.Contact.Id;
		    	} else if(thisMember.Lead.MobilePhone!=null){
		    		strippedMobilePhone = thisMember.contact.mobilePhone;
		    		whoId = thisMember.Lead.Id;
		    	}
		    	strippedMobilePhone = strippedMobilePhone.replaceAll('[^0-9]', '');

		    	endpoints.add('http://localhost:8565/'+ strippedMobilePhone +'/'+ dummyTask.Description);
		    	SMSTasks.add(new Task(WhoId = whoId,WhatId=this.campaign.id,type = 'SMS',ActivityDate = Date.Today(),Description=dummyTask.Description,Status='Completed',Subject='SMS: '+dummyTask.Description));
	    	}
	    	numberSent += myCampaignMembers.size();
	    	if(endpoints.size()>0){
	    		While(endpoints.size()<10){
	    			endpoints.add('');
	    		}

		    	endpoint1 = endpoints[0];
				endpoint2 = endpoints[1];
				endpoint3 = endpoints[2];
				endpoint4 = endpoints[3];
				endpoint5 = endpoints[4];
				endpoint6 = endpoints[5];
				endpoint7 = endpoints[6];
				endpoint8 = endpoints[7];
				endpoint9 = endpoints[8];
				endpoint10 = endpoints[9];
	    	}
	    	for(CampaignMember myMember : myCampaignMembers){
	    		myMember.status='Responded';
	    	}
	    	update myCampaignMembers;
	    	insert SMSTasks;
		} else {
			showActionBlock = false;
		}
		return null;
    }

}
Learn More

Send To Omnifocus from Google Reader

Last Updated on Tuesday, 25 May 2010 11:45 Written by Steve Tuesday, 25 May 2010 11:45

When I’m surfing the web or reading email, I often run across things that I don’t have time to complete now, but want to do later. It’s at times like these I “send” things to Omnifocus. I do this in many ways. Omnifocus has great built in scripting, so I have keyboard shortcuts to send things to the Omnifocus Quick Entry form from Entourage, Safari, Mailplane, etc.

When I’m on a web page, I use Nik’s URIHandler to send things directly to my reading list via a bookmarklet. It’s incredibly handy for all sorts of things–check out my example for creating a grocery list.

But most of the stuff I read comes in via Google Reader and there hasn’t been a good way for me to send a story from Google Reader to Omnifocus. A while back Google Reader added a Send To function allowing you to send information from the current story to any URL. The problem is, Omnifocus is a locally installed app–it doesn’t have URLs.

To solve this problem, I have built a very simple PHP page that Google Reader can use to Send To Omnifocus. Here’s a quick video of how it works:

First you need to install Nik’s URIHandler. It’s really easy to install and once you’ve done that, your computer will have a way to create items in Omnifocus via Javascript.

Then you need to set up Google Reader to Send To the URIHandler.

Go to Settings > Send To and create a new link

The URL looks like this (substitute your real project and context names): http://gokubi.com/ofocus.php?title=Read: ${title}&url=${url}&context=yourContextHere&project=yourProjectHere

And the image URL looks like this: http://gokubi.com/images/omnifocus-128-75×75.png

Save that and you can Send To Omnifocus right from Google Reader!

If you want to do this on your own, and not use my send to URL, the PHP script is really simple:

<?php
echo "<script language=\"JavaScript\">\n";
echo "window.location = \"x-omnifocus://parsetasks?text=" .  htmlentities($_GET['title']) . " :: " . htmlentities($_GET['project']) . " @ " . htmlentities($_GET['context']) . " // " . htmlentities($_GET['url']) . "\";\n";
echo "</script>\n";
?>

You can create as many Send To buttons as you want–put whatever you want in the title, and use different projects and contexts. It’s very flexible and easy to use.

Learn More

Participation

Last Updated on Monday, 24 May 2010 02:25 Written by Steve Monday, 24 May 2010 02:25

Alinsky on dignity:

We learn, when we respect the dignity of the people, that they cannot be denied the elementary right to participate fully in the solutions to their own problems. Self-respect arises only out of people who play an active role in solving their own crises and who are not helpless, passive, puppet-like recipients of private or public services. To give people help, while denying them a significant part in the action, contributes nothing to the development of the individual. In the deepest sense it is not giving but taking — taking their dignity. Denial of the opportunity to participate is the denial of human dignity and democracy. It will not work.

Learn More
Copyright © 2009 Afterburner - Free GPL Template. All Rights Reserved.
WordPress is Free Software released under the GNU/GPL License.