Oh my god. It's full of code!

Visualforce

Mimicking callback functions for Visualforce ActionFuncitons

Hey everyone. So I’ve got a nifty ‘approach’ for you this time around. So let me give you a quick run down on what I was doing, the problem I encountered and how I decided to solve it using what I believe to be a somewhat novel approach. The deal is that I have been working on a fairly complicated ‘one page’ app for mobile devices. What I decided to do was have one parent visualforce page, and a number of components that are hidden and shown depending on what ‘page’ the user is on. This allows for a global javascript scope to be shared between the components and also for them to have their own unique namespaces as well. I may cover the pros and cons of this architecture later.

The issue I started to have, is that I wanted some action functions on the main parent container page to be used by the components in the page. That’s fine, no problem there. The issue becomes the fact that since actionFunctions are asynchronous, and do not allow for dynamic callback functions anything that wants to invoke your actionFunction is stuck having the same oncomplete function as all the functions that may want to invoke it. So if component A and component B both want to invoke ActionFunctionZ they both are stuck with the same oncomplete function, and since it’s async there is no good way to know when it’s done. Or is there?

My solution to this problem doesn’t use any particularity amazing hidden features, just a bit of applied javascript knowledge. What we are going to do is create a javascript object in the global/top level scope. That object is going to have properties that match the names of action functions. The properties will contain the function to run once the action function is complete. Then that property will be deleted to clean up the scope for the next caller. That might sound a little whack. Here let’s check an example.

    <style>
        #contentLoading
        {
            height: 100%;
            width: 100%;
            left: 0;
            top: 0;
            overflow: hidden;
            position: fixed; 
            display: table;
            background-color: rgba(9, 9, 12, 0.5);  
              
        }
        #spinnerContainer
        {
            display: table-cell;
            vertical-align: middle;        
            width:200px;
            text-align:center;
            margin-left:auto;
            margin-right:auto;
        }

        div.spinner {
          position: relative;
          width: 54px;
          height: 54px;
          display: inline-block;
        }
        
        div.spinner div {
          width: 12%;
          height: 26%;
          background: #fff;
          position: absolute;
          left: 44.5%;
          top: 37%;
          opacity: 0;
          -webkit-animation: fade 1s linear infinite;
          -webkit-border-radius: 50px;
          -webkit-box-shadow: 0 0 3px rgba(0,0,0,0.2);
        }
        
        div.spinner div.bar1 {-webkit-transform:rotate(0deg) translate(0, -142%); -webkit-animation-delay: 0s;}    
        div.spinner div.bar2 {-webkit-transform:rotate(30deg) translate(0, -142%); -webkit-animation-delay: -0.9167s;}
        div.spinner div.bar3 {-webkit-transform:rotate(60deg) translate(0, -142%); -webkit-animation-delay: -0.833s;}
        div.spinner div.bar4 {-webkit-transform:rotate(90deg) translate(0, -142%); -webkit-animation-delay: -0.75s;}
        div.spinner div.bar5 {-webkit-transform:rotate(120deg) translate(0, -142%); -webkit-animation-delay: -0.667s;}
        div.spinner div.bar6 {-webkit-transform:rotate(150deg) translate(0, -142%); -webkit-animation-delay: -0.5833s;}
        div.spinner div.bar7 {-webkit-transform:rotate(180deg) translate(0, -142%); -webkit-animation-delay: -0.5s;}
        div.spinner div.bar8 {-webkit-transform:rotate(210deg) translate(0, -142%); -webkit-animation-delay: -0.41667s;}
        div.spinner div.bar9 {-webkit-transform:rotate(240deg) translate(0, -142%); -webkit-animation-delay: -0.333s;}
        div.spinner div.bar10 {-webkit-transform:rotate(270deg) translate(0, -142%); -webkit-animation-delay: -0.25s;}
        div.spinner div.bar11 {-webkit-transform:rotate(300deg) translate(0, -142%); -webkit-animation-delay: -0.1667s;}
        div.spinner div.bar12 {-webkit-transform:rotate(330deg) translate(0, -142%); -webkit-animation-delay: -0.0833s;}
    
         @-webkit-keyframes fade {
          from {opacity: 1;}
          to {opacity: 0.25;}
        }    	
	</style>
	
		var globalScope = new Object();
		
		function actionFunctionOnCompleteDispatcher(functionName)
		{
			console.log('Invoking callback handler for ' +functionName);
			console.log(globalScope.actionFunctionCallbacks);
			
			if(globalScope.actionFunctionCallbacks.hasOwnProperty(functionName))
			{
				console.log('Found registered function. Calling... ');
				console.log(globalScope.actionFunctionCallbacks.functionName);
				globalScope.actionFunctionCallbacks[functionName]();
				delete globalScope.actionFunctionCallbacks.functionName;
			}
			else
			{
				console.log('No callback handler found for ' + functionName);
			}    
		}         
		
		function registerActionFunctionCallback(functionName, callback)
		{
			console.log('Registering callback function for ' + functionName + ' as ' + callback);
			globalScope.actionFunctionCallbacks[functionName] = callback;
			
			console.log(globalScope.actionFunctionCallbacks);
		} 
		
		function linkActionOne(dataValue)
		{
			registerActionFunctionCallback('doThing', function(){
				console.log('Link Action Two was clicked. Then doThing action function was called. Once that was done this happened');
				alert('I was spawened from link action 1!');
			});		
			
			doThing(dataValue);
		}
		
		function linkActionTwo(dataValue)
		{
			registerActionFunctionCallback('doThing', function(){
				console.log('Link Action Two was clicked. Then doThing action function was called. Once that was done this happened');
				alert('I was spawened from link action 2!');
			});		

			doThing(dataValue);
		}

		function loading(isLoading) {
			if (isLoading) 
			{            
				$('#contentLoading').show();
			}
			else {
				$('#contentLoading').hide();
			}	
		}		
    
	
	<apex:form >
		<apex:actionFunction name="doThing" action="{!DoTheThing}" reRender="whatever" oncomplete="actionFunctionOnCompleteDispatcher('doThing');">
			<apex:param name="some_data"  value="" />
		</apex:actionFunction>
		
		<apex:actionStatus id="loading" onstart="loading(true)" onstop="loading(false)" />
	
		<a href="#" onclick="linkActionOne('Link1!')">Link One!</a>
		<a href="#" onclick="linkActionTwo('Link2!')">Link Two!</a>

		
id="contentLoading" style="display:none">
id="spinnerContainer">
class="spinner">
class="bar1">
class="bar2">
class="bar3">
class="bar4">
class="bar5">
class="bar6">
class="bar7">
class="bar8">
class="bar9">
class="bar10">
class="bar11">
class="bar12">
</div> </div> </div> </apex:form>

So what the hell is going on here? Long story short we have two links which both call the same actionFunction but have different ‘callbacks’ that happen when that actionFunction is complete. I was trying to come up with a more interesting example, but I figured I should keep it simple for sake of explanation.  You click link one, the doThing action is called. Then it calls the actionFunctionOnCompleteDispatcher function with it’s own name. That function looks to see if any callbacks have been registered for that function name. If so, it is called. If not, it just doesn’t do anything. Pretty slick eh? You may be wondering why I included all that code with the action status, the loading animation, the overlay and all that. Not really relevant to what we are doing right (though the animation is cool.)? The answer to that is (other than the fact you get a cool free loading mechanism), this approach as it stands will start to run into odd issues if you users clicks link2 before link1 has finished doing it’s work. The callback function registered by 2 would get called twice. Once the call to doThing from link1 one its going to call whatever function is registered, even if that means the click from link2 overwrote what link1 set.  I am thinking you could probably get around this by making the property of the global object an array instead of just a reference to a function. Each call would just push it’s requested callback into the array and then as they were called they would be removed from the array, but I haven’t played with this approach yet (‘I’m pretty sure it would work, I’m just too lazy and tired to write it up for this post. If there is interest I’ll do it later). In any case putting up a blocking loading screen while the action function does its work ensures that the user cannot cause any chaos by mashing links and overwriting callbacks.

The thing that is kind of cool about this which becomes clear pretty quick is that you can start ‘chaining’ callbacks. So you can have a whole series of action functions that all execute in sequence instead of just running async all over the place. So you can do things like this. Also make note of the commenting. The thing about callbacks is you can quickly end up ‘callback hell’ where it gets very difficult to track what is happening in what order. So I at least attempt to label them in an effort to stem the madness. This is just a quick copy paste from the thing I’m actually working on to give you a flavor of how the chaining can work.

//once a project has been created we need to clear out any existing temp record, then set the type of the new/current temp record as being tpe. Then finally
//we have to set the project Id on that temp record as the one we created. Then finally we can change page to the select accounts screen.
VSD_SelectProject.addNewTpeComplete = function()
{

	//order 2: happens after clearTempRecord is done 
	//once the existing temp record has been cleared and a new one is created, get a new temp record and set the type as tpe
	registerActionFunctionCallback('clearTempRecord', function(){
		setRequestType('tpe');
	});
				
	//order 3: happens after setRequestType is done 
	//once set request type is done (which means we should now have a temp record in the shared scope, then call set project id
	registerActionFunctionCallback('setRequestType', function(){
		setProjectId('{!lastCreatedProjectId}');
	});

	//order 4: happens after setProjectId is done 
	//once set project id is called and completed change the page to the new_pcr_start (poorly named, it should actually be called select_accounts
	registerActionFunctionCallback('setProjectId', function(){
		setTitle('New TPE Request');
		setSubHeader('Select Accounts');
					
		changePage('new_pcr_start');
	});
	 
	//order 1: happens first. Kicks off the callback chain defined above.                                                
	clearTempRecord();                
}

Anyway, I hope this might help some folks. I know it would be easy to get around this issue in many cases by just creating many of the ‘same’ actionFunction just with different names and callbacks but who want’s dirty repetitive code like that?

Tune in next time as I reveal the solution to an odd ‘bug’ that prevents apex:inputFields from binding to their controller values. Till next time!

-Kenji


Export SOQL Query as CSV

Hey guys,
Long time no blog! Sorry about that, been kind of busy and honestly haven’t had too many interesting tidbits to share. However, I think I have something kind of neat to show you. I had a project recently where the user wanted to be to create a custom SOQL query and export the results as a CSV file. I don’t know why they didn’t want to use regular reports and export (my guess is they figured the query may be too complex or something) but it sounded fun to write, so I didn’t argue.

Breaking this requirement down into it’s individual parts revealed the challenges I’d have to figure out solutions for:
1) Allow a user to create a custom SOQL query through the standard interface
2) Extract and iterate over the fields queried for to create the column headings
3) Properly format the query results as a CSV file
4) Provided the proper MIME type for the visualforce page to prompt the browser to download the generated file

As it turns out, most of this was pretty easy. I decided to create a custom object called ‘SOQL_Query_Export__c’ where a user could create a record then specify the object to query against, the fields to get, the where condition, order by and limit statements. This would allow for many different queries to be easily created and saved, or shared between orgs. Obviously the user would have to know how to write SOQL in the first place, but in this requirement that seemed alright. The benefit as well is that an admin could pre-write a query, then users could just run it whenever.

With my data model/object created now I set about writing the apex controller. I’ll post it, and explain it after.

public class SOQL_Export {

    public SOQL_Query_Export__c exporter     {get;set;}
    public list<sobject>        queryResults {get;set;}
    public list<string>         queryFields  {get;set;}
    public string               queryString  {get;set;}
    public string               fileName     {get;set;}
    
    public SOQL_Export(ApexPages.StandardController controller) 
    {
        //Because the fields of the exporter object are not refernced on the visualforce page we need to explicity tell the controller
        //to include them. Instead of hard coding in the names of the fields I want to reference, I simply describe the exporter object
        //and use the keyset of the fieldMap to include all the existing fields of the exporter object.
        
        //describe object
        Map<String, Schema.SObjectField> fieldMap = Schema.SOQL_Query_Export__c.sObjectType.getDescribe().fields.getMap();
        
        //create list of fields from fields map
        list<string> fields = new list<string>(fieldMap.keySet());
        
        //add fields to controller
        if(!Test.isRunningTest())
        {
            controller.addFields(fields);
        }
        //get the controller value
        exporter = (SOQL_Query_Export__c) controller.getRecord();

        //create a filename for this exported file
        fileName = exporter.name + ' ' + string.valueOf(dateTime.now());
                
        //get the proper SOQL order direction from the order direction on the exporter object (Ascending = asc, Descending = desc)
        string orderDirection = exporter.Order_Direction__c == 'Ascending' ? 'asc' : 'desc';
        
        //create a list of fields from the comma separated list the user entered in the config object
        queryFields =  exporter.fields__c.split(',');
        
        //create the query string using string appending and some ternary logic
        queryString = 'select ' + exporter.fields__c + ' from ' + exporter.object_name__c;
        queryString += exporter.where_condition__c != null ? ' where ' + exporter.where_condition__c : '';
        queryString += exporter.Order_by__c != null ? ' order by ' + exporter.Order_by__c + ' ' + orderDirection :'';
        queryString += exporter.Limit__c != null ? ' limit ' +string.valueOf(exporter.Limit__c) : ' limit 10000';
        
        //run the query
        queryResults = database.query(queryString);
        
        
    }

