Migrating S-Controls to API and AJAX 8.0


S-Controls for Dummies, Part 2 Migrating to API and AJAX Toolkit 8.0


Glenn Braunstein January 30, 2007 Contents Document Overview 3 API and AJAX Toolkit 8.0 Highlights 4 Sample S-Control 5 Upgrading the S-Control to 8.0 6 Step 1 – Change pointer to the AJAX Toolkit 6 Step 2 – Remove the AJAX Connection statement 6 Step 3 – Change the Connection Object reference 7 Step 4 – Change Reference to the Query Results Array 7 Step 5 – Add Relationship Queries 8 Step 6 – Add Order By and Limit clauses 10 Appendix A – Original S-Control (pre 8.0) 11 Appendix B – Upgraded S-Control (8.0) 13 Appendix C – Additional Resources 15


Document Overview Last summer, I published a document that illustrated how to add Stylesheets to your S-Controls. In just a few simple steps, utilizing the Beta Ajax Toolkit, it enabled you to transform your S-Controls from a very basic textual display of raw data, to a page that was branded to look like a seamless extension of Salesforce.com.

Now, with Winter 07, Salesforce.com has released version 8.0 of our API while at the same time productizing the old “beta” AJAX Toolkit. The combination of the new API and AJAX Toolkit 8.0 provides many new and exciting features that can add more power (and simplicity!) to your S-Controls. The purpose of this Guide is to highlight some of those new features (I won’t cover all the improvements) and illustrate the steps necessary to migrate your older S-Controls using the Beta Toolkit to the new API and AJAX Toolkit 8.0.

One more note – The audience for this document will be similar to my prior document. It is not a deep-dive exploration into S-Controls intended for the advanced developer, but rather a higher-level guide intended for the rookie developer or anyone interested in throwing together a very quick-and-dirty S-Control. For example, Sales Engineers looking to build a simple S-Control for demonstration purposes rather than a full-blown implementation-level S-Control. Hence the title: “S-Controls for Dummies, Part 2”.

API and AJAX Toolkit 8.0 Highlights These are just some of the many highlights of the API and AJAX Toolkit 8.0, and they are the items that will be highlighted in this guide. For a more comprehensive listing of all the new features in 8.0, check out: https://wiki.apexdevnet.com/index.php/What%27s_New_in_Apex_Web_Services_API_Winter_%2707 and http://www.salesforce.com/us/developer/docs/ajax/index.htm


New Features and Changes highlighted in this Guide:

• SOQL Relationship Queries • ORDER BY and LIMIT clauses in SOQL/SOSL • New AJAX Toolkit Location • Automatic Authentication and Session Management in AJAX • AJAX Connection Object change


Sample S-Control To illustrate the changes in 8.0 and the steps necessary to migrate older SContols that were built using the Beta Toolkit, we will begin with the S-Control that was created in the previous guide. This S-Control allowed the user to do a partial search on Account Names, click a Search button, and then be presented with a list of all accounts that contain that partial search text. See Appendix A for the code for the original S-Control.

Here’s a screenshot of the original S-Control:



Upgrading the S-Control to 8.0 We’re going to upgrade this S-Control to 8.0 and then make some slight modifications to it that will leverage the power of API and the AJAX Toolkit 8.0. This will be accomplished via the following simple steps: 1) Change the pointer from the old beta AJAX Toolkit to the new 8.0 Toolkit 2) Remove the AJAX Connection statement (no longer needed in 8.0!) 3) Change the connection object used in all AJAX Toolkit calls 4) Change the reference to the query results 5) Utilize Relationship Queries to retrieve parent and/or children records in one single SOQL statement

  • Prior to 8.0, this required multiple SOQL calls and therefore multiple hops back to the API, resulting in poorer performance

6) Add Order By and Limit clauses


Step 1 – Change pointer to the AJAX Toolkit The way that your S-Control “knows” where the AJAX Toolkit is located is by referencing it in the following statement:

<script language="javascript" src="https://www.sforce.com/ajax/beta1/sforceclient.js" type="text/javascript"></script>

