Syntax Highlighter JS

Tuesday, December 11, 2012

A follow-up on google maps

UPDATE: As this example no longer works, Please see the updated version which can be found here.

I previously posted a Geocoder example for Google maps. Since that was posted, Salesforce has added Geolocation fields to their system.  I highly recommend that you use this new field type instead of the text field type in my original post.  This is so you can query by distance as seen in the example query from Salesforce.

This class is great for coding existing data and will eventually code new data coming into the system.  However, if you want a more real time coder (i.e. based off a trigger), you can see an example of that here.  Please be sure to read my comments at the bottom of that page for some improvements you might make if you use that class alone or in combination with my scheduled class.

If you are going to use scheduled classes (such as my Geocoder) in your system, then I highly recommend that you read this very interesting scheduled apex solution by Dan Appleman.

Once you have your data geocoded, you will most likely want to create your own Goolemaps mashup.  Here is a great GoogleMaps API tutorial to get you started.

You will want to look at the source to his example page to get you started with the JavaScript required to have a mash-up that paints some static points on the map.  Once you have a static example created, then you can explore how to integrate this with visual force to paint a dynamic list on the screen.

You will need to go ahead and create your apex controller for the page. Inside of that controller populate a list of objects from SOQL.  If want to do this based on distance, be sure to look back up at the example query in the first paragraph for an example.

For this example, I am going to query for 10 random accounts.  I am sure that your final code will be much more complex but I am focusing this post on the JavaScript and Visualforce required to make this work.

Please note that I am querying the Name and ID field in addtion to the BillingLong__c and BillingLat__c fields from the Geocoder example.  This is important because any data you want to present will need to be included in the query.  Here is the example controller:
public with sharing class TestController { public List<Account> AccountsList {get;set;} public TestController() { AccountsList = [SELECT ID, Name, BillingLat__c, BillingLong__c FROM Account LIMIT 10]; } // end constructor } // end class

Please note that you cannot use the standard Salesforce css with this example. Also, this example is using V3 of Google maps API which doesn't require a key. Here is the example page: <apex:page sidebar="false" showHeader="false" cache="false" controller="Test2Controller"> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> <title>Google Maps Example</title> <script src="http://maps.google.com/maps?file=api&amp;v=3&amp;sensor=false" type="text/javascript"></script> <script type="text/javascript"> // map and geocoder vars var map = null; var geocoder = null; var BaseMarkerOptions = null; // This function should be called onload of the body of the page function initialize() { if (GBrowserIsCompatible()) { var polys = []; var labels = []; // Display the map, with some controls and set the initial location map = new GMap2(document.getElementById("map")); map.addControl(new GLargeMapControl()); map.addControl(new GMapTypeControl()); // You will need to adjust this location to be the center coordinates and zoom level that you want // You can set this using visual force if needed map.setCenter(new GLatLng(32.5206608,-86.80249),5); geocoder = new GClientGeocoder(); // use our default marker options SetMarkerOptions(); // use visualforce to pull in the data from the SOQL query and use javascript to paint the points on the screen // the funciton call is: showAddress(html, Lat, Long) // HTML is the code to be displayed in pop-up when the point/marker is clicked. // Notice that JSENCODE is used to prevent javascript errors <apex:repeat value="{!AccountsList}" var="Account"> showAddress("{!JSENCODE(Account.Name)} <br /> <a href='../{!Account.id}' target='_blank'>Details</a>", "{!Account.BillingLat__c}", "{!Account.BillingLong__c}"); </apex:repeat> // enable the mouse wheel for zooming. Alot of mash-ups don't enable this for some reason. map.enableScrollWheelZoom(); send(null); } // display a warning if the browser was not compatible else { alert("Sorry, the Google Maps API is not compatible with this browser"); } // GBrowserIsCompatible() } // function initialize() // this function sets the icon and size to be used. It is using the defaults ATM but could be changed. function SetMarkerOptions() { var baseIcon = new GIcon(G_DEFAULT_ICON); baseIcon.shadow = "http://www.google.com/mapfiles/shadow50.png"; baseIcon.iconSize = new GSize(20, 34); baseIcon.shadowSize = new GSize(37, 34); baseIcon.iconAnchor = new GPoint(9, 34); baseIcon.infoWindowAnchor = new GPoint(9, 2); BaseMarkerOptions = { icon:baseIcon }; } // SetCustomMarkerOptions // this is the function that actually adds a marker on the map from the point (long/lat) data. function createMarker(point,html) { var marker = new GMarker(point); GEvent.addListener(marker, "click", function() { marker.openInfoWindowHtml(html); }); IncrementRenderedPoints(); return marker; } function showAddress(html, Lat, Long) { var myLatlng = new GLatLng(parseFloat(Lat), parseFloat(Long)); if (myLatlng != null) { var marker = new GMarker(myLatlng, BaseMarkerOptions); GEvent.addListener(marker, "click", function() { marker.openInfoWindowHtml(html); }); map.addOverlay(marker); } // check for null myLatlng } // end show address </script> </head> <body onload="initialize()" onunload="GUnload()"> <div id="map" style="width: 1024px; height: 768px"></div> <noscript><b>JavaScript must be enabled in order for you to use Google Maps.</b> However, it seems JavaScript is either disabled or not supported by your browser. To view Google Maps, enable JavaScript by changing your browser options, and then try again. </noscript> </body> </html> </apex:page>
And here is what the example looks like:

Monday, December 3, 2012

Visual Force component to generate SOQL where clause


 This is a screen shot of the the "Create new view" page in sales force (standard on any tab).



Having this as a Visual Force component could be used for all kinds of handy things.  In this post, I demonstrate this component and have it return a where clause for SOQL.   It dynamically lists the accessible and filterable fields on the object passed.  Operators are automatically set based on the field type from the schema.

Looking back up at that screen shot one has to wonder, why isn't the list sorted alphabetically?

I think that a possible reason is that the platform lacks a direct way to sort a selection options list.

With permission, I have used a class from Mohammad Usman to sort this control alphabetically.  I have added a test method in my code but his post was found here.

This is an early but working edition.  I hope to eventually fully mirror the native functionality but in the mean time, please be aware that this version has some limitations:
  • It does not have advanced criteria. Right now multiple rows / clauses are and only.
    • Updated on 1/7/2013 to include advanced filtering.
  • Excludes and Includes are not yet supported as operators.
  • DateTime fields (not date fields) must be entered in the SOQL format.
  • No date picker for Date fields. 
    • Updated on 3/25/2013 to include the JQuery UI date picker widget and to reformat the date behind the scenes for SOQL use.
  • Picklist values must already be known as I have yet to add code to list them in a pop-up window like salesforce does.
    • Updated on 1/22/2013 to include a basic look-up window for picklist and name fields.
Since controllers can't directly modify primitives, a wrapper class named SOQLQueryFilterResult is used to leverage the fact that they can modify objects.  This is how the resulting data will be returned.

Here is a sample apex controller:
public with sharing class testControllerExt { public SOQLQueryFilterResult FilterResult {get; set;} public SObject Object2Filter {get; set;} public testControllerExt () { FilterResult = new SOQLQueryFilterResult(); Account MyAccount = new Account(); Object2Filter = MyAccount; } // testControllerExt constructor } // main class
And here is a sample of how the component would be called with that controller:

The full code can be found here.

Sunday, November 18, 2012

Update to the Salesforce Attachment Downloader

I previously posted the "Salesforce Attachment Downloader" that allows you to download attachments and apex pages rendered as PDF, convert them to PDF (using open office).  You can also combine those files further - into two levels (say accounts and divisions) using the where clause in the SOQL search.

I have updated the program to fix a issue with PDF rotation in the combined files, added some more error checking / boxes, and added the ability to import and export the presets to a XML file.

The update can be found at the same location as the original program.

Contact me if you notice any other problems.

Monday, October 1, 2012

VF component for USPS address verification of leads

Previously, I posted an example class for accessing the USPS's e-commerce API to validate addresses for free.   I have updated that logic and created a component for easy use.   The code has been posted to github and can be found here.

Since the code has decent comments and is available above, this post will focus on an example of how to use the component in a page.

Please note that you will need an account with the USPS for this to work.  Please see my previous post for links to create the account and the user guide. 

Usage Notes:
  1. As this component contains its own form, you cannot place it inside of another form.
  2. Due to a lack of an action method on components, you will trigger the lookup by passing a valid USPS User code and reloading the page - when you are ready for the pop-up.
  3. This component uses the new mock HTTP response class and as such you won't be able to use it without Winter 13 of Salesforce.
  4. JQuery, JQuery UI (with lightness css), and JQuery Block UI are all used by this component but are loaded from CDN's.  Please see the read me file for details.
  5. This component is designed to be used on the Lead object only and for the standard address fields.  You will need to customize it for use with any other object and/or fields.
 In your page, you can reference your component (outside of a form) like this:

<c:USPS_Address LeadObject="{!CurrentLead}" CheckFullZip="true" USPS_USERID="{!USPSUserIDCode}" /> In your page controller, declare the variable and default it to a blank string: public string USPSUserIDCode {get; set;} public TestController() { USPSUserIDCode = ''; } // TestController
Then in your page controllers action function, you will need some logic like this to trigger the lookup:
USPSUserIDCode = 'INSERTYOURIDHERE'; return null; The method should be a page refrerence return type. When the variable is set and the page gets re-rendered, the pop-up will appear and look something like this:


Note that the rest of the page will be blocked until the pop-up dialog has been addressed.