    //creates and returns a newline character for the CSV export. Seems kind of hacky I know, but there does not seem to be a better
    //way to generate a newline character within visualforce itself.
    public static String getNewLine() {
      return '\n';
    }
}

Because I was going to use the SOQL_Query_Export__c object as the standard controller my apex class would be an extension. This meant using the controller.addFields method (fields not explicitly added by the addFields method or referenced in the visualforce page are not available on the record passed into the controller. So if I had attempted to reference SOQL_Query_Export__c.Name without putting it in my add fields method, or referencing it on the invoking page it would not be available). Since my visualforce page was only going to be outputting CSV content, I have to manually add the fields I want to reference. I decided instead of hard coding that list, I’d make it dynamic. I did this by describing the the SOQL_Query_Export__c object and passing the fields.getMap() keyset to the controller.addFields method. Also, just as something to know, test classes cannot use the addFields method, so wrap that part in an if statement.

Next it’s just simple work of constructing a filename for the generated file, splitting the fields (so I can get an array I can loop over to generate the column headers for the CSV file). Then it’s just generating the actual query string. I  used some ternary statements since things like order by and limit are not really required. I did include a hard limit of 10000 records if one isn’t specified since that is the largest a read only collection of sobjects can be. Finally we just run the query. That last method in the class is used by the visualforce page to generate proper CSV line breaks (since you can’t do it within the page itself. Weird I know).

So now with the controller, we look at the page.

<apex:page standardController="SOQL_Query_Export__c" cache="true"  extensions="SOQL_Export" readOnly="true" showHeader="false" standardStylesheets="false" sidebar="false" contentType="application/octet-stream#{!fileName}.csv">

  <apex:repeat value="{!queryFields}" var="fieldName">{!fieldName},</apex:repeat>{!newLine}
  
  <apex:repeat value="{!queryResults}" var="record"><apex:repeat value="{!queryFields}" var="fieldName">{!record[fieldName]},</apex:repeat>{!newLine}</apex:repeat>
  

</apex:page>

I know the code looks kind of run together. That is on purpose to prevent unwanted line breaks and such in the generated CSV file. Anyway, the first line sets up the page itself obviously. Removes the stylesheets, header, footer, and turns on caching. Now there are two reasonably important things here. The readOnly attribute allows a visualforce collection to be 10000 records instead of only 1000, very useful for a query exporter. The second is the ‘contentType=”application/octet-stream#{!fileName}.csv”‘ part. That tells the browser to treat the generated content as a CSV file, which in most browsers should prompt a download. You can also see that the filename is an Apex property that was generated by the class.

With the page setup, now we just need to construct the actual CSV values. To create the headers of the file, we simply iterate over that list of fields we split in the controller, putting a comma after each one (according to CSV spec trailing commas are not a problem so I didn’t worry about them). You can see I also invoke the {!newLine} method to create a proper CSV style newline after the header row. If anyone knows of a way to generate a newline character in pure visualforce I’d love to hear it, because I couldn’t find a way.

Lastly we iterate over the query results. For each record in the query, we then iterate over each fields. Using the bracket notation we can the field from the record dynamically. Again we create a newline at the end of each record. After this on the SOQL Export object I simple created a button that invoked this page passing in its record ID. That newly opened window would provide the download and then user could then close it (I’m experimenting with ways to automatically close the window once the download is done, but it’s a low priority and any solution would be rather hacky).

There you have it. A simple SOQL query export tool. I have this packaged up, but I’m not 100% I can give that URL away right now. I’ll update this entry if it turns out I’m allowed to share it. Anyway, hope this someone, or if nothing else shows a couple neat techniques you might be able to use.


Automatic Resize of Embedded/Inline Visualforce pages

I’m pretty sure at one time or another, most of us developers have made a sweet inline visualforce page that lives on the object detail page. Problem is since it’s all dynamic and cool we don’t know how ‘tall’ the content might end up being. We are basically stuck with having to guess at a decent height for the VF page and enable scrollbars, which kind of blows. Well not anymore (some exceptions apply)! Expanding on my previous post about using HTML5 postmessaging to move data between embedded visualforce pages and the standard salesforce domain I’ve come up with a first release of a ‘framework’ of sorts (term used very liberally). First an example. Lets check out what I’m talking about using my wordcloud app to demonstrate.

BEFORE

Scrollbars are Sad

 

AFTER
Woot woot no scrolling :D

So how did I accomplish this? The trick, as you may be able to guess by now is the HTML5 PostMessaging feature that I wrote about. By enabling communication between the frame (the visualforce page) and the parent (the detail page) it’s fairly simple to have the frame report it’s size to the parent, and have the parent adjust the height of the frame accordingly. Of course that isn’t everything my framework can do, oh no. It allows for passing of arbitrary commands/data between the two, so you can extend the functionality to pass any kind of commands/dom manipulations you might want between them. Of course the caveat is that this only works on newer browsers since postMessaging is fairly new. Also you’ll need to enable the sidebar on all pages, since that is where the proxy component lives. If you want to play with this install my alpha package from

https://login.salesforce.com/packaging/installPackage.apexp?p0=04ti0000000TY0J

Then include the home page component in the narrow/sidebar and enable the sidebar for all pages (setup->user interface->Show Custom Sidebar Components on All Pages). After that you just include these two components on any page you want to resize itself when embedded. The first component is the core piece that talks to the home page component. The second one is just some code that utilizes that framework to cause the iframe to resize. I’d put them at the bottom of your visualforce page.

<c:vfProxy />

<c:vfProxy_autoHeight />

I’m planning on possibly adding more ‘plugins’ for my framework to do other common handy things so if you have any ideas for potential plugins let me know.


Communication Between Visualforce Pages and Standard Pages Via Iframes

So I had what I thought was going to be a quick easy project recently, an idea spawned from some idea spit-balling at the office. Basically we wanted to create a page where you could queue up records, and it would show you the edit screen for them one at a time. Hit save, next record comes up in edit mode, save it, next record comes up in edit mode. Sounds a lot like the inline list view editor I know, but this would give you the full edit screen, ability to modify rich text areas, etc. I dunno, some other people wanted it. I decided it sounded fun and easy enough to build so I decided to take a crack at it.

I had a few internal conditions I wanted to satisfy as well. I wanted it to be light weight, fast, and not require much if any configuration to work for any object in Salesforce. It should be flexible, easy and straight forward. I didn’t want some huge apex controller backing it, required custom buttons or anything weird like that. This was a simple project and it should have a simple solution. The approach I decided to go with was to create a visualforce page that received a list of Ids to edit, it would then request the record for each of those ideas via an iframe. The iframe avoids having to run any messy queries, page layouts are respected, all that good stuff. Using some URL hacks I could tell it to load the record straight in edit mode (if you append a /e on the end of a record url it loads straight into edit mode), and even set the return URL (where the browser is directed after the user saves/cancels), seemed easy enough. Have the frame load the first record, set the return url to being the edit mode of the next record, and boom just follow that process until you exhaust the list. I forgot one crucial thing, well not so much forgot as ignored and hoped I could figure a way around it. Cross domain iframe security.

Let’s back up one second. If you are not aware, when using iframes if the parent frame and the child frame are both in the same domain you can use javacript to transverse them, inspect the child, share variables in the global JS scope, all that good stuff. If they are not on the same domain about the only thing you can do is load the frame, and tell when it’s source changes/reloads. That’s it. You can’t insepct it, you can’t modify it, you have extremely minimal control. Salesforce hosts visualforce pages and standard object pages on different domains. Bummer. This means I could not modify the behavior of the save button, and I couldn’t know if the user saved, cancelled, or anything about what they were doing in the frame. This means that after the got passed the second record (the first I could load into the frame directly, and then by setting the returnURL I could get them to go to the second one) I could no longer control where the user was going to easily. Sure I could change the src on the iframe itself but the only time I could do that was during a load event, which quickly gets you stuck in a loop. I know it sounds like it should be easy, but take a whack at it, I was unable to figure out an elegant solution after a couple hours. Bottom line, this wasn’t going to be as easy as I thought.

So here I was kind of stuck, I could load the first record and even set it to saving and returning to the second record in edit mode, but that was about it. The iframe source doesn’t change on record saves, and there isn’t any way to modify the behavior of the save button to return to the next record URL. I did come up with a hacky version that by detecting every other page load I could change the source to the assumed next record. EX the first record loads and the user saves. Code detects 2nd load event (which is now the detail view of the first record after the save) and changes the source to the next record. Waits for another reload and forwards them again. It worked, but it was hacky, unsatisfying and required waiting for the detail version of the page to load before forwarding the user onto the next edit mode. There had to be a better way, but I’d need to somehow modify the contents of the iframe.

I can’t say exactly how I remembered, but suddenly a few pieces of information kind of hit me all at once.
1) Sidebar components can run custom javascript and run off the core salesforce domain, not the visualforce domain.
2) HTML5 spec gives us a postMessage API to frames from different domains to pass information to each other.
3) Javascript can run arbitrary code using eval.

Could I stitch all this information together to build some kind of javascript proxy to run code on the core domain on behalf of my visualforce page? Indeed I could, and now I’ll show you how. Gather round; Kenji’s got a cool new trick to show you.

First you are going to need to enable custom sidebar components on all pages. Head to:

setup-> user interface -> check: Show Custom Sidebar Components on All Pages

Now create a narrow HTML homepage component. Slap this code in there. Yeah I know eval is bad, you can of course replace this with specific function calls if you want but I wanted to play around. Besides SF is a mostly secure environment.

<div id="jsProxy" style="width:100%;height:100px"></div>
<script>
    function receiveMessage(event) {
		var result = new Object();
		result.success = true;
		result.callback = event.data.callback;
		result.command = event.data.command;
		
        try {
            document.getElementById("jsProxy").innerHTML = 'JS proxy received command: ' + event.data.command;
            result.data = eval(event.data.command);
         
        } catch (ex) {
			result.data = ex.message;
			document.getElementById("jsProxy").innerHTML = 'Error running command: ' + ex.message;
        }
		event.source.postMessage(result,event.origin);
    }
    window.addEventListener("message", receiveMessage, false);
</script>

What we just did is created an event listener that listens for any postmessages coming in. It hands them off to our receiveMessage function. That will set the content of our div so we can see what got passed in (mostly for debugging) and then attempts to run the received message data as code. This received code is now running in the context of the core salesforce page, not within visualforce. With this we can modify elements on the standard object interface.

So now you might be saying, okay great, you can pass code to the page to run, but still, how are you going to change the returnURL? It’s too late to try and pass that into the URL by the time the page loads right? You are right, but thankfully Salesforce usually just takes URL parameters and jams them into hidden fields and uses them for whatever from there. So it’s just a matter of changing the value of the hidden ‘retURL’ field. So I pass to my function a bit of code that locates that field and modifies it to be the url of the next record in edit mode. Something like this.

    //where to go next
    var nextRecord = multiEdit.objectIds[parseInt(indexPosition+1,10)];
    
    //create command to pass to listener to run
    message = 'document.getElementById("retURL").value = "/'+nextRecord+'/e"';
    
    //create container variable so we could specify a callback if we wanted to
    var setReturnUrl = new multiEdit.postMessageData(message,null);
    
    //send the command
    iframeMessageWindow.postMessage(setReturnUrl,'*'); 
                       
    multiEdit.receiveMessage = function(event)
    {
        if(event.data.callback != null)
        {
            window.multiEdit[event.data.callback](event);
        }
        
    }
    
    multiEdit.postMessageData = function(command, callback)
    {
         this.command = command;
         this.callback = callback;   
    }
   

    window.addEventListener("message",multiEdit.receiveMessage, false);

Boom, now the code for changing the field is passed via the postMessage API to the homepage component that has the ability to run said code and operate on the DOM within the iframe. Pretty slick eh? With this technique the border between visualforce pages and standard pages has pretty much been smashed. Sure it needs a bit of refining, but overall I think this could allow for some pretty cool stuff.