You can see in this statement that the older beta version of the toolkit is being referenced. We will change this reference to point to the new version of the toolkit by replacing the above line with the following:

<script language="javascript" src="/soap/ajax/8.0/connection.js" type="text/javascript"></script>


Step 2 – Remove the AJAX Connection statement Why would we want to do this?? Well, one of the improvements to the AJAX Toolkit 8.0 is that a user running an S-Control is already authenticated by their current session and does need to “re-authenticate” when connecting to the Toolkit.

In the older beta toolkits, even though a user was already authenticated during their actual Salesforce.com session (i.e.-while they are simply navigating around and using Salesforce.com as normal), once they hit a link or web page that executes an S-Control, a statement was required to connect that user’s session to the AJAX Toolkit and then re-authenticate them. Now, with 8.0, their current user session automatically authenticates them to the Toolkit, thereby eliminating the need for this connection statement!

So all you need to do is simply remove (yes, I mean permanently delete) the following statement from your S-Control:

sforceClient.init("{!API.Session_ID}", "{!API.Partner_Server_URL_60}");

Step 3 – Change the Connection Object reference Once the script in step 1 is executed, a global object called sforce.connection is created. The object is what’s used throughout the S-Control to reference the functions in the Toolkit.

In the beta Toolkits, the object was called sforceClient. So to leverage the 8.0 Toolkit, you simply need to change the name of this object wherever it is referenced.

In my sample S-Control, there’s just one statement I need to change: var queryResult = sforceClient.Query(SearchString); should be changed to var queryResult = sforce.connection.query(SearchString);

    • One thing you will notice above is that I also changed the capitalization of the query function. This is important, as 8.0 requires the query function (and possibly all other functions, although I’m certain about that) to be lowercase. This is the opposite of previous versions, where the query function needed to be uppercase.
    • IMPORTANT NOTE - On the topic of capitalization…it appears that the 8.0 API is less forgiving when it comes to capitalization. Whereas in the original S-Control I was able to reference each Account field without regard to case, 8.0 is actually case-sensitive. So, for example, in the query of the original S-Control, I was selecting “Id, Site, Phone, and Name”, while referencing them in my output variables as “id, site, phone, and name”. That was not a problem previously. However, in 8.0, it is important to match the case in the variable assignments. So my output variable assignments have been changed to reference “Id, Site, Phone, and Name”.


Step 4 – Change Reference to the Query Results Array There has been a slight syntactical change to the way the query results are referenced as an array. Make the following change to the query results that are referenced in the “for” loop (i.e. – when cycling through the Accounts that were returned by the query so that they can be displayed on the page):

Change the following line: for (var j = 0; j < queryResult.records.length; j++) to: for (var j = 0; j < queryResult.getArray("records").length; j++)

In addition, change the following line in a similar manner: var Account = queryResult.records[j]; to: var Account = queryResult.getArray("records")[j];


Step 5 – Add Relationship Queries We will now modify the query to take advantage of the new Relationship Query capabilities. This new enhancement will allow you to retrieve parent and children records all with a single call. Previously, you would have had to do it in multiple statements, each of which required a call back to the API. With Relationship Queries, S-Control performance will most likely be improved due to the decrease in calls back to the database.

Relationship queries are built off of the relationships that are defined in the application configuration between two objects. Each relationship has a “name” that depends on which direction you are working. The name is singular when going from child to parent, and plural when going from parent to child.

