<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>gokubi.com &#187; CRM</title>
	<atom:link href="http://gokubi.com/archives/category/technology/crm/feed" rel="self" type="application/rss+xml" />
	<link>http://gokubi.com</link>
	<description></description>
	<lastBuildDate>Sat, 14 Aug 2010 14:19:35 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Textexpander snippets for Apex</title>
		<link>http://gokubi.com/archives/textexpander-snippets-for-apex</link>
		<comments>http://gokubi.com/archives/textexpander-snippets-for-apex#comments</comments>
		<pubDate>Sat, 14 Aug 2010 14:19:35 +0000</pubDate>
		<dc:creator>Steve</dc:creator>
				<category><![CDATA[CRM]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://gokubi.com/?p=1261</guid>
		<description><![CDATA[I just upgraded my work computer to Snow Leopard and one of the benefits I was most excited about was being able to update Textexpander, the incredibly handy snippet tool. You create short codes and when you type them they are replaced with pre-recorded text. They&#8217;re much better at describing what it does. It&#8217;s really [...]]]></description>
			<content:encoded><![CDATA[<p>I just upgraded my work computer to Snow Leopard and one of the benefits I was most excited about was being able to update <a href="http://www.smileonmymac.com/TextExpander/">Textexpander</a>, the incredibly handy snippet tool. You create short codes and when you type them they are replaced with pre-recorded text. They&#8217;re much better at <a href="http://www.smileonmymac.com/TextExpander/screencast/index.html">describing what it does</a>. It&#8217;s really handy for email signatures, canned content for technical support&#8211;really anything you ever type more than once.</p>
<p>With the new features Textexpander got really interesting for use in coding on the Force.com platform. The Force.com IDE doesn&#8217;t have code completion like the in-line code editor does, and it&#8217;s something I look forward to Salesforce.com providing. But in the meantime Textexpander is a great solution for helping you with code you write over and over again.</p>
<p>Textexpander now allows you to put variables in your snippets and allows you to fill those variables as you are expanding the snippet. So, for example, when you&#8217;re declaring a List of Accounts, you don&#8217;t have to type List twice, and Account twice. It&#8217;s really slick.</p>
<p>I&#8217;ve created a <a href="http://gokubi.com/files/Apex%20Snippets.textexpander">Snippet Group for common Apex syntax</a>. To use the file, install Textexpander. Right click on the link to the Snippet Group and save to your computer. It&#8217;s an XML file, but you need to maintain the .textexpander extension on it. Then open up Textexpander and &#8216;Add Group from File.&#8217; The group will show up in Textexpander.</p>
<p>Right now this Snippet Group includes:</p>
<ul>
<li>declaration of lists, sets, and maps</li>
<li>declaration of an sObject</li>
<li>for loops, both kinds</li>
<li>if statements</li>
<li>select statements</li>
<li>test methods</li>
<li>try catch block</li>
<li>system.assert statements</li>
<li>describe result for an sObject</li>
<li>describe result for a field</li>
</ul>
<p>It&#8217;s just a start, but if you take it and like it, add to it. Let me know and we&#8217;ll expand this Snippet Group to be comprehensive. Clearly there should be a VisualForce Sinppet Group as well. If you create one, please let me know!</p>
]]></content:encoded>
			<wfw:commentRss>http://gokubi.com/archives/textexpander-snippets-for-apex/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Salesforce.com as free, bolt-on analytics engine for any database app</title>
		<link>http://gokubi.com/archives/salesforce-com-as-free-bolt-on-analytics-engine-for-any-database-app</link>
		<comments>http://gokubi.com/archives/salesforce-com-as-free-bolt-on-analytics-engine-for-any-database-app#comments</comments>
		<pubDate>Wed, 02 Jun 2010 21:47:26 +0000</pubDate>
		<dc:creator>Steve</dc:creator>
				<category><![CDATA[CRM]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://gokubi.com/?p=1219</guid>
		<description><![CDATA[Over my career I&#8217;ve noticed that most applications have very limited reporting and charting features. I&#8217;ve tried to write some of my own and realized quickly why this is&#8211;analytics is hard! Visual query builders, dynamic charting, aggregation of data, it&#8217;s all really tough to do right. I also think most application teams start out with [...]]]></description>
			<content:encoded><![CDATA[<p>Over my career I&#8217;ve noticed that most applications have very limited reporting and charting features. I&#8217;ve tried to write some of my own and realized quickly why this is&#8211;analytics is hard! Visual query builders, dynamic charting, aggregation of data, it&#8217;s all really tough to do right. I also think most application teams start out with the feature list of business processes that need supporting&#8211;recording transactions, editing information, etc. I think they often punt on the analytics engine for these reasons&#8211;it&#8217;s hard and the value is limited if the app doesn&#8217;t even do what it needs to do yet. Because of this, most apps come later, if at all to the analytics game.</p>
<p>And because most of the apps we use have lousy analytics, we often don&#8217;t know what we&#8217;re missing. In my CRM consulting it was very common for people to under-value the analytics compared to the process streamlining aspects. Getting things done more quickly is important, but analytics can help tell you if you&#8217;re doing the right things.</p>
<p>Salesforce.com has a great analytics engine. It&#8217;s an amazing blend of efficiency and power. Easy things are in the reach of most users, and hard things are possible without coding.  Why are the analytics so good? Salesforce.com is a billion dollar company. They&#8217;ve spent amazing amounts of money building their platform, and as part of that, the killer analytics engine. They&#8217;ve made it a strategic priority, invested in it, and iterated over 10 years. It really shows.</p>
<p>Great, so Salesforce.com has good analytics. You might think that if you want to use those analytics tools, you need to ditch your app and rewrite the whole thing on Salesforce.com, right? Porting a functioning application is a big project, and while sometimes it might makes sense to do that, a lot of times it doesn&#8217;t. You may not have the money to do it. You may be on an open source platform and not want to go to a proprietary one.</p>
<p>But it turns out you don&#8217;t need to ditch your app to get Salesforce.com analytics for your data. Using free and open source tools you can bolt the Force.com Free Edition onto your application and very quickly get the power of Salesforce.com analytics for 100 users. For free. I did it myself last weekend. Here&#8217;s how it works:</p>
<ol>
<li>Get your app up and running</li>
<li>Get <a href="https://www.salesforce.com/form/signup/freeforce-platform.jsp">Force.com Free Edition</a>, or use your existing Salesforce.com organization</li>
<li>Create tables in Salesforce matching the key tables in your app</li>
<li>Create some simple triggers to get all your data relationships right</li>
<li>Set up an extract-transform-load (ETL) server like <a href="http://jitterbit.com">Jitterbit</a> to pull data from your app and push it to Salesforce.com.</li>
<li>Set up reports and dashboards on that data</li>
<li>Let your data inform the strategic direction of your organization</li>
</ol>
<p>If you do all this, you can pull data from your application directly into Salesforce.com, automatically updating it as changes are made in your app. Then your end users can get the analytics they can&#8217;t get in the app. And your key decision makers, who probably don&#8217;t use the app in the first place, can get the top level numbers and trends that help them manage the impact of your organization.</p>
<p><img class="aligncenter size-full wp-image-1226" title="jitterbit_architecture" src="http://gokubi.com/wp-content/uploads/2010/06/jitterbit_architecture2.png" alt="" width="481" height="232" /></p>
<p>I&#8217;ll now go into detail on each of the steps above to show you how it can be done.</p>
<p><strong>Step 1: Get your app up and running</strong></p>
<p>I chose to work with <a href="http://mifos.org">Mifos</a>, an open source application that helps microfinance institutions track their loans and borrowers. Microfinance is a global effort to get working capital in the hands of the poorest people. It&#8217;s amazing work being done by organizations all over the world. Mifos happens to be built on Tomcat with a MySQL database.  But which app I use really isn&#8217;t important. All that matters is that is has extensive functionality for specific business cases, and it has a database we can directly access. If it does these things, it&#8217;s a candidate for Salesforce analytics.</p>
<p>I installed Mifos on an Amazon.com EC2 server, which saved me the trouble of trying to get it to run on my Mac. I love EC2!</p>
<p><strong>Step 2: Get Salesforce</strong></p>
<p>I got <a href="http://developer.force.com/">a free Developer account</a> for this test case, but you could get <a href="http://www.salesforcefoundation.org/products/donation">Salesforce.com donated to your nonprofit</a>, go with the Force.com Free Edition, or purchase Salesforce.com.</p>
<p><strong>Step 3: Create your schema</strong></p>
<p>Creating analogous tables to all the tables in your app is one of the more time-consuming parts of this process. I sped things up by using Model Metrics&#8217;s great <a href="http://sites.force.com/appexchange/listingDetail?listingId=a0N300000016cVIEAY">Cloud Converter tool</a>. I got the field names and one row of values for each table I cared about&#8211;you don&#8217;t need to get every table, just the ones that figure into your analytics. I created an Excel tab for each table, and pasted the header names and values into each. I then let the Cloud Converter loose on it, and the tables were created in just a few minutes! There might be some cleanup necessary with field types&#8211;Cloud Converter guesses what type the field should be, but sometimes it guesses incorrectly. You can do this by hand, or use the metadata API with Eclipse for the fastest action.</p>
<p>All the tables in your app probably have primary keys&#8211;some unique Id field that marks each row. In your Salesforce schema, you want to mark the analagous fields are External Ids. This will allow you to use Salesofrce&#8217;s Upsert web service method. Without External Ids, you need to know the Salesforce.com record Id in order to Update it. Upsert is incredibly amazing for this use case&#8211;it makes it all so easy. Just pass it the primary key from the app and it will find the right record in Salesforce.</p>
<p>The app you&#8217;re coming from has relationships between tables (using the primary key fields I just talked about), so you need to recreate those relationships in Salesforce. Cloud Converter might be able to do this for you, but I didn&#8217;t try that out. Create your lookup relationships between the objects and you&#8217;re good to go.</p>
<p><strong>Step 4: Create your triggers</strong></p>
<p>The ETL server will create your Salesforce records just fine, but it won&#8217;t make sure they&#8217;re related to the correct parent records for you. To do this you need simple triggers on each object to look at the foreign key from your app find the right Salesforce object, and relate the two. Here&#8217;s an example where the Account Payment object looks for the parent Account when it&#8217;s inserted or updated. If it finds it, the relationship gets recorded and the Account Payments show up under the Account:</p>
<pre class="brush: java">
trigger accountPaymentParentfinder on account_payment__c (before insert, before update) {

	Set&lt;Integer&gt; accountIds = new Set&lt;Integer&gt;();

	Map&lt;Integer,Id&gt; accountIdMap = new Map&lt;Integer,Id&gt;();

	//loop through the trigger set and build a set of all the Account Ids
	for(account_payment__c myAccountPayment : Trigger.new){
		if(myAccountPayment.account_ID__c!=null){
			accountIds.add(myAccountPayment.account_ID__c.intValue());
		}

	}

	//go query for those Account Ids and create a map connecting them with the Salesforce Ids
	for(account__c thisaccount : [select id,account_Id__c from account__c where account_Id__c IN :accountIds]){
		accountIdMap.put(thisaccount.account_Id__c.intValue(),thisaccount.id);
	}

	//put the right Salesforce Id in the lookup relationship
	for(account_payment__c myAccountPayment : Trigger.new){
		if(myAccountPayment.account_ID__c!=null){
			myAccountPayment.account__c = accountIdMap.get(myAccountPayment.account_ID__c.intValue());
		}
	}
}
</pre>
<p>You need to do this for each object. If an object has more than one lookup relationship, you&#8217;ll need to create the Set and Map structure for each. They all follow the same pattern. Easy peasy.</p>
<p><strong>Step 5: Set up your ETL server and transformations</strong></p>
<p>I chose <a href="http://jitterbit.com">Jitterbit</a> for my ETL server. They have a free Community edition but I used a 30 day trial of their Enterprise edition, only because there was a machine image on Amazon.com EC2 for the enterprise version. Installation was a 10 minute affair.</p>
<p>The Jitterbit client is where you create the operations that pull data from the source and get it where you want it to go. I created an operation to log in to Salesforce.com and store my session Id. Then you pretty much create one operation for each table in your app, grabbing the data and passing it to Salesforce.com&#8217;s Upsert web service method. This will try to update a record that exists with that External Id. If it can&#8217;t find the record, it will create one. Brilliant.</p>
<p>It&#8217;s a ton of drag-and-drop mapping&#8211;this app field maps to this field on the Salesforce object. But once you get the hang of it, you&#8217;ll crank through all your tables.</p>
<p>When your operations are set up you run them, starting with the login, until all your data comes over. These operations can be scheduled to run as often as makes sense to you. You can pull over only data modified since you last pulled. I created a very simple data flow&#8211;one way from my app to Salesforce.com. But you can really do just about anything. You can send data back to your app. You could pull from 25 different apps all to Salesforce. It&#8217;s up to you.</p>
<p><strong>Step 6: Set up your reports and dashboards</strong></p>
<p>Once your data is in Salesforce.com and related correctly, start creating your reports and dashboards. What I find is that when I create a dashboard the next one immediately comes to mind. Loan default rate over time. But does marital status have an impact on that? What about number of children the borrow has? And what are the patterns for defaults? Do loans tend to default more frequently during the dry season? In the months before harvest? Do non-agriculture loans behave differently? People who work at microfinance institutions ask themselves these questions. They see patterns and want the data to confirm or challenge their assumptions. Because Salesforce.com makes analytics easy, users can ask these questions and get immediate answers. Dashboards can even be sent to them via email every week.</p>
<p>Then step back and take a look at your analytics. Here are some I created with a sample data set in my app:</p>
<ul>
<li><a href="https://www.salesforce.com/secur/frontdoor.jsp?un=readonly@mfi.demo&amp;pw=grameen1&amp;startURL=/01Z30000000r9UQ">Customer Info</a></li>
<li><a href="https://www.salesforce.com/secur/frontdoor.jsp?un=readonly@mfi.demo&amp;pw=grameen1&amp;startURL=/01Z30000000r9jb">Default Info</a></li>
<li><a href="https://www.salesforce.com/secur/frontdoor.jsp?un=readonly@mfi.demo&amp;pw=grameen1&amp;startURL=/01Z30000000r9UG">Delinquency Info</a></li>
<li><a href="https://www.salesforce.com/secur/frontdoor.jsp?un=readonly@mfi.demo&amp;pw=grameen1&amp;startURL=/01Z30000000r9UV">Historic Delinquency Rates</a></li>
<li><a href="https://www.salesforce.com/secur/frontdoor.jsp?un=readonly@mfi.demo&amp;pw=grameen1&amp;startURL=/01Z30000000r9ui">Loan Demographics</a></li>
<li><a href="https://www.salesforce.com/secur/frontdoor.jsp?un=readonly@mfi.demo&amp;pw=grameen1&amp;startURL=/01Z30000000r9ns">Loan Info</a></li>
<li><a href="https://www.salesforce.com/secur/frontdoor.jsp?un=readonly@mfi.demo&amp;pw=grameen1&amp;startURL=/01Z30000000r9oQ">Loan Products</a></li>
</ul>
<p>I was very pleased with the story I am just starting to tell about the loan programs at this pretend microfinance institution. I think the take away is this: Salesforce.com can add value by helping you be creative in telling the story of your data. It&#8217;s a story that might be hard for you to tell right now. But with very little cost other than time and technical ability, you can add flexible, user-driven analytics to any database application by following this recipe.</p>
<p>You can add this power to your app. But the really amazing part is how easy it is to share this kind of setup. Once you create a schema that matches Mifos or OpenMRS or CiviCRM you can package that up in Salesforce and share it with anyone. Just a couple clicks and they can install all the objects, triggers, and dashboards for that app. Jitterbit operations can be shared as well. You export them as a &#8220;jitterpak&#8221; and anyone can use them with their setup. Just change the login information, point to your Salesforce.com web services and it should work without changes.</p>
<p>And perhaps the most important thing is that you&#8217;re not embedding Salesforce.com into your app. You&#8217;re not requiring that everyone using the app use Salesforce.com. I hop this example shows that Salesforce.com can be &#8220;bolted-on&#8221;, adding value along side your mission-critical application. The cost can be very low, the impact can be high, and you can do it without making big decisions about app architecture direction.</p>
]]></content:encoded>
			<wfw:commentRss>http://gokubi.com/archives/salesforce-com-as-free-bolt-on-analytics-engine-for-any-database-app/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Salesforce sending SMS through FrontlineSMS</title>
		<link>http://gokubi.com/archives/salesforce-sending-sms-through-frontlinesms</link>
		<comments>http://gokubi.com/archives/salesforce-sending-sms-through-frontlinesms#comments</comments>
		<pubDate>Thu, 27 May 2010 14:13:07 +0000</pubDate>
		<dc:creator>Steve</dc:creator>
				<category><![CDATA[CRM]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://gokubi.com/?p=1208</guid>
		<description><![CDATA[FrontlineSMS is an open source application used primarily in the developing world for communicating with large numbers of cell phone users. It&#8217;s a locally installed application that pushes SMS messages out through your tethered cell phone. It&#8217;s a really cool app with all sorts of great real-world usage. My friend Dale Zak just built an [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://frontlinesms.com">FrontlineSMS</a> is an open source application used primarily in the developing world for communicating with large numbers of cell phone users. It&#8217;s a locally installed application that pushes SMS messages out through your tethered cell phone. It&#8217;s a really cool app with all sorts of great real-world usage. My friend Dale Zak just built an awesome <a href="http://www.frontlinesms.com/tag/frontlinesms-reminders/">reminders module</a> for it, and that got me looking at it again.</p>
<p>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&#8217;s a short video of it in action:</p>
<p style="text-align: center;"><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="640" height="385" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/au-iS2yD97w&amp;hl=en_US&amp;fs=1&amp;" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="640" height="385" src="http://www.youtube.com/v/au-iS2yD97w&amp;hl=en_US&amp;fs=1&amp;" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p style="text-align: left;">Here&#8217;s what&#8217;s going on in the one-off send:</p>
<ol>
<li>I&#8217;m using Visualforce to create UI that is called by the Send SMS buttons</li>
<li>The Visualforce page accepts the SMS text input</li>
<li>Clicking send shows an iFrame that calls the FrontlineSMS API correctly: http://localhost:&lt;port&gt;/sms_number/sms_message</li>
<li>A Task is related to the Contact with the data and content of the SMS message</li>
</ol>
<p>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.</p>
<p>To all the members of a Campaign, we do the following:</p>
<ol>
<li>The Visualforce page accepts the SMS text input</li>
<li>Clicking Send exposes an Action Poller, which calls code every 5 seconds</li>
<li>When the Action Poller calls code, 10 Campaign Members of the correct Status are queried</li>
<li>10 iFrames are populated with the FrontlineSMS API URLs for the messages</li>
<li>The Campaign Member records have their Status changed so they won&#8217;t be queried again</li>
<li>Tasks are related to the Campaign and the Contact or Lead</li>
</ol>
<p>Here are the Salesforce code bits.</p>
<p>Visualforce page for one-off sends:</p>
<pre>
<pre class="brush: html">
&lt;apex:page standardController=&quot;Contact&quot; extensions=&quot;extSendSMS&quot; &gt;
	&lt;apex:form &gt;
		&lt;apex:sectionHeader title=&quot;FrontlineSMS&quot; subtitle=&quot;Send SMS via FrontlineSMS&quot;&gt;
			&lt;description&gt;
				Send an SMS using FrontlineSMS:
				&lt;ul&gt;
					&lt;li&gt;Install FrontlineSMS on your computer&lt;/li&gt;
					&lt;li&gt;Make sure FrontlineSMS is running&lt;/li&gt;
					&lt;li&gt;Make sure your phone is connected to FrontlineSMS&lt;/li&gt;
				&lt;/ul&gt;

			&lt;/description&gt;

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

			&lt;/apex:pageBlockButtons&gt;
		&lt;/apex:pageBlock&gt;
		&lt;apex:pageBlock id=&quot;smsiframe&quot; title=&quot;&quot;&gt;
		&lt;apex:iframe id=&quot;frontlinesmsIframe&quot; src=&quot;{!endpoint}&quot; scrolling=&quot;no&quot;  rendered=&quot;{!showFrontlineSMSiframe}&quot;/&gt;
		&lt;/apex:pageBlock&gt;
	&lt;/apex:form&gt;
&lt;/apex:page&gt;
</pre>
<p>Apex controller for one-off sends:</p>
<pre>
<pre class="brush: java">
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 = &#039;SMS&#039;;
		this.task.ActivityDate = Date.Today();

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

    	endpoint = &#039;http://localhost:8565/&#039;+ strippedMobilePhone +&#039;/&#039;+ this.task.Description;
    	showFrontlineSMSiframe = true;
    	return null;
    }
}
</pre>
<p>Visualforce page for Campaign sends:</p>
<pre>
<pre class="brush: html">
&lt;apex:page standardController=&quot;Campaign&quot; extensions=&quot;extSendSMSCampaign&quot; &gt;
&lt;apex:form &gt;
		&lt;apex:sectionHeader title=&quot;FrontlineSMS&quot; subtitle=&quot;Send SMS via FrontlineSMS&quot;&gt;
			&lt;description&gt;
				Send an SMS using FrontlineSMS:
				&lt;ul&gt;
					&lt;li&gt;Install FrontlineSMS on your computer&lt;/li&gt;
					&lt;li&gt;Make sure FrontlineSMS is running&lt;/li&gt;
					&lt;li&gt;Make sure your phone is connected to FrontlineSMS&lt;/li&gt;
				&lt;/ul&gt;

			&lt;/description&gt;

		&lt;/apex:sectionHeader&gt;
		&lt;apex:pageMessages id=&quot;pageMessages&quot;/&gt;
		&lt;apex:pageBlock id=&quot;smsblock&quot; title=&quot;&quot;&gt;
			&lt;apex:pageBlockSection id=&quot;headersection&quot; &gt;
				&lt;apex:outputLabel for=&quot;membercount&quot;&gt;Member Count&lt;/apex:outputLabel&gt;&lt;apex:outputText id=&quot;membercount&quot; value=&quot;{!memberCount}&quot;/&gt;
				&lt;apex:outputLabel for=&quot;numbersent&quot;&gt;Number Sent&lt;/apex:outputLabel&gt;&lt;apex:outputText id=&quot;numbersent&quot; value=&quot;{!numberSent}&quot;/&gt;
				&lt;apex:inputField value=&quot;{!dummyTask.Description}&quot;/&gt;

			&lt;/apex:pageBlockSection&gt;
			&lt;apex:pageBlockSection id=&quot;smssection&quot; rendered=&quot;{!showFrontlineSMSiframe}&quot;&gt;
				&lt;apex:outputLabel for=&quot;endpoint1&quot;&gt;SMS 1 (debug)&lt;/apex:outputLabel&gt;&lt;apex:outputText id=&quot;endpoint1&quot; value=&quot;{!endpoint1}&quot;/&gt;
				&lt;apex:outputLabel for=&quot;endpoint2&quot;&gt;SMS 2 (debug)&lt;/apex:outputLabel&gt;&lt;apex:outputText id=&quot;endpoint2&quot; value=&quot;{!endpoint2}&quot;/&gt;
				&lt;apex:outputLabel for=&quot;endpoint3&quot;&gt;SMS 3 (debug)&lt;/apex:outputLabel&gt;&lt;apex:outputText id=&quot;endpoint3&quot; value=&quot;{!endpoint3}&quot;/&gt;
				&lt;apex:outputLabel for=&quot;endpoint4&quot;&gt;SMS 4 (debug)&lt;/apex:outputLabel&gt;&lt;apex:outputText id=&quot;endpoint4&quot; value=&quot;{!endpoint4}&quot;/&gt;
				&lt;apex:outputLabel for=&quot;endpoint5&quot;&gt;SMS 5 (debug)&lt;/apex:outputLabel&gt;&lt;apex:outputText id=&quot;endpoint5&quot; value=&quot;{!endpoint5}&quot;/&gt;
				&lt;apex:outputLabel for=&quot;endpoint6&quot;&gt;SMS 6 (debug)&lt;/apex:outputLabel&gt;&lt;apex:outputText id=&quot;endpoint6&quot; value=&quot;{!endpoint6}&quot;/&gt;
				&lt;apex:outputLabel for=&quot;endpoint7&quot;&gt;SMS 7 (debug)&lt;/apex:outputLabel&gt;&lt;apex:outputText id=&quot;endpoint7&quot; value=&quot;{!endpoint7}&quot;/&gt;
				&lt;apex:outputLabel for=&quot;endpoint8&quot;&gt;SMS 8 (debug)&lt;/apex:outputLabel&gt;&lt;apex:outputText id=&quot;endpoint8&quot; value=&quot;{!endpoint8}&quot;/&gt;
				&lt;apex:outputLabel for=&quot;endpoint9&quot;&gt;SMS 9 (debug)&lt;/apex:outputLabel&gt;&lt;apex:outputText id=&quot;endpoint9&quot; value=&quot;{!endpoint9}&quot;/&gt;
				&lt;apex:outputLabel for=&quot;endpoint10&quot;&gt;SMS 10 (debug)&lt;/apex:outputLabel&gt;&lt;apex:outputText id=&quot;endpoint10&quot; value=&quot;{!endpoint10}&quot;/&gt;
			&lt;/apex:pageBlockSection&gt;
			&lt;apex:pageBlockButtons id=&quot;smsbuttons&quot;&gt;
				&lt;apex:commandButton value=&quot;Send SMS&quot; action=&quot;{!sendSMS}&quot; rerender=&quot;smsblock,smsiframe,actionblock&quot;/&gt;
			&lt;/apex:pageBlockButtons&gt;
		&lt;/apex:pageBlock&gt;
		&lt;apex:pageBlock id=&quot;actionblock&quot; title=&quot;&quot;&gt;
			&lt;apex:actionPoller action=&quot;{!sendSMSToMembers}&quot; rerender=&quot;smsblock,smsiframe,actionblock&quot; interval=&quot;5&quot; rendered=&quot;{!showActionBlock}&quot;/&gt;
		&lt;/apex:pageBlock&gt;
		&lt;apex:pageBlock id=&quot;smsiframe&quot; title=&quot;&quot; &gt;
			&lt;apex:pageBlockSection id=&quot;smsisection&quot; rendered=&quot;{!showFrontlineSMSiframe}&quot;&gt;
				SMS 1 (debug)
				&lt;apex:iframe id=&quot;frontlinesmsIframe1&quot; height=&quot;30&quot; src=&quot;{!endpoint1}&quot; scrolling=&quot;no&quot;  /&gt;
				SMS 2 (debug)
				&lt;apex:iframe id=&quot;frontlinesmsIframe2&quot; height=&quot;30&quot; src=&quot;{!endpoint2}&quot; scrolling=&quot;no&quot;  /&gt;
				SMS 3 (debug)
				&lt;apex:iframe id=&quot;frontlinesmsIframe3&quot; height=&quot;30&quot; src=&quot;{!endpoint3}&quot; scrolling=&quot;no&quot;  /&gt;
				SMS 4 (debug)
				&lt;apex:iframe id=&quot;frontlinesmsIframe4&quot; height=&quot;30&quot; src=&quot;{!endpoint4}&quot; scrolling=&quot;no&quot;  /&gt;
				SMS 5 (debug)
				&lt;apex:iframe id=&quot;frontlinesmsIframe5&quot; height=&quot;30&quot; src=&quot;{!endpoint5}&quot; scrolling=&quot;no&quot;  /&gt;
				SMS 6 (debug)
				&lt;apex:iframe id=&quot;frontlinesmsIframe6&quot; height=&quot;30&quot; src=&quot;{!endpoint6}&quot; scrolling=&quot;no&quot;  /&gt;
				SMS 7 (debug)
				&lt;apex:iframe id=&quot;frontlinesmsIframe7&quot; height=&quot;30&quot; src=&quot;{!endpoint7}&quot; scrolling=&quot;no&quot;  /&gt;
				SMS 8 (debug)
				&lt;apex:iframe id=&quot;frontlinesmsIframe8&quot; height=&quot;30&quot; src=&quot;{!endpoint8}&quot; scrolling=&quot;no&quot;  /&gt;
				SMS 9 (debug)
				&lt;apex:iframe id=&quot;frontlinesmsIframe9&quot; height=&quot;30&quot; src=&quot;{!endpoint9}&quot; scrolling=&quot;no&quot;  /&gt;
				SMS 10 (debug)
				&lt;apex:iframe id=&quot;frontlinesmsIframe10&quot; height=&quot;30&quot; src=&quot;{!endpoint10}&quot; scrolling=&quot;no&quot;  /&gt;
			&lt;/apex:pageBlockSection&gt;
		&lt;/apex:pageBlock&gt;
	&lt;/apex:form&gt;
&lt;/apex:page&gt;
</pre>
<p>Apex controller for campaign sends:</p>
<pre>
<pre class="brush: java">
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&lt;String&gt; endpoints = new List&lt;String&gt;();
	private List&lt;Task&gt; SMSTasks = new List&lt;Task&gt;();
	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=&#039;sent&#039; and (Contact.MobilePhone &lt;&gt; null OR Lead.MobilePhone &lt;&gt; null)];
		showFrontlineSMSiframe = false;
		showActionBlock=false;
		SMSmessage = &#039;test message&#039;;
		numberSent =0;
		dummyTask = new Task();
    }

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

     public PageReference sendSMSToMembers(){
     	endpoints = new List&lt;String&gt;();
     	endpoint1 = &#039;&#039;;
		endpoint2 = &#039;&#039;;
		endpoint3 = &#039;&#039;;
		endpoint4 = &#039;&#039;;
		endpoint5 = &#039;&#039;;
		endpoint6 = &#039;&#039;;
		endpoint7 = &#039;&#039;;
		endpoint8 = &#039;&#039;;
		endpoint9 = &#039;&#039;;
		endpoint10 = &#039;&#039;;
		SMSTasks = new List&lt;Task&gt;();
    	List&lt;CampaignMember&gt; myCampaignMembers = new List&lt;CampaignMember&gt;();
    	myCampaignMembers = [select id, Contact.MobilePhone,Contact.Id,Lead.Id, Lead.MobilePhone, status from CampaignMember where CampaignId=:this.campaign.id AND status=&#039;sent&#039; and (Contact.MobilePhone &lt;&gt; null OR Lead.MobilePhone &lt;&gt; null) limit 10];
		if(myCampaignMembers.size()&gt;0){
	    	for(Integer i=0;i&lt;myCampaignMembers.size();i++){
	    		CampaignMember thisMember = myCampaignMembers[i];
		    	String strippedMobilePhone = &#039;&#039;;
		    	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(&#039;[^0-9]&#039;, &#039;&#039;);

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

		    	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=&#039;Responded&#039;;
	    	}
	    	update myCampaignMembers;
	    	insert SMSTasks;
		} else {
			showActionBlock = false;
		}
		return null;
    }

}
</pre>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://gokubi.com/archives/salesforce-sending-sms-through-frontlinesms/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Strategic impact and making multi-level relationships look nice</title>
		<link>http://gokubi.com/archives/strategic-impact-and-making-multi-level-relationships-look-nice</link>
		<comments>http://gokubi.com/archives/strategic-impact-and-making-multi-level-relationships-look-nice#comments</comments>
		<pubDate>Wed, 05 May 2010 21:03:22 +0000</pubDate>
		<dc:creator>Steve</dc:creator>
				<category><![CDATA[CRM]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://gokubi.com/?p=1138</guid>
		<description><![CDATA[I&#8217;ve been doing a little prototyping in Salesforce.com as a volunteer project for a nonprofit in town. They&#8217;ve got an interesting need to track their strategic plans in Salesforce. I&#8217;ve seen this kind of thing built many times and have built it myself for another org or two. For this org they&#8217;ve got 3 levels [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://gokubi.com/wp-content/uploads/2010/05/drivermetric.png" alt="" title="drivermetric" width="143" height="350" class="alignright size-full wp-image-1137" />I&#8217;ve been doing a little prototyping in Salesforce.com as a volunteer project for a nonprofit in town. They&#8217;ve got an interesting need to track their strategic plans in Salesforce. I&#8217;ve seen this kind of thing built many times and have built it myself for another org or two. For this org they&#8217;ve got 3 levels of custom objects to hold their data. Driver is their high-level strategic focus. Goal is the next level down&#8211;there can be many goals for each Driver. And Metrics our measurable things related to each Goal.</p>
<p>Salesforce lets you create these objects and relationships between them very easily. I created the schema in just a few minutes. I created three objects and related them together&#8211;in this case I made them master-detail relationships. You can see in the diagram what my schema ended up looking like.</p>
<p>Now tracking your strategic goals is great, and most nonprofits do that. But where I think it gets more interesting is when we can tie the actual work we do directly to the metrics we&#8217;re trying to deliver on. So you&#8217;ll see in the diagram that there is a 4th object called project. Project is used to track all the work efforts of the org that are worth recording. I created a lookup relationship from Project to Metric so every project this groups takes on can be explicitly related to a Metric we need to deliver on. Which is in turn related to a Goal, which is related to a Driver. So, why are you updating that website for the 100th time this year? Because the website Project is meant to deliver on the Metric of increasing engagement of our supporters 25%, which is part of a Goal to grow the direct impact our supporters have in the world, which in turn feeds our Driver which calls for us to lead the world with a model of catalytic impact.</p>
<p>Those strategic objectives are made up for this example, but you get the idea how daily work can roll up to why your organization exists. Seeing that direct relationship can be very powerful. So when you look at the Project record in Salesforce, what do you see? Because there is only a relationship to the Metric object, you only see the Metric on the page layout.</p>
<p><img src="http://gokubi.com/wp-content/uploads/2010/05/project_metrics.png" alt="" title="project_metrics" width="635" height="327" class="aligncenter size-full wp-image-1145" /></p>
<p>Because the Project isn&#8217;t directly related to Goal or Driver, we can&#8217;t have lookup fields to those objects. Lookups are only for direct relationships, from child to parent, not up to grandparent and beyond. So we need another solution, and it turns out to be formula fields. On the Project I can create a Goal field and a Driver field that are formula fields of type Text. Here&#8217;s the formula for each of them:</p>
<pre>
for Goal: HYPERLINK("/" + Metric__r.Goal__r.Id , Metric__r.Goal__r.Name,"_self" )
for Driver: HYPERLINK( "/" + Metric__r.Goal__r.Driver__r.Id , Metric__r.Goal__r.Driver__r.Name,"_self" )
</pre>
<p>We&#8217;re creating a hyperlink to fake a real lookup. The URL of the hyperlink is relative, so it starts with / rather than http://. It then has the Id of the object, which we get by traversing up the relationships in Salesforce. From project we follow the Metric__r relationship to the Metric object, then up the Goal__r relationship to the Goal where we can grab any fields we want. With Driver we do the same thing, just go up one more level. If you think this looks hard, don&#8217;t worry, the formula field editor is really easy to use and does all the hard work for you&#8211;you just click the relationship you want to traverse.</p>
<p>The text of the link is the Name of the object in question. And then we target the hyperlink to &#8220;_self&#8221; so that it will load the record in question into the current window&#8211;just like a regular lookup would.</p>
<p>After adding these formula fields to the page layout, the end result is this:</p>
<p><img src="http://gokubi.com/wp-content/uploads/2010/05/project_drivers.png" alt="" title="project_drivers" width="635" height="368" class="aligncenter size-full wp-image-1146" /></p>
<p>Now we&#8217;re telling the full story about this project with a little help from simple formula fields.</p>
]]></content:encoded>
			<wfw:commentRss>http://gokubi.com/archives/strategic-impact-and-making-multi-level-relationships-look-nice/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Medical Records for Gitwe</title>
		<link>http://gokubi.com/archives/medical-records-for-gitwe</link>
		<comments>http://gokubi.com/archives/medical-records-for-gitwe#comments</comments>
		<pubDate>Thu, 18 Mar 2010 20:37:30 +0000</pubDate>
		<dc:creator>Steve</dc:creator>
				<category><![CDATA[Africa]]></category>
		<category><![CDATA[CRM]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://gokubi.com/?p=1120</guid>
		<description><![CDATA[My trip to Rwanda was amazing. This first post is going to be about the volunteer project I was a part of. I&#8217;ll write more later about Rwanda and the people. There is so much to say&#8230; First of all, our project team fully delivered on all we set out to do. While success was [...]]]></description>
			<content:encoded><![CDATA[<p>My trip to Rwanda was amazing. This first post is going to be about the volunteer project I was a part of. I&#8217;ll write more later about Rwanda and the people. There is so much to say&#8230;</p>
<p style="text-align: center;"><img class="centered aligncenter" title="Gitwe hills" src="http://farm3.static.flickr.com/2791/4415239372_3f883638bb.jpg" alt="Gitwe hills" width="500" height="333" /></p>
<p>First of all, our project team fully delivered on all we set out to do. While success was in doubt up until the last minute (why do I work in technology?) we pulled it out at the 11th hour. Literally&#8211;11pm the night before we left. I look at what we did and we really nailed every aspect. The hardware we brought is high-quality and well configured. We got all the server software running in the configuration required. The electrical infrastructure is sound and protects the hardware. And we documented the systems very well.</p>
<p>It was really interesting going to Rwanda, such an amazingly different place from where I live, and then spending a lot of time working with technology. Every day felt like two days. There was the experiential part&#8211;meeting people, taking in the bustle and beauty, and eating amazing food. And then there was the volunteer work. Every day we were focused on moving toward project success. It made for a trip that went by quickly, but at the same time seemed to be a month long.</p>
<p>And I wouldn&#8217;t do it differently. I highly recommend going to Rwanda and Kenya. And I really recommend going there with good work to do. Volunteer. Help out. Contribute to the country while you experience it. You&#8217;ll get more out of your time there. You will spend more time with Rwandans and Kenyans. You will connect deeper with the place and the people then you would as a tourist. When I go back to Rwanda, I will be going with some good work to do. Maybe technology related, maybe not. But this kind of travel really resonated with me.</p>
<p>Here&#8217;s a quick recap of the project and why I was there:</p>
<p style="text-align: center;"><img class="centered aligncenter" src="http://farm5.static.flickr.com/4049/4414451983_f3938e2733.jpg" alt="" width="500" height="333" /></p>
<p><a href="http://www.mmissions.org/">Medical Missions for Children</a> sends doctors to the hospital in a town called Gitwe a couple times a year. These doctors volunteer their time and perform surgeries on kids and adults. Things like repairing cleft pallets and resectioning goiters. Surgeries that are common and easy in more wealthy countries, but just don&#8217;t happen in poorer places. These cosmetic surgeries can be absolutely life changing&#8211;much like the <a href="http://dental.pacific.edu/News_and_Events/News_Archive/Dental_Hope_for_Homeless.html">impact of homeless people getting free dentures in San Francisco</a>. It turns out that these kinds of procedures can change people&#8217;s lives&#8211;giving them confidence they&#8217;ve lacked for years because of their limitations. The freedom to smile, and not feel self-conscious, re-connects people with society and their families in profound ways. These cosmetic procedures can be truly life-changing.</p>
<p>Lucky Gunasekara was the project lead and he assembled the team&#8211;me, Jordan Smock, and the incomparable Canadian, Dale Zak. I didn&#8217;t know any of these guys going into the trip, and I came away with real friends I know I&#8217;ll stay in touch with.</p>
<p style="text-align: center;"><img class="centered aligncenter" src="http://farm3.static.flickr.com/2737/4415121734_d80352c000.jpg" alt="" width="500" height="333" /></p>
<p>Our task on this trip was to install a computerized medical records system that could be used by Medical Missions for Children to record the patients they worked with. When you&#8217;re performing surgery, it&#8217;s really important to know the patient&#8217;s medical history so that you don&#8217;t inadvertently harm them with drugs they are alergic to, for example. Phase one of this project was to get the system in place and have MMFC use it after we left. Phase two is for Gitwe Hospital to use the medical records system more broadly&#8211;allowing for better care for the 300,000 people who turn to the hospital.</p>
<p style="text-align: center;"><img class="centered aligncenter" src="http://farm5.static.flickr.com/4059/4415193972_b9130b88a6.jpg" alt="" width="500" height="333" /></p>
<p>Gitwe Hospital is owned and operated by the wonderful Urayeneza Gerard. He was our gracious host during our time there. Everything we requested to get the project done&#8211;server room with a padlock, upgrade to the electrical system, curtains for the windows&#8211;was gotten on short order.</p>
<p style="text-align: center;"><img class="centered aligncenter" src="http://farm3.static.flickr.com/2777/4415191178_09d1a376b0.jpg" alt="" width="500" height="333" /></p>
<p>Because of limited prep time for the trip, we purchased the hardware in the US and carried it on the plane with us. 2 Netbooks, 2 small desktops, a server, a backup power supply (UPS) and a million cables, surge protectors and other miscellany. Surprisingly, everything made it to our destination safely, and worked when we unpacked it. We did the unpacking in Kigali, where we were more confident we would have reliable power and Internet access. One of our hotel rooms turned into a makeshift server room as we built up the hardware, the network, and the software.</p>
<p style="text-align: center;"><img class="centered aligncenter" src="http://farm3.static.flickr.com/2678/4414326585_00ec8c5219.jpg" alt="" width="500" height="333" /></p>
<p>The biggest issue we ran into while unpacking things was power. Rwandan power turned out to be reliable and clean, but it comes out of the wall at ~250V. The power supplies in all of our devices were rated to 240V, save the most important one, the UPS. Luckily that required an awesome day of walking around the commercial district searching for a solution. We found one with the amazing folks at Bricotech. They sold us a voltage regulator which would serve the dual purpose of stepping the voltage down to 110V and protecting our equipment against power fluctuations. They also put a British plug end on our UPS, solving our other issue.</p>
<p style="text-align: center;"><img class="centered aligncenter" src="http://farm3.static.flickr.com/2706/4415127582_1e460e6404.jpg" alt="" width="500" height="333" /></p>
<p>We were now ready to leave the big city for the rural outpost of Gitwe. We packed all our gear and made the 2+ hour drive south to the hospital, stopping along the way for a Mutzig in the van.</p>
<p style="text-align: center;"><img class="centered aligncenter" src="http://farm5.static.flickr.com/4058/4414409061_fe0b93f530.jpg" alt="" width="500" height="333" /></p>
<p>Gitwe is an amazing little spot. It&#8217;s not really a town, more of a collection of shops and homes. The main centers of activity are the Hospital and Esapac, a technical school with about 1000 students. So while it was a tiny little place, it bustled with activity of people coming and going.</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="480" height="385" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/kLDzOtsrGrU&amp;hl=en_US&amp;fs=1&amp;" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="480" height="385" src="http://www.youtube.com/v/kLDzOtsrGrU&amp;hl=en_US&amp;fs=1&amp;" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p>Over the course of five days we took an empty room at the hospital and transformed it into the Gitwe Hospital server room. Here are the before and after pics:</p>
<p style="text-align: center;"><img class="centered aligncenter" src="http://farm3.static.flickr.com/2743/4414431679_582595fbd4.jpg" alt="" width="500" height="333" /></p>
<p style="text-align: center;"><img class="centered aligncenter" src="http://farm5.static.flickr.com/4030/4415324990_af9a0aa1d6.jpg" alt="" width="500" height="333" /></p>
<p>We worked side by side with the IT staff at the hospital&#8211;Innocent and Charles, setting up furniture, hardware and software. The technology we brought in is somewhat new to these folks. They haven&#8217;t used the medical records system before, and they need to come up to speed on Linux systems. We&#8217;re hoping to get them involved in an intensive nine-month training course offered by the Rwandan government to train folks to work with medical records systems. It&#8217;s a plumb placement, and a great opportunity if it comes through.</p>
<p style="text-align: center;"><img class="centered aligncenter" src="http://farm5.static.flickr.com/4058/4414439097_44d019ff92.jpg" alt="" width="500" height="333" /></p>
<p>OpenMRS was our steepest learning curve in this project. We ended up running up against a problem with a bad install file posted to their website. Things just weren&#8217;t working for the first 3 days of our stay. We tried every angle we could think of, and then used our cellphone modem Internet access to throw a hail mary out to Dave Thomas, who works at the other end of Rwanda for <a href="http://www.pih.org">Partners in Health</a>. He saved our bacon. Completely. With his help, we were able to get the system functioning on our last day, and then spent the rest of the day and night configuring it for the special needs of MMFC. At midnight we headed back to our rooms for a beer, a bit of whiskey and Coke, with a palpable sense of relief and accomplishment.</p>
<p>The next morning we had breakfast with Gerard and his wife Justine, like we did each day in Gitwe. After breakfast Gerard said a prayer for our travels and really got us choked up as we realized how important this contribution is to him. He&#8217;s doing amazing things for Gitwe with the school and the hospital. He is by far the largest employer in the area, with the hospital alone having 1,500 employees. And he has no plans to slow down&#8211;Gitwe will likely be the home of the second medical school in Rwanda. I can&#8217;t wait for that to happen!</p>
<p style="text-align: center;"><img class="centered aligncenter" src="http://farm5.static.flickr.com/4060/4414563569_3b7b7367e3.jpg" alt="" width="500" height="333" /></p>
<p>Now that our work was done, we could enjoy a little tourism. But, Lucky, Dale and I aren&#8217;t your average tourists. What we like to see are real people, and amazing good work. So, naturally, we headed up to Rwinkwavu to get a tour of the Partners in Health operation. It was the most amazing download of information I&#8217;ve had in a while. Hearing about the innovative things they are doing to ensure the health of the poor in northeastern Rwanda was nothing short of amazing and inspiring. <a href="http://www.pih.org">PIH</a> is my favorite nonprofit, and I highly recommend reading <a href="http://books.google.com/books?id=XifdzgU9ilsC&amp;source=gbs_navlinks_s">Mountains Beyond Mountain</a>s if you want to learn more about how comprehensive healthcare can be provided to people with no money. Thanks to our hosts Cheryl and Dave, we had plenty to think about on the long drive to Kigali.</p>
<p style="text-align: center;"><img class="centered aligncenter" src="http://farm5.static.flickr.com/4010/4414606971_a11cbf13a8.jpg" alt="" width="500" height="333" /></p>
<p>I can&#8217;t say enough about the experience of working side-by-side with great folks in an amazing setting. It&#8217;s a great feeling to visit a place and leave behind something of value that can be built on. I hope to go back and contribute some of that building. I&#8217;d love to have more time to focus on knowledge transfer and skill building with Charles and Innocent and whomever is interested. This trip felt like a first step into a longer relationship. I hope that&#8217;s the case and that Gitwe moves forward as fast as we all want. There are governmental plans to bring fiber optic Internet to all the district hospitals in the next year or so. Gitwe could be transformed in short order if that&#8217;s the case. I&#8217;d love to witness it first hand!</p>
]]></content:encoded>
			<wfw:commentRss>http://gokubi.com/archives/medical-records-for-gitwe/feed</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>An Introduction to Exception Handling in Apex</title>
		<link>http://gokubi.com/archives/an-introduction-to-exception-handling-in-apex</link>
		<comments>http://gokubi.com/archives/an-introduction-to-exception-handling-in-apex#comments</comments>
		<pubDate>Tue, 12 Jan 2010 17:48:41 +0000</pubDate>
		<dc:creator>Steve</dc:creator>
				<category><![CDATA[CRM]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://gokubi.com/?p=1095</guid>
		<description><![CDATA[I was privileged to get to write an article for DeveloperForce.com on exception handling in Apex. I wrote this article not from the perspective of an expert on the subject, but as someone who really wanted to learn more about my options for using exception handling in my Apex code. I&#8217;ve always had a nagging [...]]]></description>
			<content:encoded><![CDATA[<p>I was privileged to get to write an article for DeveloperForce.com on <a href="http://wiki.developerforce.com/index.php/An_Introduction_to_Exception_Handling">exception handling in Apex</a>. I wrote this article not from the perspective of an expert on the subject, but as someone who really wanted to learn more about my options for using exception handling in my Apex code.</p>
<blockquote><p>I&#8217;ve always had a nagging feeling that I wasn&#8217;t following best practices in my exception handling in Apex. In most cases I wasn&#8217;t doing any exception handling at all. I decided to write up what I have learned about error handling on the Force.com platform so that others could jump into it full-bore, and make their programs more robust and fault tolerant.</p></blockquote>
<p>I hope it&#8217;s helpful to Apex programmers out there. Let me know what you think!</p>
]]></content:encoded>
			<wfw:commentRss>http://gokubi.com/archives/an-introduction-to-exception-handling-in-apex/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>I&#8217;m going to Rwanda</title>
		<link>http://gokubi.com/archives/im-going-to-rwanda</link>
		<comments>http://gokubi.com/archives/im-going-to-rwanda#comments</comments>
		<pubDate>Wed, 30 Dec 2009 04:50:56 +0000</pubDate>
		<dc:creator>Steve</dc:creator>
				<category><![CDATA[Africa]]></category>
		<category><![CDATA[CRM]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://gokubi.com/?p=1082</guid>
		<description><![CDATA[Today I got confirmation of some great news&#8211;in February I&#8217;ll be going to Rwanda for a week to help install an open source medical records system at a rural health clinic! I&#8217;ll be going with Lucky Gunasekara and meeting with Partners in Health. I&#8217;m really excited to work with Lucky! This project will be amazing, [...]]]></description>
			<content:encoded><![CDATA[<p>Today I got confirmation of some great news&#8211;in February I&#8217;ll be going to Rwanda for a week to help install an open source medical records system at a rural health clinic! I&#8217;ll be going with <a href="http://www.youtube.com/watch?v=Lm5Ep54IP9A">Lucky Gunasekara</a> and meeting with <a href="http://www.pih.org">Partners in Health</a>.</p>
<p>I&#8217;m really excited to work with Lucky! This project will be amazing, and Lucky has a longer-term vision. He wants to answer the question: does cloud computing have a role in health systems in rural Africa? He&#8217;s planning some really interesting efforts in Kenya later this year. In time, I think Salesforce.com can be a boon in rural Africa, and I&#8217;m really interested to start the work to figure out if that time is now, soon, or farther down the road. I see this trip as a great first step on that path.</p>
<p>When I started working with Salesforce.com five years ago, I felt part of my role was to go down the path of trying to deeply customize the platform for nonprofits, and then to report on that experience, warts and all. I really enjoyed that work, and I hope to do similar work and storytelling around Salesforce.com in parts of the world where people assume the cloud can&#8217;t work.</p>
<p>But most of all, I&#8217;m really excited to have all my assumptions and expectations blown out of the water! I can&#8217;t wait to walk the roads, meet folks, and most of all listen and learn. I will be arriving with no answers, and I don&#8217;t expect to leave with any. For me, it&#8217;s all about getting to the right questions. There will be so much to take in, to start orienting myself to this new set of challenges.</p>
<p>Only just a week ago a friend pointed me to this piece by David Brancaccio on Partners In Health&#8217;s work in Rwanda. I was floored by the outcomes they are getting, and the thoughtful design of the community health program. I recommend watching it&#8211;it&#8217;s amazing.</p>
<p align=center><embed src="http://www.pbs.org/now/media_player/flvplayer1.swf"; width="635" height="380" bgcolor="000000" allowfullscreen="true" allowscriptaccess="always" flashvars="file=http://www-tc.pbs.org/now/video/NOW-537-stream.mp4&#038;plugins=embed-1&#038;image=http://www-tc.pbs.org/now/shows/537/images/video-512.jpg"></embed></p>
<p>So many times people look to technology as a savior. That never works in my estimation&#8211;human systems are how problems are solved. Technology can augment and transform those solutions, but it isn&#8217;t the answer. I&#8217;m incredibly excited by what&#8217;s already working in Rwanda, and am fired up to start thinking about how technology can help extend that impact.</p>
<p>I watched this video and emailed the link to a friend of mine with the message, &#8220;this is the work I want to do with my life&#8211;helping people build community-based systems that really work.&#8221; Four days later I got an email from Lucky inviting me to Rwanda, to the very region where that video was filmed. Serendipity is a fabulous thing!</p>
<p>Ahh! I&#8217;m going to Rwanda! I still can&#8217;t really believe it. It all feels so fast and so amazingly exciting. Of course I&#8217;ll be writing about the experience, as well as taking tons of pictures and videos. So much to do to prepare!</p>
]]></content:encoded>
			<wfw:commentRss>http://gokubi.com/archives/im-going-to-rwanda/feed</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>A great job opportunity</title>
		<link>http://gokubi.com/archives/a-great-job-opportunity</link>
		<comments>http://gokubi.com/archives/a-great-job-opportunity#comments</comments>
		<pubDate>Fri, 11 Dec 2009 15:37:56 +0000</pubDate>
		<dc:creator>Steve</dc:creator>
				<category><![CDATA[CRM]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://gokubi.com/?p=1061</guid>
		<description><![CDATA[Groundwire is an organization near and dear to my heart. I spent many wonderful years there back when it went by the name ONE/Northwest. My Salesforce-related career got it&#8217;s start there. Some of my most cherished relationships began there. And I was given the space and encouragement to grow exponentially as a person, technologist, and [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://groundwire.org">Groundwire</a> is an organization near and dear to my heart. I spent many wonderful years there back when it went by the name ONE/Northwest. My Salesforce-related career got it&#8217;s start there. Some of my most cherished relationships began there. And I was given the space and encouragement to grow exponentially as a person, technologist, and agent of change. I look back on those years very fondly&#8211;the work, the nonprofits we worked with, but most importantly the people.</p>
<p><a href="http://groundwire.org/about/jobs/CRM-Consultant">Groundwire is looking for a top-notch CRM Consultant</a>. Opportunities to join this amazing team are rare. If you are at all interested in using your Salesforce.com skills to make a difference in this world, you should follow up with Groundwire and make sure you don&#8217;t miss this chance.</p>
]]></content:encoded>
			<wfw:commentRss>http://gokubi.com/archives/a-great-job-opportunity/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Add a Donor Status Funnel to the Nonprofit Starter Pack</title>
		<link>http://gokubi.com/archives/add-a-donor-status-funnel-to-the-nonprofit-starter-pack</link>
		<comments>http://gokubi.com/archives/add-a-donor-status-funnel-to-the-nonprofit-starter-pack#comments</comments>
		<pubDate>Wed, 09 Dec 2009 16:18:42 +0000</pubDate>
		<dc:creator>Steve</dc:creator>
				<category><![CDATA[CRM]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://gokubi.com/?p=1027</guid>
		<description><![CDATA[The vision of the Nonprofit Starter Pack has always been that it would be a starting place for nonprofits. It&#8217;s not a &#8220;application&#8221; so much as it&#8217;s a &#8220;platform.&#8221; We&#8217;ve tried to architect it in a way that gives some immediate benefit, while not precluding the change and extension that I feel is necessary for [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://gokubi.com/wp-content/uploads/2009/12/donorstatusfunnel.png" alt="donorstatusfunnel" title="donorstatusfunnel" width="400" height="300" class="alignright"/>The vision of the Nonprofit Starter Pack has always been that it would be a starting place for nonprofits. It&#8217;s not a &#8220;application&#8221; so much as it&#8217;s a &#8220;platform.&#8221; We&#8217;ve tried to architect it in a way that gives some immediate benefit, while not precluding the change and extension that I feel is necessary for successful use. In that spirit, I want to show how to add some donor segmentation you might find interesting if you&#8217;re using the One-to-one Account model.</p>
<p>The image on the right is from a Dashboard showing all Contacts in an instance with the Nonprofit Starter Pack installed. All Contacts have been categorized by something called &#8220;Donor Status.&#8221; We&#8217;re looking at their giving and putting them in one of four categories&#8211;never a donor, a donor previous to last year, a donor last year, or a donor this year. Here&#8217;s how I created it.</p>
<p>First, we need to have 2 rollup summary fields on Account that calculate the Opportunity values over this year and last year. You could also add a third rollup to get the value from two years back:</p>
<ul>
<li>Total Giving Year To Date &#8211; (Won EQUALS true) AND (Close Date GREATER THAN 12/31/2008) AND (Close Date LESS THAN 1/1/2010)</li>
<li>Total Giving Last Year &#8211; (Won EQUALS true) AND (Close Date GREATER THAN 12/31/2007) AND (Close Date LESS THAN 1/1/2009)</li>
<li>Total Giving 2 Years Previous &#8211; (Won EQUALS true) AND (Close Date GREATER THAN 12/31/2006) AND (Close Date LESS THAN 1/1/2008)</li>
</ul>
<p>These dates need to be hard-coded for the rollup summaries to work. On Jan 1, you&#8217;ll need to add a year to all the dates. Don&#8217;t worry, all the rollups will then recalculate for you.</p>
<p>Now you want to show these Account fields on the Contact. Create three formulas that just show the Account field value:</p>
<ul>
<li>YTD Giving Total &#8211; Account.Total_Giving_Year_To_Date__c</li>
<li>Last Year Giving Total &#8211; Account.Total_Giving_Last_Year__c</li>
<li>2 Years Previous Giving Total &#8211; Account.Total_Giving_2_Years_Previous__c</li>
</ul>
<p>Once you have these summary values, you can use them in another Formula, our Donor Status formula:</p>
<p><code>IF( npe01__Lifetime_Giving_History_Amount__c >0, IF( YTD_Giving_Total__c =0, IF( Previous_Year_Giving_Total__c =0, "2. SYBUNT" , "3. LYBUNT") , "4. Current Donor") , "1. Not Donor")</code></p>
<p>Note that the npe01__Lifetime_Giving_History_Amount__c field is included in the Nonprofit Starter Pack. The others are fields you just created, so make sure the names match.</p>
<p>Now that you&#8217;ve got the formula field built, create a Contact and Organizations report that takes all Contacts and groups by Donor Status. Variations on this report can easily be used to send a letter to everyone who gave in the past but not this year. Once you save this report, you can build a funnel dashboard based on it and you&#8217;re done! You&#8217;ve now segmented your donors based on when they last gave.</p>
]]></content:encoded>
			<wfw:commentRss>http://gokubi.com/archives/add-a-donor-status-funnel-to-the-nonprofit-starter-pack/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>My Nonprofit Starter Pack Session from Dreamforce 2009</title>
		<link>http://gokubi.com/archives/my-nonprofit-starter-pack-session-from-dreamforce-2009</link>
		<comments>http://gokubi.com/archives/my-nonprofit-starter-pack-session-from-dreamforce-2009#comments</comments>
		<pubDate>Sun, 29 Nov 2009 01:10:27 +0000</pubDate>
		<dc:creator>Steve</dc:creator>
				<category><![CDATA[CRM]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://gokubi.com/?p=989</guid>
		<description><![CDATA[Getting the Most of of the Nonprofit Starter Pack. We cover 3 main things: Configuring the Starter Pack Importing Contacts via the wizard Upgrading a package Feel free to fast forward through the first 4 minutes, where we&#8217;re getting 110 people logged into their orgs&#8230;]]></description>
			<content:encoded><![CDATA[<p>Getting the Most of of the Nonprofit Starter Pack. We cover 3 main things:</p>
<ol>
<li>Configuring the Starter Pack</li>
<li>Importing Contacts via the wizard</li>
<li>Upgrading a package</li>
</ol>
<p>Feel free to fast forward through the first 4 minutes, where we&#8217;re getting 110 people logged into their orgs&#8230;</p>
<p align="center"><object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/nOY7IHp38WA&#038;rel=0&#038;color1=0xb1b1b1&#038;color2=0xcfcfcf&#038;hl=en_US&#038;feature=player_profilepage&#038;fs=1&#038;hd=1"></param><param name="allowFullScreen" value="true"></param><param name="allowScriptAccess" value="always"></param><embed src="http://www.youtube.com/v/nOY7IHp38WA&#038;rel=0&#038;color1=0xb1b1b1&#038;color2=0xcfcfcf&#038;hl=en_US&#038;feature=player_profilepage&#038;fs=1&#038;hd=1" type="application/x-shockwave-flash" allowfullscreen="true" allowScriptAccess="always" width="425" height="344"></embed></object></p>
]]></content:encoded>
			<wfw:commentRss>http://gokubi.com/archives/my-nonprofit-starter-pack-session-from-dreamforce-2009/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Inflection Point</title>
		<link>http://gokubi.com/archives/inflection-point</link>
		<comments>http://gokubi.com/archives/inflection-point#comments</comments>
		<pubDate>Sun, 22 Nov 2009 20:21:34 +0000</pubDate>
		<dc:creator>Steve</dc:creator>
				<category><![CDATA[Africa]]></category>
		<category><![CDATA[CRM]]></category>
		<category><![CDATA[Change]]></category>
		<category><![CDATA[Civics]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://gokubi.com/?p=968</guid>
		<description><![CDATA[Dreamforce is a 4 day software user conference. It&#8217;s easy to look at it from afar and see only that&#8211;money to be made, customers to retain, sales to be won. And it&#8217;s absolutely true that Dreamforce is about all of that. Customers want value. Salesforce.com wants to give it to them. Partners want to get [...]]]></description>
			<content:encoded><![CDATA[<p>Dreamforce is a 4 day software user conference. It&#8217;s easy to look at it from afar and see only that&#8211;money to be made, customers to retain, sales to be won. And it&#8217;s absolutely true that Dreamforce is about all of that. Customers want value. Salesforce.com wants to give it to them. Partners want to get the word out about their solutions. All of that is true.</p>
<p>But I left Dreamforce feeling transformed&#8211;personally and professionally. I didn&#8217;t expect it. And I&#8217;m not exactly sure what to do with it.</p>
<p>I realized last week the reason I took my current job. People ask me all the time. It was clearly a good fit for my skills. The people are great. And it&#8217;s a lot of fun. But I realized the real reason last week.</p>
<p><strong>I took this job so that I can help create better lives for millions of people</strong>.</p>
<p>Not tens, not hundreds, but millions. Even hundreds of millions. That&#8217;s why I&#8217;m on this earth. That&#8217;s what I think I can contribute to our shared existence. And I think I can do that in my job.</p>
<p>What brought about this clarity? I met some amazing people. I met one woman who has an amazingly clarity of her purpose in life, and that purpose is to help Africa transform itself. To break the colonial, top-down models and return to self-organized, systemic change, completely in partnership with Africans. No more $1M schools dropped from the sky. Holistic, collaborative, community-based change. Generational change. She burns with this goal, and nothing will stand in her way.</p>
<p>Her passion lit a fire in me. And caused me to ask questions of myself. How does my bug fixing contribute to the reemergence of Africa? Am I making the kind of difference I need to make? What can I do differently?</p>
<p>I met a man who has a full-time job and volunteers with a group working in the Sudan. They are building a school from compressed soil, hand dug, hand compressed, and it will be hand built. I had talked to him about his Salesforce needs before I realized he was a volunteer. I thought he was an employee. But it turns out his organization only has one employee. He quietly does this work in his spare time, because he can&#8217;t possibly not do it. That wouldn&#8217;t make sense to him. And his work is infectious. He asked me, &#8220;do you want to go to the Sudan?&#8221; Hell yes I want to go to the Sudan.</p>
<p>I met a woman who is taking a holistic approach to education in Tanzania. Children found abandoned in garbage heaps find a home with her organization. They learn and grow because of her work. Their lives are forever changed. She does this work which has nothing to do with her professional history. She was called to it. And is phenomenal.</p>
<p>These people showed me their fire, their passion, the absolute unacceptability of doing nothing. The necessity of action today. The absolute need for impatience and patience all at once. It bowled me over. I&#8217;m reeling from it still.</p>
<p>It has caused me to look at my work differently. I will continue to do the best work I possibly can with the responsibilities I have. And I feel compelled to start initiatives of my own. The first thoughts I&#8217;ve had is to make Salesforce.com the absolute standard for use in Africa. Here&#8217;s what I think needs to be done to show that Salesforce.com can excel for nonprofits in Africa:</p>
<h2>Nonprofit Starter Pack</h2>
<p>I need to make this code base the best it can be. I want no software bugs standing in the way of this work. It&#8217;s the easiest place for me to make an impact on Monday, and I will.</p>
<h2>Language Support</h2>
<p>Salesforce is translatable into many languages. At this writing, that list includes 20 languages. English, French and Spanish are official languages in many African countries, and those languages are supported currently. I want Salesforce.com to be available in the next tier of African languages: <a href="http://en.wikipedia.org/wiki/Arabic_language">Arabic</a>, <a href="http://en.wikipedia.org/wiki/Swahili_language">Swahili</a>, <a href="http://en.wikipedia.org/wiki/Portuguese_language">Portuguese</a> (Brazilian Portuguese is currently available, and maybe that&#8217;s close enough?), and perhaps <a href="http://en.wikipedia.org/wiki/Afrikaans">Afrikaans</a>. There are over 2000 languages spoken in Africa, so we may never get to support <a href="http://en.wikipedia.org/wiki/Berber_languages">Berber</a>, <a href="http://en.wikipedia.org/wiki/Hausa_language">Hausa</a>, <a href="http://en.wikipedia.org/wiki/Xhosa_language">Xhosa</a>, and the countless others.</p>
<p>Are these the right languages? I&#8217;m new to this, but Arabic, Swahili, and Portuguese seem like the place to start. Arabic is a right to left language, so Salesforce.com first must be able to be switched to that mode, but once that&#8217;s possible, I want Arabic the next day.</p>
<ul>
<li><a href="http://ideas.salesforce.com/article/show/10093498">Vote for Arabic support</a> on the Idea Exchange.</li>
<li><a href="http://ideas.salesforce.com/article/show/10098690">Vote for Swahili support</a> on the Idea Exchange.</li>
</ul>
<p>And as soon as Salesforce.com supports a language, we&#8217;ll start the work to translate the Nonprofit Starter Pack to that language.</p>
<h2>Offline Support</h2>
<p>As you know, much of Africa lacks reliable Internet access. Much of Africa lacks clean water, electricity, and other services much more basic than that. So to support Africans working to transform their countries, we need rock-solid offline tools. Salesforce.com has had offline support in Internet Explorer for years, and many African nonprofits are currently using that to successfully support their work.</p>
<p>Salesforce.com recently released <a href="http://developer.force.com/flashbuilder">Adobe Flashbuilder for Salesforce</a> a toolkit that includes easy to use offline support. I think we need to build some killer offline apps with this toolkit to show the way in this space. Classroom attendance, health clinic service tracking, micro-finance payment collection. We need to build these and share the source code.</p>
<h2>Deep Case Studies</h2>
<p>I want to work with a few nonprofits in Africa to go deep in their usage of Salesforce.com. I want to hear the stories of how this helps them achieve their goals, and I want to share those stories with the world. Knowing what&#8217;s possible is an important step in the path to impact and excellence. We need to prove it, show it, tell it.</p>
<p>I normally don&#8217;t share my goals publicly. I like to under promise and over deliver. That&#8217;s worked for me for years. But I can&#8217;t seem to contain this new fire I feel. So I&#8217;m laying out what I want to see happen and I&#8217;ll do what I can to bring it about.</p>
<p>If you feel a stirring when you read this, join me. What will we do? I don&#8217;t know. How will we do it? We&#8217;ll figure it out. But I need to act today. If you feel that need, drop me a line, and we&#8217;ll help Africa together.</p>
]]></content:encoded>
			<wfw:commentRss>http://gokubi.com/archives/inflection-point/feed</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Using Javascript to validate an Apex form</title>
		<link>http://gokubi.com/archives/using-javascript-to-validate-an-apex-form</link>
		<comments>http://gokubi.com/archives/using-javascript-to-validate-an-apex-form#comments</comments>
		<pubDate>Mon, 16 Nov 2009 21:15:25 +0000</pubDate>
		<dc:creator>Steve</dc:creator>
				<category><![CDATA[CRM]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://gokubi.com/?p=956</guid>
		<description><![CDATA[Recently I came into a project where an Apex form on a Sites page had to validate itself in Javascript before sending an email via a controller method. I hadn&#8217;t ever done this before, so after some digging and some help, I thought I&#8217;d share the solution. First, here is the VisualForce form: &#60;apex:form id=&#34;emailform&#34; [...]]]></description>
			<content:encoded><![CDATA[<p>Recently I came into a project where an Apex form on a Sites page had to validate itself in Javascript before sending an email via a controller method. I hadn&#8217;t ever done this before, so after some digging and some help, I thought I&#8217;d share the solution.</p>
<p>First, here is the VisualForce form:</p>
<pre class="brush: html">
&lt;apex:form id=&quot;emailform&quot;  &gt;

&lt;input type=&quot;hidden&quot; id=&quot;redirect&quot; name=&quot;redirect&quot; value=&quot;contactus&quot;&gt;
Your Email: &lt;apex:inputText value=&quot;{!youremail}&quot; id=&quot;youremail&quot; maxlength=&quot;80&quot; size=&quot;40&quot; /&gt;&lt;br/&gt;

Subject: &lt;apex:inputText value=&quot;{!subject}&quot; id=&quot;Subject&quot; maxlength=&quot;80&quot; size=&quot;40&quot; /&gt;&lt;br/&gt;

Message: &lt;apex:inputTextarea value=&quot;{!body}&quot; id=&quot;Body&quot; rows=&quot;6&quot; cols=&quot;40&quot;/&gt;&lt;br/&gt;
&lt;apex:commandButton value=&quot;Send&quot; id=&quot;sendButton&quot; onclick=&quot;validate3()&quot;/&gt;

	&lt;apex:actionFunction id=&quot;emailSendFunction&quot; name=&quot;sendEmail&quot; action=&quot;{!send}&quot;&gt;
		&lt;apex:param name=&quot;youremailparam&quot; assignTo=&quot;{!youremail}&quot; value=&quot;&quot;/&gt;
		&lt;apex:param name=&quot;subjectparam&quot; assignTo=&quot;{!subject}&quot; value=&quot;&quot;/&gt;
		&lt;apex:param name=&quot;topicparam&quot; assignTo=&quot;{!topic}&quot; value=&quot;&quot;/&gt;
		&lt;apex:param name=&quot;bodyparam&quot; assignTo=&quot;{!body}&quot; value=&quot;&quot;/&gt;
	&lt;/apex:actionFunction&gt;
&lt;/apex:form&gt;
</pre>
<p>Note that the form doesn&#8217;t have an action: we&#8217;re invoking code via the onclick action of the command button. When the button is clicked, we&#8217;re calling Validate() a Javascript function on the page that looks like this:</p>
<pre class="brush: javascript">
function validate() {
     var errorMessage = &quot;Please fix the following errors: \n\n&quot;;
     var errorFound = 0; 

     if (!isValidEmail(document.getElementById(&quot;about_contact:emailform:youremail&quot;).value)) { errorMessage += &quot;Enter a Valid Email Address\n&quot;; errorFound = 1; }
     if (document.getElementById(&quot;about_contact:emailform:Subject&quot;).value.length == 0) {errorMessage += &quot;Enter a Subject\n&quot;; errorFound = 1; }
     if (document.getElementById(&quot;about_contact:emailform:Body&quot;).value.length == 0) {errorMessage += &quot;Enter your Message\n&quot;; errorFound = 1; }    

     if (errorFound == 1) {
	alert(errorMessage);
     } else {
	sendEmail();
     }
}
</pre>
<p>It&#8217;s a simple validation scheme. Check a bunch of fields and if they don&#8217;t look right, add a message and pop it up to the user. If everything is successful, we call sendEmail().</p>
<p>SendEmail() is the tricky part. It&#8217;s actually an Apex controller method exposed as a Javascript function so that it can be called by Javascript. This is done via the ActionFunction tag in Visualforce. You can see it in the VisualForce form above, but I&#8217;ll reproduce it here:</p>
<pre class="brush: html">
	&lt;apex:actionFunction id=&quot;emailSendFunction&quot; name=&quot;sendEmail&quot; action=&quot;{!send}&quot;&gt;
		&lt;apex:param name=&quot;youremailparam&quot; assignTo=&quot;{!youremail}&quot; value=&quot;&quot;/&gt;
		&lt;apex:param name=&quot;subjectparam&quot; assignTo=&quot;{!subject}&quot; value=&quot;&quot;/&gt;
		&lt;apex:param name=&quot;topicparam&quot; assignTo=&quot;{!topic}&quot; value=&quot;&quot;/&gt;
		&lt;apex:param name=&quot;bodyparam&quot; assignTo=&quot;{!body}&quot; value=&quot;&quot;/&gt;
	&lt;/apex:actionFunction&gt;
</pre>
<p>The controller&#8217;s send method is exposed and 4 parameters are asked for. Each of these parameters are mapped to public variables in the controller, where they are then used by the send method.</p>
<pre class="brush: java">
public class sffSendEmail {

	public String youremail { get; set; }
	public String subject { get; set; }
	public String topic { get; set; }
	public String body { get; set; }
	public String redirect { get; set; }	// Value tells us where to redirect

	// Constructor
	public sffSendEmail() {
	}

	public PageReference send() {
	 // Define the email
	 Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();

	 String[] toAddresses = new String[] { &#039;destination@email.com&#039; };

	 // Sets the paramaters of the email
	 email.setSubject( subject );
	 email.setToAddresses( toAddresses );
	 email.setPlainTextBody( &#039;From: &#039; + youremail + &#039;\n\n\r&#039; + &#039;Subject: &#039; + subject + &#039;\n\n\r&#039; + &#039;Message: &#039; + body + &#039;\n\n\r&#039; );

	 // Sends the email
	 Messaging.SendEmailResult [] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {email});

	 PageReference secondPage = Page.confirmation_page;

	 secondPage.setRedirect(true);
	 return secondPage;

	}
}
</pre>
<p>If you don&#8217;t use the param tags and explicitly map the form fields to the public variables, I found that the send method is invoked before the setters have run on your form, leaving you with null variables. Using the params gets the values into those variables for use.</p>
<p>Update: From the comments, Andrew Waite has a better way of constructing the validate() javaScript:</p>
<blockquote><p>
FYI, it’s a best practice to avoid the dependency on the generated DOM ID, i.e.: document.getElementById(”about_contact:emailform:youremail”. You’ve qualified every element in your path which is good but your code is still susceptible to being broken by a new layer in the hierarchy.<br />
&#8230;<br />
The better approach is to define your validation function to accept the DOM IDs as args and use $Component. For example, assume the validate function took one arg for the “youremail” field: function validate(emailid) {….}, and then your onclick would be something like: onclick=”validate({!$component.youremail});”.
</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://gokubi.com/archives/using-javascript-to-validate-an-apex-form/feed</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Nonprofit Salesforce Users &#8211; you need to get to Dreamforce</title>
		<link>http://gokubi.com/archives/nonprofit-salesforce-users-you-need-to-get-to-dreamforce</link>
		<comments>http://gokubi.com/archives/nonprofit-salesforce-users-you-need-to-get-to-dreamforce#comments</comments>
		<pubDate>Fri, 30 Oct 2009 18:28:51 +0000</pubDate>
		<dc:creator>Steve</dc:creator>
				<category><![CDATA[CRM]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://gokubi.com/?p=946</guid>
		<description><![CDATA[Metcalfe&#8217;s Law states that the value of a network is proportional to the square of the number of connected users. Network value isn&#8217;t additive&#8211;it&#8217;s geometric. Right now we&#8217;re at 115% of the number of registrants from last year. Following Metcalfe, and if Dreamforce was tomorrow, that means it would be 128% move valuable this year [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://en.wikipedia.org/wiki/Metcalfe's_law">Metcalfe&#8217;s Law</a> states that the value of a network is proportional to the square of the number of connected users. Network value isn&#8217;t additive&#8211;it&#8217;s geometric.</p>
<p>Right now we&#8217;re at 115% of the number of registrants from last year. Following Metcalfe, and if Dreamforce was tomorrow, that means it would be 128% move valuable this year than it was last year. You can&#8217;t argue with math. <img src='http://gokubi.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>But Dreamforce isn&#8217;t tomorrow&#8211;it starts November 17. If you come, you nonprofit user who hasn&#8217;t registered yet, you who has great ideas and insight to share with others, you who has so much to learn, the power of our network increases amazingly quickly.</p>
<p><strong>If we can get to double last year&#8217;s attendance, we will acheive 4 times the value of last year&#8217;s Dreamforce! It&#8217;s true&#8211;Metcalfe is right!</strong></p>
<p>This year will be my 4th Dreamforce conference. Every year has been much better than the previous. Every nonprofit organization that has taken the time and effort to use Salesforce.com to help manage their operations should find a way to send at least one staff member to the conference. The information, relationships, and experience will pay off many times the costs of traveling and registration.</p>
<p>If you&#8217;re at a Nonprofit using Salesforce.com, or thinking about using it, we currently have a $199 registration rate for you. To get the promotion code, email <a href="mailto:crmdonation@salesforce.com">crmdonation@salesforce.com</a>.</p>
<p>Come be a part of network power. See Metcalfe&#8217;s Law up close and personal! Join us in November!</p>
]]></content:encoded>
			<wfw:commentRss>http://gokubi.com/archives/nonprofit-salesforce-users-you-need-to-get-to-dreamforce/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>ONE/Northwest is hiring a CRM consultant</title>
		<link>http://gokubi.com/archives/onenorthwest-is-hiring-a-crm-consultant</link>
		<comments>http://gokubi.com/archives/onenorthwest-is-hiring-a-crm-consultant#comments</comments>
		<pubDate>Mon, 26 Oct 2009 15:08:13 +0000</pubDate>
		<dc:creator>Steve</dc:creator>
				<category><![CDATA[CRM]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://gokubi.com/?p=939</guid>
		<description><![CDATA[Anyone who asks me has heard the story&#8211;ONE/Northwest was an absolutely fabulous job with amazing people. I loved my time there&#8211;the technical challenges, the freedom to lead, the opportunity to shape the strategy. I won&#8217;t ever stop talking about ONE/Northwest, and the folks I shared that time with will always have a place in my [...]]]></description>
			<content:encoded><![CDATA[<p>Anyone who asks me has heard the story&#8211;ONE/Northwest was an absolutely fabulous job with amazing people. I loved my time there&#8211;the technical challenges, the freedom to lead, the opportunity to shape the strategy. I won&#8217;t ever stop talking about ONE/Northwest, and the folks I shared that time with will always have a place in my heart.</p>
<p>So good news for you! <a href="http://www.onenw.org/about/jobs/CRM-Consultant">ONE/Northwest is hiring a CRM consultant</a>. You could deliver cutting-edge Salesforce solutions to groups that will change the world with your work. Check out the job description and see if you&#8217;re up for some of what I think is the most fulfilling work around! </p>
]]></content:encoded>
			<wfw:commentRss>http://gokubi.com/archives/onenorthwest-is-hiring-a-crm-consultant/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Force.com User Group starting in Seattle</title>
		<link>http://gokubi.com/archives/force-com-user-group-starting-in-seattle</link>
		<comments>http://gokubi.com/archives/force-com-user-group-starting-in-seattle#comments</comments>
		<pubDate>Tue, 22 Sep 2009 21:28:44 +0000</pubDate>
		<dc:creator>Steve</dc:creator>
				<category><![CDATA[CRM]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://gokubi.com/?p=931</guid>
		<description><![CDATA[I just heard from Val that the Seattle Force.com Developer User group will kick off on October 1st. If you&#8217;re near Seattle and code in Apex and VisualForce, come join the group. I&#8217;m really excited about it. Want to take a deeper dive into the technical capabilities of Salesforce? Would you like to meet other [...]]]></description>
			<content:encoded><![CDATA[<p>I just heard from Val that the Seattle Force.com Developer User group will kick off on October 1st. If you&#8217;re near Seattle and code in Apex and VisualForce, come join the group. I&#8217;m really excited about it.</p>
<blockquote><p>Want to take a deeper dive into the technical capabilities of Salesforce? Would you like to meet other Force.com developers in the Seattle area? Interested in learning about new/different tools that will help you create and develop better solutions? A new Salesforce user group will be starting up on Thursday Oct 1. Meetings will be held on the first Thursday of every month.</p>
<p>We will try to cover a different range of topics at these monthly meetings such as:</p>
<ul>
<li>Summary of new functionality in the upcoming release</li>
<li>Project spotlight where someone will demo their project and the technical details</li>
<li>Best Practices</li>
<li>3rd Party development and integration tools</li>
<li>Apex and VisualForce</li>
<li>Open session to discuss specific questions you may have regarding your current projects</li>
</ul>
<p>Time: 8-9am<br />
Date: Oct 1, 2009<br />
Location:</p>
<p>West Monroe Partners<br />
1215 4th Ave Suite 1010<br />
Seattle, WA 98161</p>
<p>Please contact Val Grasparil (<a href="mailto:vgrasparil@westmonroepartners.com">vgrasparil@westmonroepartners.com</a>) or Andrew Brown (<a href="mailto:abrown@westmonroepartners.com">abrown@westmonroepartners.com</a>) if you have any questions.</p>
<p>Since it is in the morning, coffee and snacks will be provided. Please RSVP if you are planning on attending so we can make sure to have enough food for everyone.</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://gokubi.com/archives/force-com-user-group-starting-in-seattle/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