BTW I’ll probably be releasing the utility that I wrote using this approach fairly soon. Till next time!


Salesforce Live Agent Review & Customization

So you are building a new website hosted on force.com and your boss says

‘Oh and we gotta have chat. Everyone has chat these days, we need it.’

Agree, disagree, doesn’t matter. You are doing it because rent is coming due and you can’t tell him that that idea is as bad as his comb-over (all purely hypothetical of course). So you start thinking about writing your own chat app because it sounds like fun (some UI options, push notifications, some cool chances to use javascript remoting maybe?), then realize you don’t have time for fun because this thing is due in like a week. So you frantically google around a bit and realize,

‘Wait a minute, Salesforce has it’s own native chat app “live agent”. That could probably do most of my work for me!’

Only question is can you hack at it enough to make it do what you need? Does it have a pre-chat form? Does it have a post chat survey? Does it save logs? How about a queue system? The short answer is yes. I was actually blown away at how much time and energy they put into the live agent chat. It can do pretty much everything any reasonable person would ask of it, and a few unreasonable things as well.  As a developer though you just want to get it up and running as fast as possible so you can play with all the bells and whistles right? Manuals are readme’s are for suckers, let’s just throw it on a page somewhere as a POC to make the boss man happy. So how do you go about doing that, what does the process look like? In a nutshell, it’s going to go like this.

1) Get the Salesforce liveagent trial for your org.
2) Log into Salesforce and go to customize->live agent.
3) Create a deployment, and a button
4) Paste the scripts they give you onto your webpage
5) Feel slightly disappointed that it was too easy and you didn’t get to derive any satisfaction from solving a problem yourself.

OH! Before I forget, I felt really dumb when I first set this up because I put it all in place and couldn’t figure out how to actually let an agent login to the chat. I kept seeing something about a console view, but the console didn’t say anything about chat. It turns out you just have to create a console view (you know like you do for cases or whatever) and in the lower right corner there is a chat login in. It’s all within Salesforce, there is no other service to authenticate to or anything, which is pretty sweet.

So now you show your boss, and he’s like ‘Yeah that’s cool, but we’d like to know who we are talking to via a pre-chat form, and if they don’t exist in our system it would be cool to create a lead for them. Also if nobody is online we should just redirect them to a contact us form or something’. Ah-hah finally something fun! So you probably saw while creating your chat button there was a lookup field to a pre-chat form, but didn’t really have anything to populate that with. Nor do you have any idea how to build one. Well it turns out Salesforce actually has a pretty robust API centered around their chat, and a couple ways to pass information to the agent responding to the chat. I’m going to focus on the ‘simple’ form based approach since I haven’t used their javascript API they offer. So this pre-chat form can actually perform lookups, pass data, save information into the chat record itself, it’s pretty wild, but a little confusing. So first check out the pre-chat form sample Salesforce provides, it gives a good basic understanding of how it works.

Salesforce Pre-Chat Form Sample

You can see that you create fields, then some hidden fields take the values of the user entered fields and run queries or pass that info along to the console. You can populate fields on the chat record by creating ‘fields’ that look like this

<input type=”hidden” name=”liveagent.prechat.save:Email”  value=”Provided_Email__c” />

That says, take the value of the field called liveagent.prechat.Email from this form and save it into the Provided_Email__c field on the chat history object. Of course you could reference another hidden field in that name attribute and use code to set the value of that other hidden field allowing you to pass in basically whatever you like to the chat history. You can create custom fields and pass values to them, as well as passing values to the standard fields too.

But now we need to solve our issue of lookuping up the contact or lead based on email, and if one doesn’t exist creating one on the fly and returning that.  There are a few ways you could take this, but since I love javascript I decided to go with the javascript remoting approach. The user is going to enter their name and email, when they click submit instead of actually submitting the form a remoting function is going to run that runs our query and returns the result, or creates one. With that data we will populate the contactid or leadid field (depending what kind of record it is) and pass that along to the console by using javascript to submit the form once the function has run. Additionally using the ability to detect if there are agents online or not, we can change the behavior of the buttons (well actually we just show and hide different buttons) to send the user elsewhere if there is nobody online. It looks something like this.

<apex:page showHeader="false" controller="PreChatController">

<apex:variable var="deploymentId" value="572c0000000CaRW" />
<apex:variable var="orgId" value="00Dc0000001M6Ix" />
<apex:variable var="buttonId" value="573c0000000CaSe" />

<!-- This script takes the endpoint URL parameter passed from the deployment page and makes
it the action for the form -->
<script type='text/javascript' src='https://c.la7cs.salesforceliveagent.com/content/g/js/29.0/deployment.js'></script>

<script type='text/javascript'>
    liveagent.init('https://d.la7cs.salesforceliveagent.com/chat', '{!deploymentId}', '{!orgId}');
</script>

<script type="text/javascript">
    (function() 
    {
        function handlePageLoad()
        {
            var endpointMatcher = new RegExp("[\\?\\&]endpoint=([^&#]*)");
            document.getElementById('prechatForm').setAttribute('action',
            decodeURIComponent(endpointMatcher.exec(document.location.search)[1]));
        } 
        if (window.addEventListener) 
        {
            window.addEventListener('load', handlePageLoad, false);
        } 
        else 
        { 
            window.attachEvent('onload', handlePageLoad, false);
        }
    })();

    if (!window._laq) { window._laq = []; }

    window._laq.push(function()
    {
        liveagent.showWhenOnline('{!buttonId}', document.getElementById('prechat_submit'));
        liveagent.showWhenOffline('{!buttonId}', document.getElementById('liveagent_button_offline_{!buttonId}'));
    });

    function getLeadOrContact()
    {
        console.log('Getting lead or contact');
        var emailAddr = document.getElementById('email').value.trim();
        var fname = document.getElementById('name').value.trim();
        var phone = document.getElementById('phone').value.trim();

        try
        {
            Visualforce.remoting.Manager.invokeAction(
                '{!$RemoteAction.PreChatController.findLeadOrContactByEmail}', 
                fname,
                emailAddr,
                phone, 
                function(result, event)
                {
                    if (event.status) 
                    {
                        console.log(result);
                        if(result.Id.substring(0,3) === '003')
                        {
                            document.getElementById('contactid').value = result.Id;
                        }
                        else if(result.Id.substring(0,3) === '00Q')
                        {
                            document.getElementById('leadid').value = result.Id;
                        }
                        document.forms["prechatForm"].submit();

                        return true;
                    } 
                }, 
                {escape: false}
            );
        }
        catch(ex)
        {
            alert(ex.message);
            console.log(ex);
            return false;
        }
        return false;
    }   