For example, let’s take the relationship between the Account and Contact objects. The relationship name between when you’re looking at it from the perspective of a given account is “Contacts” (i.e.-show me the Contacts for this Account), whereas the relationship name is “Account” when looking the other direction (i.e.-show me the Account for this particular Contact). Relationships are documented in much more detail in the API docs (http://www.salesforce.com/us/developer/docs/api/index.htm). Simply look up “Relationships” in the Index.

Understanding these Relationships is very important when constructing your SOQL statements, since it’s the relationship name that you reference in the query statement. This should become clearer as we take a look at the example below.

What we are going to do here in this example is enhance the S-Control to bring back not only Account information meeting the user-inputted criteria, but also the related Contacts for each Account as well.

Before we do that, though, I have changed the query result’s variable name to make it clearer as we walk through the example. So instead of each record being assigned to a variable called Account, we are instead going to use the variable record, which will make it easier to understand the example below. This is done by changing the following line: var Account = queryResult.getArray("records")[j]; to: var Record = queryResult.getArray("records")[j];

So now, to add the Relationship Query, we’ll have to do 2 things: 1) Modify the SOQL statement to include the relationships 2) Add the fields to the output

Modify the SOQL statement (as assigned to the SearchString variable) to include the Relationship by changing it from: SearchString="Select Id, Site, Phone, Name From Account Where Name like '%" + document.AccountSearchForm.AccountName.value + "%' "; to: SearchString="Select FirstName, LastName, Phone, Email, Account.Id, Account.Site, Account.Name From Contact Where Account.Name like '%" + document.AccountSearchForm.AccountName.value + "%' ";

You can see that in order to bring in the fields from the Account object into this query, it’s simply a matter of just preceding each Account field with the “Relationship Name” (which is called “Account”, as described earlier). Prior to 8.0, this could only be accomplished by using 2 separate queries: 1 to retrieve the Contacts, and then an entirely separate one to retrieve the Account information for each Contact.


So now, it’s simply a matter of changing the output variable (which contains the HTML to present these results on the page) to include these new fields. Do this by replacing the old output assignment lines with the following:

output += "<tr onmouseover=hiOn(this) onmouseout=hiOff(this)>"; output += "<td>" + Record.FirstName + " " + Record.LastName + "</td>"; output += "<td>" + Record.Phone + "</td>"; output += "<td>" + Record.Email + "</td>"; output +="<td><a href='/"+Record.Account.Id+"' target=_parent>" + Record.Account.Name + "</a></td>"; output += "<td>" + Record.Account.Site + "</td></tr>";

You will also want to add these new fields to the table header. This can be done by replacing the output assignments in the “Build Table Header” section with the following:

var output = ""; output += ""; output += ""; output += ""; output += ""; output += "</th>";


Step 6 – Add Order By and Limit clauses You’re almost done! The last 2 things we want to do are limit the # of rows that are being returned (since there could be a large number of Contacts returned across the Accounts we search for), and then alphabetize them. These are accomplished by 2 additional new features of API 8.0: the Order By and Limit clauses.

Adding these clauses is very easy. Simply add the following to the very end of your Select statement (after the Where clause): Order By FirstName ASC Limit 5 So that your entire SearchString query appears as follows: SearchString="Select FirstName, LastName, Phone, Email, Account.Id, Account.Site, Account.Name From Contact Where Account.Name like '%" + document.AccountSearchForm.AccountName.value + "%' Order By FirstName ASC Limit 5";


That’s it!! Your S-Control has now been upgraded to API and AJAX 8.0, and includes a couple of the key features and improvements that 8.0 has to offer.

See Appendix for the entire final S-Control. And here’s a screenshot:



See Appendix for the entire final S-Control. And here’s a screenshot:

Appendix A – Original S-Control (pre 8.0)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <link href="/dCSS/Theme2/default/common.css" type="text/css" media="handheld,print,projection,screen,tty,tv" rel="stylesheet" > <link href="/dCSS/Theme2/default/custom.css" type="text/css" media="handheld,print,projection,screen,tty,tv" rel="stylesheet" >

<HTML>

<script type="text/javascript" src="/js/functions.js"></script>

<script language="javascript" src="https://www.sforce.com/ajax/beta1/sforceclient.js" type="text/javascript"></script> <script language="JavaScript">

sforceClient.init("{!API_Session_ID}", "{!API_Partner_Server_URL_60}");