</script>
<style>
body
{
    background-color:#f4f4f4;
}
#chatFormDiv
{
    width:200px;
    text-align:center;
    padding:5px;
}
#chatHeader
{
    color:#6d6d6d;
    font-size:18px;
    font-weight:bold;
}
label
{
    width:150px;
    font-weight:bold;
}
input[type=text], textarea
{
    width:200px;
    background: #f3f3f3; /* Old browsers */
    background: -moz-linear-gradient(top, #f3f3f3 0%, #ffffff 100%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f3f3f3), color-stop(100%,#ffffff)); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(top, #f3f3f3 0%,#ffffff 100%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(top, #f3f3f3 0%,#ffffff 100%); /* Opera 11.10+ */
    background: -ms-linear-gradient(top, #f3f3f3 0%,#ffffff 100%); /* IE10+ */
    background: linear-gradient(to bottom, #f3f3f3 0%,#ffffff 100%); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f3f3f3', endColorstr='#ffffff',GradientType=0 ); /* IE6-9 */    
    border-color: #dedede;
    border-top-color: #d3d3d3;
}
textarea
{
    height:140px;
}
.chatStatusDiv
{
    display:none;
}
</style>

<div id="chatFormDiv">
    <img src="{!URLFOR($Resource.BeaconWebsite,'img/chatIconSmallGrey.png')}" /> <span id="chatHeader">Chat</span><br/>
    <hr />

    <form method='post' id='prechatForm' onsubmit="return false;" action="https://15af.la7cs.salesforceliveagent.com/content/s/chat?language=en_US#deployment_id={!deploymentId}&org_id={!orgId}&button_id={!buttonId}">

    <input type='text' name='liveagent.prechat.name' id='name' placeholder="Your Name" required="required"/><br />

    <input type='text' name='liveagent.prechat:Email' id='email' placeholder="Email Address" required="required" /><br />

    <input type='text' name='liveagent.prechat:Phone' id='phone' placeholder="Phone" required="required" /><br />

    <textarea name='liveagent.prechat:Body' id='body' placeholder="Message" required="required" ></textarea><br />

    <input name="liveagent.prechat.buttons" value="{!buttonId}" type="hidden" /><br />

    <!-- Creates an auto-query for a matching Contact record’s Email field based on the
    value of the liveagent.prechat:Email field -->
    <input type="hidden" name="liveagent.prechat.query:Email" value="Contact,Contact.Email" />

    <!--- populate fields ---->
    <input type="hidden" name="liveagent.prechat.query:Email" value="Lead,Lead.Email" />
    <input type="hidden" name="liveagent.prechat.save:Email"  value="Provided_Email__c" />
    <input type="hidden" name="liveagent.prechat.save:name"   value="Provided_Name__c" />
    <input type='hidden' name='liveagent.prechat:ContactId'   value='' id='contactid'/>  
    <input type="hidden" name="liveagent.prechat.save:ContactId" value="Contact" />

    <input type='hidden' name='liveagent.prechat:LeadId' id='leadid' />  
    <input type="hidden" name="liveagent.prechat.save:LeadId" value="Lead" />

    <!--- Button that shows up if someone is online --->    
    <img   src="{!URLFOR($Resource.BeaconWebsite,'img/chatButton.png')}" 
           alt="Submit Form" 
           onclick="getLeadOrContact()"
           id='prechat_submit'
           style="display: none; border: 0px none; cursor: pointer; float: left;"/>

    <!--- Button that shows up if nobody is online --->
    <img id="liveagent_button_offline_{!buttonId}" 
         style="display: none; border: 0px none; cursor: pointer; float: left;" 
         src="{!URLFOR($Resource.BeaconWebsite,'img/chatButton.png')}" 
         onclick="alert('nobody online, put your redirect here');"/>

    </form>
</div>
</apex:page>

And the Apex Controller looks like this

global class PreChatController
{

    @remoteAction
    global static sobject findLeadOrContactByEmail(string name, string email, string phone)
    {
        sObject returnObject; //the id that will store the contact or lead this chat is related to
        //first we should see if there is a contact with this email
        list<contact> contacts = [select accountid, name, id, email from contact where email = :email limit 1];
        if(!contacts.isEmpty())
        {
            return contacts[0];
        }

        //if there is no contact, then lets look for a lead instead. Yeah we could combind both queries into a single SOSL search, but the code for that doesn't
        //end up being much cleaner when you account for having to figure out what record to return when you have a list of lists.
        else
        {
            list<lead> leads = [select name, id, email from lead where email = :email limit 1];
            if(!leads.isEmpty())
            {
                return leads[0];
            }
            else
            {            
                lead thisLead = new lead();
                string[] nameParts = name.split(' ');

                thisLead.firstname = nameParts.size() > 1 ? nameParts[0] : ''; //if name parts contains more than one element that means we likely got a full name and the first part is the firstname. Otherwise nothing
                thisLead.lastname =  nameParts.size() > 1 ? nameParts[1] : nameParts[0]; //if name parts is greater than 1 then use the 2nd element as the lastname. Otherwise use the first element
                thisLead.phone = phone;
                thisLead.email = email;
                thisLead.company = name;
                thisLead.leadSource = 'Web Site';

                insert thisLead;
                return thisLead;
            }
        }
    }
}
Chatone

The user is asked to enter their information to connect with an agent.

chatTwo

When the agent is alerted that there is a person waiting to chat they automatically get a ‘screen pop’ informing them of the contact details

chatThree

The full contact or lead record is available to the agent making all their persons information instantly available. Much nicer than having to ask them a million questions.

Now when a user uses your chat form they will have to fill in their email, name and phone. Using that we can either locate them if they already exist, or create them on the fly and pass that information along to the agent. Pretty slick eh? One small bug that I dislike and currently do not know how to fix is that even though the contactId/leadId is passed to the console those fields are not populated visually. So your agent doesn’t see the lookups populated, even though they are. I think for now it’s just a training point to tell them. When the chat is saved and closed the transcript will be related to the contact/lead you just can’t see it on this screen. Weird I know.

Anyway I hope this helps getting you started and maybe even finished implementing Live Agent. It’s pretty cool and has a lot of features/power…. way more than I would have guessed. Best of all it makes you look like some kind of development wizard when you get go from request to implementation in like a few hours, so now you can get back to reddit… until the next request comes.


Visualforce Force Download of PDF or Other Content

Hey everyone,

This next trick is one I’ve kind been keeping under my hat since it’s a nice polishing touch for some of my contest entries, but I figured I should probably share it with the world now (information must be free, etc). So we all know we can create Visualforce pages that render as PDF documents. It’s a pretty cool feature especially because business people love PDF files more than I love being a cynical ass (which is like… a lot). Though the one little annoyance is that normally when you create that PDF visualforce page the user is brought to it to view it where they then can download it. Many times they simply want to download it and attach it to an email or something, the viewing isn’t required and is generally just an extra few wasted seconds waiting for it to load so they can hit file->save as. I have found/built a nifty way to force download of the file using a combination of Apex and some tricky DOM manipulation. As an added bonus I’ll show you how to conditionally render the page as a PDF based on a URL param. Here we go!

The first thing we’ll need of course is our Visualforce page, we’ll keep it simple for this example. So here is our visualforce page

<apex:page controller="forceDownloadPDF" renderAs="{!renderAs}">
<h2>PDF Download example</h2>

<p>This is some content that could be displayed as a PDF or a regular web page depedning on the URL params. The valid URL params are as follows</p>
<table width="100%" cellpadding="5" cellspacing="5">
    <tr>
        <th>Name</th>
        <th>Type</th>
        <th>Default</th>
        <th>Required</th>
        <th>Description</th>
    </tr>
    <tr>
        <td>pdf</td>
        <td>String with a boolean value</td>
        <td>null/false</td>
        <td>false</td>
        <td>if passed in as a true the page will be rendered as a PDF. Otherwise displayed as HTML</td>
    </tr>
    <tr>
        <td>force_download</td>
        <td>String with a boolean value</td>
        <td>null/false</td>
        <td>false</td>
        <td>If true the user will be prompted to download the contents of the page. Suggested to be paired with pdf=true</td>
    </tr>
    <tr>
        <td>filename</td>
        <td>String (valid file name)</td>
        <td>'My PDF Report [todays date].pdf'</td>
        <td>false</td>
        <td>A name for the file. Only used if force_download=true</td>
    </tr>    
</table>

</apex:page>

And now our controller

public class forceDownloadPDF {

    public string renderAs{get;set;}

    public forceDownloadPDF()
    {

        //figure out if the user passed in the pdf url variable and if it is set to true.
        if(ApexPages.currentPage().getParameters().get('pdf') != null && ApexPages.currentPage().getParameters().get('pdf') == 'true') 
        {
            //if so, we are rendering this thing as a pdf. If there were other renderas options that were valid we could consider allowing the user to pass
            //in the actual renderAs type in the url, but as it stands the only options are pdf and null so no reason to allow the user to pass that in directly.
            renderAs = 'pdf';

            //figure out if we are forcing download or not.
            if(ApexPages.currentPage().getParameters().get('force_download') != null && ApexPages.currentPage().getParameters().get('force_download') == 'true') 
            {
                //setup a default file name
                string fileName = 'My PDF Report '+date.today()+'.pdf';

                //we can even get more created and allow the user to pass in a filename via the URL so it can be customized further
                if(apexPages.currentPage().getParameters().get('filename') != null)
                {
                    fileName = apexPages.currentPage().getParameters().get('filename') +'.pdf';
                }
                //here is were the magic happens. We have to set the content disposition as attachment.
                Apexpages.currentPage().getHeaders().put('content-disposition', 'attachemnt; filename='+fileName);
            }               
        }        
    }
}

As noted in the comments the real secret here is setting the content disposition use the Apex getHeaders method. Now you are saying,

‘But Kenji if I call that page from a link it still opens in a  new window it just forces the user to download the file. That’s not much better!’

Oh ye of little faith, of course I got you covered. You think I’d leave you with a half done solution like that? Hell no. Lets take this mutha to the next level. Here is what we are going to do. Using a custom button with onClick javascript we are going to create an iframe with the source set as that visualofrce page (with the force_download=true param) and inject it into the DOM. When the frame loads (which will have 0 width, and height so it’s not visible) that code still runs prompting the user to download the file. They are non the wiser that a frame got injected, all they see is a happy little download dialog prompt. So go create a custom button on an object that you want to prompt the user to download your file from. Make it a detail page button (you could do a list button to, but that’s a topic for another day). Make it onClick javascript. Then slap this code in there.

ifrm = document.createElement("IFRAME"); 
ifrm.setAttribute("src", "/apex/yourPage?pdf=true&force_download=true&filename=My Happy File"); 
ifrm.style.width = 0+"px"; 
ifrm.style.height = 0+"px"; 
document.body.appendChild(ifrm);

Of course replace the ‘yourPage’ with the name of your visualforce page. The filename of course can be changed to be details from the record, or whatever you like. Now when the user clicks that button the javascript creates an invisible iframe and injects it into the DOM. Once it loads the user is prompted to download the file. Pretty slick eh?

Hope you dig it. Catch ya next time.


Building a Better WordCloud

Hey all,

I know it’s been a while since my last post. Fun projects with cool end results have been rare and I’m not the type to post stuff just to fill space so I’ve kinda just been chillin recently. Though that changed when I was asked to take another whack at putting together a word cloud app, this time for Salesforce. You may or may not remember that I created a small word cloud app a while ago that didn’t have much anything to do with Salesforce and used a PHP back end that took free text replies from lime survey and created a real time word cloud. This time the task is a little different. I was asked to create a word cloud application that would take data from multiple records, and multiple fields, aggregate it all together and create a cloud from the resulting text blob. Real time updating was not requested, so I had some more flexibility in architecture. I also wanted to take this change to upgrade the actual display a bit since I wasn’t very pleased with my last attempt (it was functional, but a little clunky. Manual CSS rules specified formatting, etc).

Since I knew there must be a good word cloud generator out there I did a bit of searching and decided on the jQuery plugin ‘awesomeCloud’ which makes very stylish clouds without being overly complex. It’s licensing is pretty open (GPL v3) so it looked like a good fit. You can check out some of the sample word clouds in generates on the awesomeCloud demo page. It’s got some nice options for themeing, cloud shape, normalization and more.

So invocation is the word cloud is pretty easy, you just need to create a div with spans in it that have a data-weight attribute that specifies the frequency of that word. You then just call the plugin with the options you want on the container div. Like so

Example of creating a wordcloud (taken from the github page of awesomeCloud)

Example of creating a wordcloud (taken from the github page of awesomeCloud)

Easy enough? But now comes the tricky part, how do we get the data from the objects and fields count the word frequencies and then insert them into the DOM? That is where we come in. I decided that since this is going to be a pretty light weight application with most likely fairly small queries we could get away with using the ajax toolkit thus avoiding the extra complexities of Apex controllers. As usual I’m defaulting to using javascript where I can. You could of course modify the approach below to use Apex if it makes you happier. Whatever works for you. Anyway, lets get on with it.

I decided that since this code may be used in different places to do different things (maybe some inline pages, a stand alone VF page, a dashboard component maybe?), it made sense to make it into a component. Makes it easier to pass in configuration parameters as well, which I figured people would want to do. I’d recommend doing the same. First up lets go ahead of define the parameters we are going to allow to be passed into the component. Most of these are for configuring the word cloud itself, but there are a few others.

    <apex:attribute name="objectType" description="type of records to get data from" type="String" required="true"/>
    <apex:attribute name="records" description="records to get data from" type="String" required="false"/>
    <apex:attribute name="fields" description="fields on records to aggregate data from" type="String" required="true" default="Name"/> 

    <apex:attribute name="lowerbound" description="words below this frequency will not be dipslayed" type="String" required="false" default="0"/> 
    <apex:attribute name="skipwords" description="words not to display regardless of frequency" type="String" required="false" default="and,the,to,a,of,for,as,i,with,it,is,on,that,this,can,in,be,has,if"/> 

    <apex:attribute name="grid" description="word spacing; smaller is more tightly packed but takes longer" type="integer" required="false" default="8"/>    
    <apex:attribute name="factor" description="font resizing factor; default 0 means automatically fill the container" type="integer" required="false" default="0"/>
    <apex:attribute name="normalize" description="reduces outlier weights for a more attractive output" type="boolean" required="false" default="false"/> 

    <apex:attribute name="font" description=" font family, identical to CSS font-family attribute" type="string" required="false" default="Futura, Helvetica, sans-serif"/> 
    <apex:attribute name="shape" description="one of 'circle', 'square', 'diamond', 'triangle', 'triangle-forward', 'x', 'pentagon' or 'star'" type="string" required="false" default="circle"/> 

    <apex:attribute name="backgroundColor" description="background color" type="string" required="false" default="transparent"/>
    <apex:attribute name="colorTheme" description="dark or light" type="string" required="false" default="light"/>

    <apex:attribute name="width" description="how wide should the cloud be (css values such as a pixel or inch amount)?" type="string" required="false" default="600px"/>  
    <apex:attribute name="height" description="how tal; should the cloud be (css values such as a pixel or inch amount)?" type="string" required="false" default="400px"/>  
    <apex:attribute name="autoRefresh" description="should the wordcloud automatically refresh every so often?" type="boolean" required="false" default="false"/>

    <apex:attribute name="refreshInterval" description="how often shold the cloud refresh? In seconds" type="integer" required="false" default="5"/>

Of course you’ll need to include the required javascript libraries.

<script src="//code.jquery.com/jquery-latest.js"></script>
<script src="{!$Resource.jQueryWordCloud}"/>
<apex:includeScript value="/soap/ajax/15.0/connection.js"/>
<apex:includeScript value="/soap/ajax/15.0/apex.js"/>

Now we’ll need to start putting together the javascript functions to get the data and do the word frequnecy analysis. First up, lets create a javascript method that can build a dynamic query to get all the required data. Using the ajax toolkit it looks something like this

function getData(objectType,recordIds, fields)
{
    //build the basic SOQL query string
    var queryString = "Select "+fields+" from "+objectType;

    //if we are limiting the query by searching for specific object ids then add that condition
    if(recordIds.length > 0)
    {
        var whereStatment = "('" + recordIds.split("','") + "')";
        queryString += ' where id in ' + whereStatment;
    }
    //make sure to put a limit on there to stop big query errors.
    queryString += ' limit 2000';

    //run the query
    result = sforce.connection.query(queryString);

    //get the results
    records = result.getArray("records");

    //lets aggregate all the fetched data into one big string
    var wordArray = [];

    //we want to build an array that contains all the words in any of the requested fields. So we will
    //put all the desired fields into an array, iterate over all the records, then over all the fields
    //and stash all the data in another array.

    //fields is a comma separated string. Lets split it into an array so we can iterate over them easily 
    var fieldsArray = fields.split(',');

    //loop over all the records
    for (var i=0; i< records.length; i++)
    {
        var record = records[i];
        //loop over all the fields that we care about
        for(var j=0; j<fieldsArray.length; j++)
        {
            //get the value of this field from this record and add it to the array
            wordArray.push(record[fieldsArray[j]]);
        }
    }  
    //we will now pass in all the words from all the records fields into the getWordFrequnecy function. By calling join
    //on the array with a space character we get one big ass text chunk with all the words from all the records.

    var frequencyResult = getWordFrequency(wordArray.join(' '));

    //pass our frequnecy result data into the load cloud function
    loadCloud(frequencyResult); 
}

With that we can pass in an sObject type, an optional list of record ids (comma separated) and a list of fields to get data from on those records (also comma separated). Now we need to build the getWordFrequency function. That’s going to take a chunk of text and return an array of objects that contain a tag property, and a freq property. That data then gets fed into awesomeCloud to generate the cloud.

            //takes a string and counts the freqnecy of each word in it. Returns array of objects with tag and freq properties.
            //words should be space separated
            function getWordFrequency(wordString){

                //convert string to lower case, trims spaces, cleans up some special chars and splits it into an array using spaces and delimiter.
                var sWords = wordString.toLowerCase().trim().replace(/[,;.]/g,'').split(/[\s\/]+/g).sort();
                var iWordsCount = sWords.length; // count w/ duplicates

                // array of words to ignore
                var ignore = '{!skipwords}'.split(',');
                ignore = (function(){
                    var o = {}; // object prop checking > in array checking
                    var iCount = ignore.length;
                    for (var i=0;i<iCount;i++){
                        o[ignore[i]] = true;
                    }
                    return o;
                }());

                var counts = {}; // object for math
                for (var i=0; i<iWordsCount; i++) {
                    var sWord = sWords[i];
                    if (!ignore[sWord]) {
                        counts[sWord] = counts[sWord] || 0;
                        counts[sWord]++;
                    }
                }

                //get the lower bound as an integer. Lower bound controls the minimum frequnecy a word/tag can have for it to appear in the cloud
                var lowerBound = parseInt({!lowerbound},10);
                var arr = []; // an array of objects to return
                for (sWord in counts) {
                    if(counts[sWord] > lowerBound)
                    {
                        arr.push({
                            tag: sWord,
                            freq: counts[sWord]
                        });
                    }
                }

                /* Sorting code, not really required for this purpose but kept in case it is decided that we want it for some reason.
                // sort array by descending frequency | http://stackoverflow.com/a/8837505
                return arr.sort(function(a,b){
                    return (a.freq > b.freq) ? -1 : ((a.freq < b.freq) ? 1 : 0);
                });
                */

                return arr;

            }

Alright, so now we have all the data from all the records analyzed and the frequency of each word is known. Words we have decided to skip have been left out and those that don’t make the cut from the lower bound are also excluded hopefully leaving us only with actually interesting words. Now we have to pass in the that data to awesomeCloud along with our settings.

function loadCloud(data) 
{ 
    //wordcloud settings
    var settings = {
            size : {
                grid : {!grid},
                factor : {!factor},
                normalize: {!normalize}
            },
            color : {
                background: "{!backgroundColor}"
            },
            options : {
                color : "random-{!colorTheme}",
                rotationRatio : 0.5
            },
            font : "{!font}",
            shape : "{!shape}"
    } 

    //create array for tag spans
    var wordStringArray = [];

    //evaluate each array element, create a span with the data from the object
    $.each(data, function(i, val)
    {
        wordStringArray.push('<span data-weight="'+val.freq+'">'+val.tag+'</span>');
    });

    //join all the array elements into a string. I've heard to push stuff into array and join then push to the DOM is faster than
    //than modifying a string (since strings are unmutable) or modifying the DOM a ton, which makes sense.
    $( "#wordcloud" ).html(wordStringArray.join(''));

    //setup our word cloud
    $( "#wordcloud" ).awesomeCloud( settings );
}

Alright, so now we just need to invoke all this these crazy functions. We’ll do that with a functional call in the document onready to make sure everything is loaded before we start going all crazy modifying the DOM and such.

//login to salesforce API so we can run query
sforce.connection.sessionId = '{!$Api.Session_ID}';
$(document).ready(function() {
    //make an immediate call to getData with the info we need from the config params.
    getData('{!objectType}', '{!records}', '{!fields}');

    //if we are doing an auto refresh, rig that up now using the setInterval method
    if({!autoRefresh})
    {
        setInterval ( "getData('{!objectType}', '{!records}', '{!fields}')", parseInt({!refreshInterval},10) * 1000 );
    }
});

Finally we just need to create our HTML container for the wordcloud and setup the CSS style.

    <style>

    .wordcloud {
        /*border: 1px solid #036;*/
        height: {!height};
        margin: 0.5in auto;
        padding: 0;
        page-break-after: always;
        page-break-inside: avoid;
        width: {!width};

    }

    </style>
    <div id="container" title="wordcloud with the content of fields {!fields} for objects {!records}"> 

        <div id="wordcloud" class="wordcloud" ></div>
    </div>

Whew, alright so that’s it for our component. Now we just need a visualforce page to invoke it. That part is easy. Although our word cloud is capable of aggreating data from multiple objects, for this demo we’ll keep it simple. We will create a little inline visualforce page that can live on the account object. It will create a wordcloud from the description, name, type and ownership fields. To do that, create a visualforce page like this

<apex:page standardController="account">
    <!--- 
    WordCloud comes as a component that can be invoked from any visualforce page. You must pass it the object type to build the cloud for. The rest is optional.
    objectType: the type of sObject to get data from to power the word cloud
    fields: the records on the objects who's content will be used to create the cloud. Must be comma separated
    records: a list of ids which to query for. If none is provided all records of the objectType are queried
    skipwords: words that will not be included in the word cloud no matter how many times they appear. 
    lowerbound: the minimum number of times a word must appear in the text before it is displayed in the cloud.
    grid: word spacing; smaller is more tightly packed but takes longer
    factor: font resizing factor; default "0" means automatically fill the container
    normalize: reduces outlier weights for a more attractive output
    shape: shape of the cloud. Must be one of "circle", "square", "diamond", "triangle", "triangle-forward", "x", "pentagon" or "star"
    font: font family, identical to CSS font-family attribute
    width: width of the cloud. Can be a percent or pixel/inch amount.
    height: height of the cloud. Must be a pixel or inch amount.
    backgroundColor: a hexidecimal (#000000) or string color value. Use 'transparent' for no background
    colorTheme: theme for word colors. Use 'dark' or 'light'
    autoRefresh: automatically refresh the cloud after a specified interval?
    refreshInterval: interval (in seconds) after which the cloud is automatically refreshed
    --->
    <c:wordCloud objectType="account" 
                 records="{!account.id}" 
                 fields="Name,Type,Ownership,Description"
                 lowerbound="1"
                 skipwords="and,an,any,so,or,are,the,to,a,of,for,as,i,with,it,is,on,that,this,can,in,be,has,if"
                 grid="8"
                 factor="0"
                 normalize="false"
                 font="Futura, Helvetica, sans-serif"
                 shape="triangle"
                 width="100%"
                 height="400px" 
                 backgroundColor="black"
                 colorTheme="light" 
                 autoRefresh="false" 
                 refreshInterval="15" />
</apex:page>

Save that page and add it to the page layout of the account. Put a nice big blob of text in the description of an account and see what happens. For my example I figured it would be fitting to copy and paste text from the frequency analysis page of wikipedia. The result looks like this.

Result of WordCloud

Pretty cool eh? Anyway this was just something I threw together in an hour or two, hopefully it’s useful to someone out there. Let me know what ya think!


Dynamic PDF Generator

I recently had a requirment where a list of any kind of sObject could be given to a visualforce page, and it should spit out a PDF report of those objects. The fields returned could possibly be defined by a field set, passed in the URL directly, or I could get passed nothing and would just have to query for all fields on the object. It was decided that the best course of action was to write a nice re-useable apex class that can handle these requirements and use the visualforce renderas attribute to make it easy to generate printable reports. You can easily rig up a custom button on a list view to get the checked elements and pass them into the exporter page as well to basically allow exporting from any list view. The following is the first draft of said functionality.

4/03/2013 EDIT: Thanks to a good tip by Cal Smith I changed how the visualforce page outputs the content and it seems to be much faster and probably safer too. I also included two new params for the exporter. You can now provided a field to order by by specifying order_by in the url. Also, if you want the records returned in the same order the ids were provided in the url you can specify return_in_order=true. This is probably slow on large data sets but in cases where your users may have put records in the order they want you can then pass the ids in that order to the controller and the PDF will be generated with the same order. Kinda a nice feature I thought.

4/05/2013 EDIT: I added the force_download param and filename params to allow you to force the user to download the file and specify a name for the downloaded file instead of letting them view it in their browser. Not totally sure why someone might want this, but it was a request I got and it was fairly easy to add.

The Apex Class

/*
Name: queryGenerator
Author: Daniel Llewellyn
Date: 4/02/2013
Description: Meant to be invoked by a visualforce page. This class can take url params to query for a list of any kind of
             sObject. Those sObjects can then be used to power user interface elements. By passing in a list of Ids and an
             option field set, this class is able to determine the object type, find a matching field set, use a default field set
             or if none is specified query for all fields defined on the object. Useful for generating lists of sObjects when the type
             of object, and desired fields is not known ahead of time.

URL Params:
name            type         req      description
--------------------------------------------------------------------------------------------------------------------
ids:            csv of ids   true     a list of sObject Ids seperated by commas. The objects to include in the return query
fields:         string       false    a comma seperated list of fields to include. Takes precidense over fieldSet if specified.
fieldSet:       string       false    the name of a fieldset to use to determine which fields to include. Used if fields param not specified. If both are null, all fields are queried.
order_by        string       false    the name of a field on the object to order the results by
return_in_order boolean      false    should the results be returned in the same order they were provided in the URL? Overrides the order_by param if set to true.
force_download  boolean      false    should the PDF file be forced to donwload instead of displayed in the browser window?
filename        string       false    the name to assign to the downloaded file if force_download is set to true. Defaults to object label + ' report.pdf' Do not include .pdf. It is appened automatically.

Gotchas:
Due to the way the query is built (filtering by a list of Ids) you can only get probably about 500 records max before the query length gets too long.
Shouldn't be a big deal though, a report of more than 500 records starts to get kind of meaningless most of the time. It will attempt to gracefully handle
any errors and return them nicely to the user.

*/

public class queryGenerator
{
    //Params. Can be used in your visualforce page to customize report data.
    public Schema.SObjectType OBJECT_SCHEMA_TYPE{get;set;}
    public string OBJECT_TYPE_NAME{get;set;}
    public string OBJECT_TYPE_LABEL{get;set;}
    public string ORDER_BY{get;set;}
    public boolean RETURN_IN_ORDER{get;set;}
    public list<string> OBJECT_FIELDS{get;set;}
    public map<string,string> OBJECT_FIELD_MAP{get;set;}
    public list<id> OBJECT_IDS{get;set;}
    public list<sobject> OBJECTS{get;set;}
    public integer RECORD_COUNT{get { 
        return objects.size();
    }set;}

    public queryGenerator(){

        try
        {

            OBJECT_FIELD_MAP = new map<string,string>();

            //get the list of ids to query for. We expect them to come in a url param called ids, and they should be
            //comma seperated. Since we know that, we can split them based on , to get a list of ids.
            if(ApexPages.currentPage().getParameters().get('ids') == null)
            {
                throw new applicationException('Please include a list of a comma seperated ids to query for in the url by specifying ?ids=id1,id2,id3 etc');
            }
            OBJECT_IDS = ApexPages.currentPage().getParameters().get('ids').split(',');

            //use the ids getSObjecType method to figure out what kind of objects these are we got passed. 
            OBJECT_SCHEMA_TYPE = OBJECT_IDS[0].getSObjectType(); 

            //caching describe results makes for faster iteration
            map<string,Schema.sObjectField> fieldMap = OBJECT_SCHEMA_TYPE.getDescribe().fields.getMap();

            for(Schema.SObjectField field : fieldMap.values())
            {
                OBJECT_FIELD_MAP.put(field.getDescribe().getName(),field.getDescribe().getLabel());
            }

            //get the name of this object type
            OBJECT_TYPE_NAME = OBJECT_SCHEMA_TYPE.getDescribe().getName();

            OBJECT_TYPE_LABEL = OBJECT_SCHEMA_TYPE.getDescribe().getLabel();

            //get the list of fields we will query for and display
            if(ApexPages.currentPage().getParameters().get('fields') == null)
            {
                OBJECT_FIELDS = getObjectQueryFields(OBJECT_SCHEMA_TYPE, ApexPages.currentPage().getParameters().get('fieldset'));    
            }
            else
            {
                OBJECT_FIELDS = ApexPages.currentPage().getParameters().get('fields').split(',');
            }
            //set the order by statment. If no order by is specified, just tell it to order by Id to prevent a syntax error
            if(ApexPages.currentPage().getParameters().get('order_by') != null)
            {
                ORDER_BY= ApexPages.currentPage().getParameters().get('order_by');   
            }
            else
            {
                ORDER_BY = 'Id';
            }            

            RETURN_IN_ORDER = false;
            if(ApexPages.currentPage().getParameters().get('return_in_order') != null && ApexPages.currentPage().getParameters().get('return_in_order') == 'true')
            {
                RETURN_IN_ORDER = true;   
            }       

            OBJECTS = getSojects();   

            if(ApexPages.currentPage().getParameters().get('force_download') != null && ApexPages.currentPage().getParameters().get('force_download') == 'true') 
            {
                string fileName = 'Report of '+OBJECT_TYPE_LABEL+'.pdf';
                if(apexPages.currentPage().getParameters().get('filename') != null)
                {
                    fileName = apexPages.currentPage().getParameters().get('filename') +'.pdf';
                }
                Apexpages.currentPage().getHeaders().put('content-disposition', 'attachemnt; filename='+fileName);
            }    

        }
        catch(exception ex)
        {
            //catch and return errors. Most often will happen from a bad Id of fieldname being passed in.
            system.debug('\n\n\n------Error occured during page init!');
            system.debug(ex.getMessage() + ' on line ' + ex.getLineNumber());
            ApexPages.addmessage(new ApexPages.message(ApexPages.severity.WARNING,ex.getMessage() + ' on line ' + ex.getLineNumber()));

        }
    } 
    //this method will be invoked by a visualforce page. It will determine the sObject
    //type by examining the Ids passed in the ids param. Once it knows the object type it will
    //then attempt to find a locate the specified fieldset if one was passed in the URL. If no fieldset
    //was provided, then it will query for all sObject fields.

    public list<sobject> getSojects()
    {
        list<sobject> queryResults;

        //lets get a list of fields to query for by using the getObjectQueryFields method. We will pass in the object type
        //and the fieldset url param (which may be null, but that doesnt matter).
        string queryFields = listToCsv(OBJECT_FIELDS);

        //build this query string
        string queryString = 'select ' + queryFields + ' from ' + OBJECT_TYPE_NAME + ' where id in :OBJECT_IDS ORDER BY '+ORDER_BY;

        if(queryString.length() > 10000)
        {
            throw new applicationException('Query too long ('+queryString.length()+'). Please reduce the number of ids or reduce the number of fields queried for to get the length under 10,000');
        }
        //run the query.
        queryResults = database.query(queryString);

        if(RETURN_IN_ORDER)
        {
            queryResults = sortQueryInOrder(OBJECT_IDS, queryResults);
        }
        return queryResults;
    }

    //takes the list of sObjects and sorts them in the order they were passed in the URL. This allows for a custom sorting order to be passed in
    //without having to make use of the SOQL order by clause which may not be robust enough to handle the types of sorts desired.
    //WARNING THIS IS PROBABLY PRETTY DAMN SLOW!
    public list<sObject> sortQueryInOrder(list<id> objectOrder, list<sObject> objects)
    {
        map<id,sObject> objectMap = new map<id,sObject>();
        list<sObject> sortedList = new list<sObject>();
        for(sObject obj : objects)
        {
            objectMap.put((id) obj.get('id'), obj);
        }

        for(id objId : objectOrder)
        {
            sortedList.add(objectMap.get(objId));
        }
        return sortedList;

    }
    //takes an sObject type and optional name of a fieldset for that sObject type (can be null). Returns a list
    //of strings of fields to query for either based on the fieldset, or by finding all sObject fields if no fieldSet
    //is specified, or a matching fieldSet can not be found.
    public list<string> getObjectQueryFields(Schema.SObjectType objectType, string fieldSetName)
    {
        set<string> fields = new set<string>();
        Schema.FieldSet thisFieldSet = null;

        //first any fieldsets that are defined for this object type. It is possible this might be empty.
        Map<String, Schema.FieldSet> fieldSetMap = objectType.getDescribe().fieldSets.getMap();  

        //check to see if the user passed in a field set, and if so, does it exist? 
        //if so, use that fieldset. Otherwise, use all fields on the object
        if(fieldSetName != null && fieldSetMap.containsKey(fieldSetName))
        {
            thisFieldSet = fieldSetMap.get(fieldSetName);
            //now that we know what field set we are using we have to iterate over it and get it feildsetmembers
            //and add each field into the query string.
            for(Schema.FieldSetMember f : thisFieldSet.getFields())
            {
                fields.add(f.getFieldPath());
            }            
        }             

        //if there are no field sets defined for this object, then lets just query for all the fields
        else
        {
            fields = getObjectFields(objectType);            
        }

        //return our variable that contains a properly comma seperated list of all the fields to query for.
        list<string> fieldList = new list<string>();
        fieldList.addAll(fields);
        return fieldList;
    }

    //a simple possibly overly abstracted method to get the fields on an object
    public set<string> getObjectFields(Schema.SObjectType objectType)
    {
        return objectType.getDescribe().fields.getMap().keySet();
    }

    //takes a list of strings and returns them in a comma seperated fashion, suitable for feeding into a query.
    public string listToCsv(list<string> stringList)
    {
        string itemList = '';
        for(string thisString : stringList)
        {
            itemList += thisString+',';
        }
        itemList=itemList.substring(0,itemList.length()-1);
        return itemList;
    }

    @isTest
    public static void testQueryGenerator()
    {
        //setup our test account
        Account testAccount = new Account();
        testAccount.name = 'My Test account';
        testAccount.billingStreet = '1234 Test Street';
        testAccount.billingState = 'NY';
        testAccount.billingPostalCode = '55555';
        testAccount.billingCountry = 'USA';

        insert testAccount;

        test.StartTest();

        PageReference pageRef = Page.exportPdf;
        Test.setCurrentPage(pageRef);

        //run it with no ids. It will come back with no records since it will error. Since the error gets caught
        //we don't need to try/catch here though.
        queryGenerator qg = new queryGenerator();

        //run test with nothing but ids specified. This will make it query for all fields
        ApexPages.currentPage().getParameters().put('ids', testAccount.id);        
        qg = new queryGenerator();

        //make sure it found our account
        system.assertEquals(1,qg.RECORD_COUNT);
        system.assertEquals(testAccount.name,(string) qg.OBJECTS[0].get('name'));

        ApexPages.currentPage().getParameters().put('fields', 'name,id,billingStreet');        
        qg = new queryGenerator();        
        //make sure it found our account
        system.assertEquals(1,qg.RECORD_COUNT);
        system.assertEquals(testAccount.billingStreet,(string) qg.OBJECTS[0].get('billingStreet'));

        ApexPages.currentPage().getParameters().put('order_by', 'name'); 
        ApexPages.currentPage().getParameters().put('return_in_order', 'true'); 
        ApexPages.currentPage().getParameters().put('force_download', 'true');
        ApexPages.currentPage().getParameters().put('filename', 'My PDF file');
        qg = new queryGenerator();       

    }
    class applicationException extends Exception {}
}

The ExportPDF visualforce Page

<apex:page controller="queryGenerator" renderAs="pdf"  standardStylesheets="false">
<head>
  <style>
    @page {
        size:landscape;
        margin : .5in;
        @top-center {
            content : element(header);
         }

        @bottom-left {
            content : element(footer);
        }

    }
    table
    {
        width:100%;
    }
    @bottom-left {
        content : element(footer);
    }
    div.footer {
        position : running(footer) ;
    }    
  </style> 
</head>
    <apex:pageMessages></apex:pageMessages>
    <h1>Report of {!OBJECT_TYPE_LABEL} ({!RECORD_COUNT} Records)</h1>

    <table>
        <tr>
            <apex:repeat value="{!OBJECT_FIELDS}" var="FieldLable">
                <apex:outputText><th>{!OBJECT_FIELD_MAP[FieldLable]}</th></apex:outputText>
            </apex:repeat>        
        </tr>

        <apex:repeat value="{!OBJECTS}" var="rec">
            <tr>
                <apex:repeat value="{!OBJECT_FIELDS}" var="FieldLable">
                    <apex:outputText><td>{!rec[FieldLable]}</td></apex:outputText>
                </apex:repeat>
            </tr>
        </apex:repeat>
    </table>

    <div class="footer">
    <apex:outputText value="The Date: {0,date,MMMMM dd, yyyy 'at' hh:mm a}" styleClass="footer" >
        <apex:param value="{!NOW()}" />
    </apex:outputText> 
    </div>    
</apex:page>

Sample List View Button

window.open('/apex/exportPdf?ids='+ {!GETRECORDIDS($ObjectType.YOUR_OBJECT_TYPE)}+'&fieldset=YOUR_FIELD_SET_NAME_HERE&order_by=name&return_in_order=false','1364931211178','width=700,height=500,toolbar=0,menubar=0,location=0,status=1,scrollbars=1,resizable=1,left=0,top=0')

You’ll need to replace the $ObjectType.YOUR_OBJECT_TYPE and the fieldset=YOUR_FIELD_SET_NAME_HERE in the list view button. Or you can just remove the fieldset part entirly, or replace it with a ‘fields’ attribute where you can specify a comma separated list of fields to query for. You’ll probably want to play with the formatting of the report a little but, but I’ll leave that as an exercise to the reader. Hopefully this helps someone out there.


Floating/Sticky Headers For Visualforce PageBlockTable

So this is it. This is going to be the definitive guide for how to get some floating headers on your Visualforce page block table. I know there are many approaches, lots of debates about how to do it, but what I’ve got here is likely the best, simplest way to do it. It’s a jQuery plugin that will make the headers of a page block table stick to the top of the tables parent div. Check out a demo here

http://xerointeractive-developer-edition.na9.force.com/partyForce/floatingHeaders

You can download the plugin here.

https://www.box.com/s/lr73ibecfvo4bi0qzzbn

Upload it as a static resource, or just copy paste the contents into your visualforce page. Either way is fine. Also, in your visualforce page you’ll need to include the css class .floatingStyle and set it’s position to relative. So just

<style>      
.floatingStyle 
{ 
    position:relative; 
} 
</style>

To use it, simply put your pageblocktable inside a div or apex:outputpanel (with layout set to block). Give that container a height. Invoke the plugin on the table either by class or id. So if my pageblock tables had the styleClass of ‘floatingHeaderTable’ I could invoke it this way.

    <script>
    $(document).ready(function() {
        $('.floatingHeaderTable').vfFloatingHeaders();
    });
    </script> 

and that’s it. You are good to go. Here is a full sample page.

Visualforce Page

<apex:page controller="floatingHeadersController">

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
    <script src="{!URLFOR($Resource.jquery_vfFloatingHeaders)}"></script>

    <style>
        .tableContainer
        {
            height:290px; 
            width: 100%;
            overflow: auto;
        }       
        .floatingStyle 
        { 
            position:relative; 
        } 
    </style>

    <script>
    $(document).ready(function() {
        $('.floatingHeaderTable').vfFloatingHeaders();
    });
    </script>   

    <apex:pageBlock >
        <apex:outputPanel styleClass="tableContainer" layout="block">
            <apex:pageBlockTable value="{!contactList}" var="item" title="Contact List" styleclass="floatingHeaderTable" >
                <apex:column value="{!item.firstname}"/>
                <apex:column value="{!item.lastname}"/>
                <apex:column value="{!item.email}"/>
                <apex:column value="{!item.phone}"/>
            </apex:pageBlockTable>
        </apex:outputPanel>
    </apex:pageBlock>
</apex:page>

Apex Class

public class floatingHeadersController 
{
    public list<contact> contactList
    {
        get
        {
          if (contactList == null)
          {
              contactList = [select firstname, lastname, email, phone from contact];
          }  
          return contactList;
        }
        set;
    }
}

I should totally mention that the bulk of this code came from the blog at

http://rajputyh.blogspot.com/2011/12/floatingfixed-table-header-in-html-page.html

I just wrapped it up and modified it a bit to work with page block tables since their table headers didn’t originally have ids, and put it into a nifty plugin.


New Cloudspokes Entry – Salesforce Global Search

Hey everyone,

I’m just wrapping up my entry for the CloudSpokes Salesforce global search utility challenge, codename Sherlock. I’m fairly pleased with how it turned out and figured I’d post the video for all to enjoy. I can’t post the code since this is for a challenge, but feel free to ask any questions about it.

http://www.screencast.com/t/GrYnfBlJFM

Features:

  • Fast operation using JS remoting. No page reloads. Ajax style searching.
  • Search any objects. Not limited to a single object type search
  • Smart formatting adjusts search result display based on data available about the object
  • Easy customization using callbacks and jQuery binds
  • Flexible, can be modified to suit many different requirements
  • Easy to use jQuery plugin based installation
  • Efficient each search consumes only one database query
  • Reliable return type makes processing search results easy
  • CSS based design makes styling results quick and easy
  • Structured namespaced code means smaller memory footprint and less chance of collisions
  • Deployable package makes for easy install
  • Over 90% code coverage with asserts provides assurance of functionality

You can check out the full documentation and feature list here

https://docs.google.com/document/d/17-SUja_SO_Enhh8LrjzDMB7VIX6S87TmPr9yBW5z-yo/edit

I don’t know why exactly they wanted such a thing, but it was certainly fun to write!


Apex Captcha with Javascript Remoting and jQuery

So at one time or another, we’ll likely all have to create a public facing form to collect data. We will also find about 2 seconds afterwards that it is getting spammed to hell. To stop the flood of crap, we have reCaptcha. An awesome little utility that will prevent bots from submitting forms. You already know what captcha is though, that’s probably how you found this post, by googling for apex and captcha. First off, there is already an awesome post on how to do with by Ron Hess (Here), but his approach is a bit complicated, and visualforce heavy. Of course being kind of anti visualforce, and the complexities of properties and all that, I made my own little approach. So here we go.

This is assuming you already signed up with reCaptcha. You can go here and sign up for recaptcha (yes you can just enter force.com as the domain)
After that, course add an entry for google to your remote sites in the admin setup under security. Disable protocol security.
Then create your visualforce page, and apex class. I called my class utilities, since this is kind of a re-usable function and I wanted to keep it generic.

Now put this crap in your controller. Also, your controller needs to be global (to use javascript/apex remoting)

@RemoteAction
    global static boolean validCaptcha(string challenge, string response)
    {
      boolean correctResponse = false;
      string secret = 'your recaptcha secret key here. Maybe make this into a custom setting?';
      string publicKey = 'your recaptcha public key here. Maybe make this into a custom setting?';
      string baseUrl = 'http://www.google.com/recaptcha/api/verify'; 

      string body ='privatekey='+ secret +  '&remoteip=' + remoteHost() + '&challenge=' + challenge + '&response=' + response + '&error=incorrect-captcha-sol';
      
      HttpRequest req = new HttpRequest();   
      req.setEndpoint( baseUrl );
      req.setMethod('POST');
      req.setBody ( body);
      try 
      {
        Http http = new Http();
        HttpResponse captchaResponse = http.send(req);
        System.debug('response: '+ captchaResponse);
        System.debug('body: '+ captchaResponse.getBody());
        if ( captchaResponse != null ) 
        {  
            correctResponse = ( captchaResponse.getBody().contains('true') );
        }          
       
      } 
      catch( System.Exception e) 
      {
         System.debug('ERROR: '+ e);
      }                             
      return correctResponse;
    }

    global static string remoteHost() 
    { 
        string ret = '127.0.0.1';
        // also could use x-original-remote-host 
        try
        {
            map<string , string> hdrs = ApexPages.currentPage().getHeaders();
            if ( hdrs.get('x-original-remote-addr') != null)
            {
                ret =  hdrs.get('x-original-remote-addr');
            }
            else if ( hdrs.get('X-Salesforce-SIP') != null)
            {   
                ret =  hdrs.get('X-Salesforce-SIP');
            }
        }
        catch(exception e)
        {
        
        }
        return ret;
    }

Ok, great, now your controller is ready. You just need to pass the right info and it will tell you if it’s right or wrong. Lets get a visualforce page set up to do that.

<apex:page controller="utilities" standardStylesheets="false" sidebar="false"  >

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" />


<script>
$(function() {

    $( "#validateButton" ).click(function(){
        
        validCaptcha(function(valid){
            if(valid)
            {
                $('#validationResultDiv').html('Valid Captcha!');
                
                //Do whatever here now that we know the captcha is good.
            }
            else
            {
                $('#validationResultDiv').html('Invalid Captcha Entered');
            }
           
        });           
    });
});

function validCaptcha(callback)
{
    var challenge = document.getElementById('recaptcha_challenge_field').value;
    var response = document.getElementById('recaptcha_response_field').value;

    utilities.validCaptcha(challenge,response, function(result, event)
    {
        if(event.status)
        {
           callback(result);
        }
    }, {escape:true});
}

</script>

<div id="captchaEnter" title="Form Submission Validation">
    <center>
    <script type="text/javascript" src="https://www.google.com/recaptcha/api/challenge?k=YOUR PUBLIC KEY GOES HERE DONT FORGET IT"></script>
    <noscript>
       https://www.google.com/recaptcha/api/noscript?k=YOUR_PUBLIC_KEY
     </noscript>  
     <div id="validationResultDiv"></div>   
     <button id="validateButton" class="inline">Submit</button>
       
     </center>
</div>


</apex:page>

Boom! Just that easy. Hook up an event handler to the submit button that runs the validCaptcha function. It will get the proper values, and send them to the apex class, which sends them to reCaptcha to verify. Once an answer comes back, it is passed into the callback function, which the can run whatever action you require. Don’t forget to replace the place holder public key in the script line above. Have fun!



Salesforce jQuery Calendar

Over the last year there has been a lot of people excited about my Salesforce jQuery calendar. Problem is, for one the code isn’t available due to a lack of hosting. Problem two is that it sucks. It uses some goofy visualforce page to pass information off the the apex class, and it can only query against one type of object. Overall, it’s pretty lame and not a good sample of the kind of work that is possible these days. So I am rebuilding it. In fact, I already have the core up and running. But now I want to know what kind of features you guys are interested in. Do you want a super bare bones easy to understand release, or do we want a little more robust full featured kind of thing? Let me know in the comments what you’d like to see in a new Salesforce calendar, and I’ll see what I can do.

For those who just want my basic functional super skeletal framework I’ll be releasing it sometime tomorrow. I need to clean up a few little things, and I’ll probably release it as an unmanaged package for easy install, and I’ll host the code on my new box.net account.


Cloudspokes Challenge – Open Social Toolkit Voting App

Hey all,
Another week another CloudSpokes challenge done. This one is the open social toolkit, voting application. It allows users to create topics to vote on, lets other users vote on those things, and have discussions about them. Integrated with facebook, and totally force.com based. I used jQuery mobile here to make sure it works on phones and iPads and whatnot, and the super awesome force.com platform for hosting and schema. Really a match made in heaven if you ask me.

You can see the demo app here

See the videos of it in action too!
Interface and Front end Video
Backend and Schema Video

I’ll be doing a post later about the nifty facebook integration, cause to me, that is the coolest part.


Cloudspokes Event Stamp Manager

Hey all,

So I just wrapped up my entry for the CloudSpokes Virtual Stamp Manager. This one was a lot of fun. One of the more complex data models I’ve had to set up for a challenge but overall I think the architecture I came up with was pretty solid. The way it’s built allows for easy reporting and good logging of who made what code when. It even has the bonus feature of some basic QR code support (if enabled in the event).

This was also my first app using the jQuery mobile framework. It took a bit of getting used to, but honestly I really like it. It takes a lot of the hard work about ajax stuff out of the equation and lets you focus on building quality apps. No worry about the loading screens or data formatting, size of the content, or even dealing with the whole hash mark bookmark back button mess.

Anyway, enough of my yapping. Check out the video
here.

Or if you want to, you can play with the app here.


Art Meets Text Mashup Cloudspokes challenge

Well, just turned in my entry for the titular challenge. Was right down to the wire too. I felt like I was pretty pressed for time here, pretty much started working as soon as I heard about and just stopped tweaking it now. This one was a lot of fun to build, and I learned a fair amount about data storage, base64 and had my first run in with HTML5 canvas. I think I’m going to enjoy playing with that in the future. The lesson here was ‘keep it simple stupid’. At one point I was going to create a proxy that got an image and used javascript to convert it to base64 on the fly to overcome some security issue with canvas (canvas has the same kind of security as ajax, where cross domain stuff can cause problems, in particular if you use load an image into the canvas from a remote host, then attempt to base64 encode the canvas, that doesn’t work). Of course when I stopped being stupid for like 5 minutes I realized the file data was already in force.com I could just write a method to extract the base64 encoded content using the encodingUtil class. Anyway, I know this sound more like a rant than anything useful, so I’ll just shut up and post the video.

http://xerointeractive.com/Art_Mashup_Challenge.swf

Also, if you want to play with it it is here

http://xerointeractive-developer-edition.na9.force.com/partyForce/artMash

though it may be down due to being over the bandwidth limit.


Apex Javascript remoting. Web developers Rejoice!

Ok, so it is probably no secret, I am in fact not an awesome object oriented programmer. Apex does not come very naturally for me (although it is getting better) but I often found the integration between VisualForce and Apex to be clunky. Too many properties, weird methods that you don’t invoke by name (when I call data, it’s actually calling getData(), wtf) and other strange behaviors that make little sense to me. So when I head about javascript remoting, which allowed tight integration between javascript and apex, I was totally stoked. No more having to have actionmethods to try and invoke controller code and dealing with properties and all that jazz. For those of us with maybe not so much java/C# background but a fair amount of web development, javascript remoting feels very natural. Once you pair it with jQuery, life just keeps getting better.

Say for example I want to create a signup form to host on a Salesforce site. I can create a form just like I always would in HTML and javascript, then create an apex method that can take the form data and do whatever. The call is asynchronous so it lends itself well to being all ajax-y and cool. To get you started, here is a sample of a form that is really cool because if I want to capture more data, all I need to do is add the fields to the form (that have fieldnames that match those on my contact). My jQuery and apex are smart enough to figure out the rest on their own.

Here is the page code.

<apex:page standardStylesheets="false" expires="1" cache="false" showHeader="false" sidebar="false" id="page" controller="contactClasses">

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script src="http://jquery.bassistance.de/validate/jquery.validate.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/jquery-ui.min.js"></script>

<link rel="stylesheet" type="text/css" media="screen" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/cupertino/jquery-ui.css" />
<link rel="stylesheet" type="text/css" media="screen" href="http://jquery-ui.googlecode.com/svn/branches/dev/themes/base/ui.button.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/jquery-ui.min.js" type="text/javascript"></script>

<script>
  
function signUp()
{
    if($("#signUpForm").valid())
    {
        $('#submit').attr('disabled', 'disabled');
        var str = $("form").serialize();
    
        contactClasses.registerUser(str , function(contactResult, event)
        {                
            if (event.status) 
            {
                 console.log(contactResult);
                if(contactResult.success == 'true' || contactResult.success == true)
                {
                      $('#statusBoxText').html('Thanks for registering!.' +contactResult.data);
                }
                else
                {
                     $('#statusBoxText').html('Looks like there was a problem registering you.' +contactResult.data);
                }
                
                $('#submit').attr('disabled', null);                
            } 
            
    
        }, {escape:true});                                         
    }
}

    
$().ready(function() {
    // validate the comment form when it is submitted
    $("#signUpForm").validate();
    
    //style the button
    $(".button").button();

    $( "#statusBox" ).dialog({
        autoOpen: false,
        show: "blind",
        hide: "explode"            
    });      
});
</script>
  <style type="text/css">
body {
    font-size: 62.5%;
}
label {
    display: inline-block;
    width: 100px;
}
legend {
    padding: 0.5em;
    margin-left:auto;
    margin-right:auto;
}
fieldset fieldset label {
    display: block;
}
#signUpForm {
    width: 500px;
    margin-left:auto;
    margin-right:auto;
    margin-top:25px;
}
#signUpForm label {
    width: 250px;
}
#signUpForm label.error, #commentForm button.submit {
    margin-left: 253px;
}
#signUpForm {
    width: 670px;
}
#signUpForm label.error {
    margin-left: 250px;
    width: auto;
    display: block;
}
</style>
  <form class="cmxform"  id="signUpForm" >
    <fieldset class="ui-widget ui-widget-content ui-corner-all">
      <legend class="ui-widget ui-widget-header ui-corner-all">Please provide your information in the form below to sign up</legend>
      <p>
        <label>Firstname</label>
        <input name="Firstname" id="Firstname" type="text" maxlegth="15" size="60" class="required"  />
      </p>
      <p>
        <label>Last Name</label>
        <input name="Lastname" id="Lastname" type="text" size="60"  class="required" />
      </p>
      <p>
        <label>Email</label>
        <input name="Email" id="Email"  type="text" size="60"  class="required email"/>
      </p>
      <p>
        <label>Phone</label>
        <input name="Phone" id="Phone" type="text" size="60" maxlength="12" alt="phone" class="required phone"/>
      </p>
      <p>
        <label>Mobile Phone</label>
        <input name="MobilePhone" id="MobilePhone" type="text" size="60" maxlength="12" alt="phone" class="required phone" />
      </p>
      <p>
        <label>Street Address</label>
        <input name="MailingStreet" id="MailingStreet" type="text" size="60" class="required" />
      </p>
      <p>
        <label>City</label>
        <input name="MailingCity" id="MailingCity" type="text" size="60" class="required" />
      </p>
      <p>
        <label>State</label>
        <input name="MailingState" id="MailingState" type="text" size="60" class="required" minlength="2" maxlength="2" />
      </p>
      <p>
        <label>Zip Code</label>
        <input name="MailingPostalCode" id="MailingPostalCode" type="text" size="60" class="required number" minlength="5" maxlength="5" />
      </p>
      <p>
        <label>Gender</label>
        <select name="Gender__c">
          <option>Male</option>
          <option>Female</option>
        </select>
      </p>
      <p>
        <label>Birth Date</label>
        <select id="Month" name="Month">
          <option value="0">--Select One--</option>
          <option value="1">January</option>
          <option value="2">February</option>
          <option value="3">March</option>
          <option value="4">April</option>
          <option value="5">May</option>
          <option value="6">June</option>
          <option value="7">July</option>
          <option value="8">August</option>
          <option value="9">September</option>
          <option value="10">October</option>
          <option value="11">November</option>
          <option value="12">December</option>
        </select>
        <select id="Day" name="Day">
          <option value="0">--Select One--</option>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
          <option value="4">4</option>
          <option value="5">5</option>
          <option value="6">6</option>
          <option value="7">7</option>
          <option value="8">8</option>
          <option value="9">9</option>
          <option value="10">10</option>
          <option value="11">11</option>
          <option value="12">12</option>
          <option value="13">13</option>
          <option value="14">14</option>
          <option value="15">15</option>
          <option value="16">16</option>
          <option value="17">17</option>
          <option value="18">18</option>
          <option value="19">19</option>
          <option value="20">20</option>
          <option value="21">21</option>
          <option value="22">22</option>
          <option value="23">23</option>
          <option value="24">24</option>
          <option value="25">25</option>
          <option value="26">26</option>
          <option value="27">27</option>
          <option value="28">28</option>
          <option value="29">29</option>
          <option value="30">30</option>
          <option value="31">31</option>
        </select>
        <select id="Year" name="Year">
          <option value="0">--Select One--</option>
          <option value="1926">1926</option>
          <option value="1927">1927</option>
          <option value="1928">1928</option>
          <option value="1929">1929</option>
          <option value="1930">1930</option>
          <option value="1931">1931</option>
          <option value="1932">1932</option>
          <option value="1933">1933</option>
          <option value="1934">1934</option>
          <option value="1935">1935</option>
          <option value="1936">1936</option>
          <option value="1937">1937</option>
          <option value="1938">1938</option>
          <option value="1939">1939</option>
          <option value="1940">1940</option>
          <option value="1941">1941</option>
          <option value="1942">1942</option>
          <option value="1943">1943</option>
          <option value="1944">1944</option>
          <option value="1945">1945</option>
          <option value="1946">1946</option>
          <option value="1947">1947</option>
          <option value="1948">1948</option>
          <option value="1949">1949</option>
          <option value="1950">1950</option>
          <option value="1951">1951</option>
          <option value="1952">1952</option>
          <option value="1953">1953</option>
          <option value="1954">1954</option>
          <option value="1955">1955</option>
          <option value="1956">1956</option>
          <option value="1957">1957</option>
          <option value="1958">1958</option>
          <option value="1959">1959</option>
          <option value="1960">1960</option>
          <option value="1961">1961</option>
          <option value="1962">1962</option>
          <option value="1963">1963</option>
          <option value="1964">1964</option>
          <option value="1965">1965</option>
          <option value="1966">1966</option>
          <option value="1967">1967</option>
          <option value="1968">1968</option>
          <option value="1969">1969</option>
          <option value="1970">1970</option>
          <option value="1971">1971</option>
          <option value="1972">1972</option>
          <option value="1973">1973</option>
          <option value="1974">1974</option>
          <option value="1975">1975</option>
          <option value="1976">1976</option>
          <option value="1977">1977</option>
          <option value="1978">1978</option>
          <option value="1979">1979</option>
          <option value="1980">1980</option>
          <option value="1981">1981</option>
          <option value="1982">1982</option>
          <option value="1983">1983</option>
          <option value="1984">1984</option>
          <option value="1985">1985</option>
          <option value="1986">1986</option>
          <option value="1987">1987</option>
          <option value="1988">1988</option>
          <option value="1989">1989</option>
          <option value="1990">1990</option>
          <option value="1991">1991</option>
          <option value="1992">1992</option>
          <option value="1993">1993</option>
          <option value="1994">1994</option>
          <option value="1995">1995</option>
          <option value="1996">1996</option>
          <option value="1997">1997</option>
          <option value="1998">1998</option>
          <option value="1999">1999</option>
          <option value="2000">2000</option>
          <option value="2001">2001</option>
          <option value="2002">2002</option>
          <option value="2003">2003</option>
          <option value="2004">2004</option>
          <option value="2005">2005</option>
          <option value="2006">2006</option>
          <option value="2007">2007</option>
        </select>
      </p>
      <p>
        <input type="button" onClick="signUp();" value="Create Account" class="button" />
 
      </p>
    </fieldset>
  </form>

  
    <div id="statusBox" title="Signup Complete!">
        <div id="statusBoxText"></div>
    </div>
</apex:page>

And the controller

global class contactClasses
{   
    global class remoteObject
    {
        public boolean success{get;set;}
        public string message{get;set;}
        public string data{get;set;}
        public list<sObject> sObjects {get;set;} 
    }

    @remoteAction
    global static remoteObject registerUser(string dataString)
    {
        remoteObject returnObj = new remoteObject();
        returnObj.success = true;
        returnObj.message = 'Member Created';
        returnObj.data = 'Booya';
        
        try
        {
            map<string,string> formData = deserializeString(dataString);
            list<Contact> contacts = new list<Contact>();
            Account thisAccount = new Account();
            thisAccount.name = formData.get('accountname');
            insert thisAccount;            
                        
            Contact thisContact = new Contact();
            thisContact.AccountId = thisAccount.id;

            
            for(String fieldName : formData.keySet())
            {
                try
                {
                    thisContact.put(fieldName,formData.get(fieldName)); 
                }
                catch(Exception e)
                {
                    system.debug('Field: ' +fieldName+' does not exist on contact');
                }          
            }
            contacts.add(thisContact);
            
            insert thisContact;
            returnObj.data = thisContact.id;
            returnObj.sObjects = contacts;
        }
        catch(exception e)
        {
            returnObj.success = false;
            returnObj.data = e.getMessage();
        }
        
        
        return returnObj;       
    }

    public static Map<string,string> deserializeString(String argString)
    {     
        string[] params = argString.split('&');
        map<String,String>  formParams = new map<String,String>();
        for(string p : params)
        {        
            formParams.put(EncodingUtil.urlDecode(p.substring(0,p.indexOf('=')),'UTF-8'),EncodingUtil.urlDecode(p.substring(p.indexOf('=')+1,p.length()),'UTF-8'));    
        }        
        return formParams;
    }  
            
    public class customException extends Exception {}
}