function SearchAccount() {

SearchString="Select Id, Site, Phone, Name From Account Where Name like '%" + document.AccountSearchForm.AccountName.value + "%' ";

var queryResult = sforceClient.Query(SearchString);

// BUILD TABLE HEADER

var output = "
Contact NamePhoneEmailAccount NameAccount Site
"; output += ""; output += ""; output += "";

//BUILD TABLE ROWS for (var j = 0; j < queryResult.records.length; j++) { var Account = queryResult.records[j];

output += ""; output +=""; output += ""; output += "";

}

output += "
Account NameAccount SiteAccount Phone
<a href='/"+Account.id+"' target=_parent>" + Account.name + "</a>" + Account.site + "" + Account.phone + "
";

document.getElementById("ResultsHolder").innerHTML = output;

}

</script> <BODY class="account">


<IMG class=pageTitleIcon alt=Account src="/s.gif">

Contents

Account:

Search Page


<form name=AccountSearchForm method="POST" action="javascript:SearchAccount()">

Account Name: <input name="AccountName" type="text" size=30">
<input class=btn type="submit" name="submit" value=" Search ">


</form>

</BODY> </HTML>

Appendix B – Upgraded S-Control (8.0)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <link href="/dCSS/Theme2/default/common.css" type="text/css" media="handheld,print,projection,screen,tty,tv" rel="stylesheet" > <link href="/dCSS/Theme2/default/custom.css" type="text/css" media="handheld,print,projection,screen,tty,tv" rel="stylesheet" >

<HTML>

<script type="text/javascript" src="/js/functions.js"></script>

<script language="javascript" src="/soap/ajax/8.0/connection.js" type="text/javascript"></script> <script language="JavaScript">



function SearchAccount() {


SearchString="Select FirstName, LastName, Phone, Email, Account.Id, Account.Site, Account.Name From Contact Where Account.Name like '%" + document.AccountSearchForm.AccountName.value + "%' Order By FirstName ASC Limit 5";

var queryResult = sforce.connection.query(SearchString);


// BUILD TABLE HEADER

var output = ""; output += ""; output += ""; output += ""; output += ""; output += "</th>";


//BUILD TABLE ROWS for (var j = 0; j < queryResult.getArray("records").length; j++) { // var Account = queryResult.records[j]; var Record = queryResult.getArray("records")[j];


output += "<tr onmouseover=hiOn(this) onmouseout=hiOff(this)>"; output += "<td>" + Record.FirstName + " " + Record.LastName + "</td>"; output += "<td>" + Record.Phone + "</td>"; output += "<td>" + Record.Email + "</td>"; output +="<td><a href='/"+Record.Account.Id+"' target=_parent>" + Record.Account.Name + "</a></td>";

output += "<td>" + Record.Account.Site + "</td>"; } output += "</table>"; document.getElementById("ResultsHolder").innerHTML = output; } </script> <BODY class="account">
<IMG class=pageTitleIcon alt=Account src="/s.gif">

Account:

Search Page


<form name=AccountSearchForm method="POST" action="javascript:SearchAccount()">

Contact NamePhoneEmailAccount NameAccount Site
Account Name: <input name="AccountName" type="text" size=30">
<input class=btn type="submit" name="submit" value=" Search ">


</form>

</BODY> </HTML>

Appendix C – Additional Resources


Force.com Web Services API Developer’s Guide (8.0) http://www.salesforce.com/us/developer/docs/api/index.htm

AJAX Toolkit Developer’s Guide (8.0) http://www.salesforce.com/us/developer/docs/ajax/index.htm

What’s New in Winter 07 API https://wiki.apexdevnet.com/index.php/What%27s_New_in_Apex_Web_Services_API_Winter_%2707

Additional Information on S-Controls in General (including a “Getting Started with…” presentation, AJAX “Hello World” Example, and others) https://wiki.apexdevnet.com/index.php/Composite_Framework#S-Controls

Additional API and AJAX Information on DFC https://wiki.apexdevnet.com/index.php/API

Force.com IDE – HIGHLY RECOMMEND! https://wiki.apexdevnet.com/index.php/Apex_Toolkit_for_Eclipse