So what’s happening here? Well in the page we create a form and style it with jQuery themes. Then we apply some validations using a nifty plugin to make sure the users don’t enter trash data. Nothing too special there. Then we move into where it gets fun. Once you click the submit button, your information is validated. If the validation passes, the entire form gets serialized, meaning all the values get turned into a string. That string is passed as a single argument to the controller using javascript remoting. The string is received and turned into a map by my neato deserializeString method (if there is a built in apex method to do this, I don’t know about it).

So once that method runs we have a map of field names to field values (just like how the original form was). We simply iterate over the values in the map, and attempt to add them to a contact object. If it errors, that means that field is either of the wrong type, or that it doesn’t exist on the contact at all (now you see why it is important to name your fields exactly as they appear on the contact object). Once the iteration is done, we insert the contact.

The last part is returning the data. I created a simple data type called a remoteObject. It’s just an object that contains a boolean success key, a key to hold a message, a key to contain some extra data, and a key that can contain a list of any kind of sObject. The great thing here is that when it gets returned, javascript knows how to handle it. It is able to access the keys, iterate over the sObjects, all that. The only weird thing is the boolean can’t seem to make up it’s mind sometimes if it’s an actual boolean value, or a string that contains true or false. I just test for both.

In your callback function of the remoting call you can evaluate the results and do whatever you like. In this case, I display a simple dialog confirming the results. There you have it, a process that feels much more natural for those of us who may not quite dig the standard VF/Apex design pattern. It’s also fast as hell, like probably faster than the ‘regular’ approach. I’m pretty sure I’m never going to write a a page the old way again.

Anyway, hopefully this helps some people get on their way with javascript remoting. The tools and examples here should make things a lot easier. Especially the trick about serializing a form, then deserializing it on the other end. No need to declare each parameter and do all that painful evaluation for each variable. I know I am digging it anyway 😛


Correct me if I’m wrong…

But it seems to me I may have found what I would consider to be a bug in Salesforce custom components. When you create a component (a reusable piece of code that can be put into visualforce pages) you can specify attributes it accepts. Basically data you can give to the component when you call it, that it will need to function properly. If you had a component to make a phone call, you might need to pass it a phone number as an attribute (also known in other languages as a parameter or argument). Of course not every attribute may be required every time (maybe you don’t need to include the country code every time in your component that makes calls). So then you can set a default value. Which is a value that will be used if when called, one is not provided. These are really handy because you can make many parts of your component configurable, without having to specify each and every option every time (it’s nice to have the option of setting some obscure font color, but if you don’t care its just extra work. Nice to have a default).

Here is where it gets a little weird. When you call your component (likely from a visualforce page) you pass in the attributes for it to use. However, those attributes may themselves be variable (say you wanted to pass in the current time, you cannot hard code that, you’d need a variable that evaluates at run time). If you have a default value set on an attribute, you cannot pass in a variable. If you do not have a default you can. If you try, you’ll get the error

‘Error: Literal value is required for attribute’

Basically saying, it doesn’t want to evaluate the variable before passing it along to the component. You have to remove the default value from the apex:attribute tag. This is a bummer because say you want to pass in a variable, but have no idea if that variable will actually contain a value (if you are reading a contacts phone number to send, but no phone number is on that record, you just pass along an empty string). So your component can end up running with a null value where it may be expecting a number or string. This can cause issues. You could probably manually test for a null in your attribute in the component and assign a value if it is, but that is extra work. I see no reason for this behavior, but then again I’m dumb as a box of rocks. Anyway, I just figured I’d write this because I know I spent a while trying to figure out the fix. So long story story short, if attempting to pass a dynamic value to your component, your must not have a default value attribute in the attribute tag.

For those curious, below is the code I was working on (some data changed, but structure remains the same). You’ll see that some of my attributes do not have default values, that is because they may be dynamic in some instances.

Visualforce Page

<apex:page standardController="Contact">
    <c:vf_gauge minVal="0"
        maxVal="0"
        currentVal="{!contact.warnings__c}" 
        backgroundColor="blue"
        fontColor="red"
        title="How close to banning"
        link="http://www.google.com"
        animate="false"/>
</apex:page>

Component

<apex:component >
   
    <apex:attribute name="minVal" type="string" description="the minimum value" required="true"/>
    <apex:attribute name="maxVal" type="string" description="the maximum value" required="true" />
    <apex:attribute name="currentVal" type="string" description="the current value" required="true" />
    <apex:attribute name="backgroundColor" type="string" description="the color of the numeric readout box" default="black"/>
    <apex:attribute name="fontColor" type="string" description="the color of the font in the readout box" default="white"/>
    <apex:attribute name="title" type="string" description="A title for this gauge" default="My awesome gauge"/>
    <apex:attribute name="link" type="string" description="A url to go to when gauge is clicked" default="#"/>
    <apex:attribute name="animate" type="boolean" description="animate the line" default="true"/>
//other code

</apex:component>

Use jQuery to create resizeable visualforce pageblocktable

Hey all,
This is just a quickie tip. I am working on an application that has a visualforce pageblocktable that can have a ton of rows in it. It also isn’t the only thing on the page, and different users want to see different amounts of records at once. So I decided to make the whole table resizeble so the user can just pick how big they want it by clicking and dragging. jQuery of course has an awesome feature in their UI library for making a div resizeable. So I put my pageblocktable in a div and made that resizeable. Problem is the overflow wasn’t hidden so it wasn’t do me much good. So I changed the CSS to make the overflow hidden on the div, but now if the list was taller than your monitor you wouldn’t get scrollbars so you could only see the first few dozen records. The solution was to put another div inside the resizeable div, set that one’s overflow to auto in the css and make it resize as the parent one did. The final product looks like so.

><script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/jquery-ui.min.js"></script>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/ui-lightness/jquery-ui.css" type="text/css" media="all" />

<script type="text/javascript">
var jQuery = jQuery.noConflict();

jQuery(document).ready(function() {
    jQuery( "#checkInList" ).resizable({
       stop: function(event, ui) 
       { 
           console.log(ui.size.height); 
           jQuery( "#tableContainer" ).height(ui.size.height-10)
       }
    });
});
</script>

<div style="height:450px; overflow:hidden;" id="checkInList">
    <div style="height:440px; overflow:auto" id="tableContainer">            
        <apex:pageBlockTable value="{!payments}" var="payment"  id="fieldTable" style="width:100%;">
        
            <!--- apex:columns go here --->
        
        </apex:pageBlockTable>
    </div>
</div>    

You end up with a resizeable table that will have vertical only scrollbars. Of course you can expand this technique. Anything you put in the second div will be contained and get scrollsbars and resize to the height of the parent. If you want to set the width you can do that to, but most of the time it isn’t needed. Hope this helps someone.