The Scripting Toolkit is a JavaScript Ajax wrapper around the API:
This document explains how to use the Scripting Toolkit to build batch interfaces.
A batch using the Scripting Toolkit is a standalone Javascript file (*.js) called by the engine. A batch run in console mode (command line).
The scripting Toolkit is a standalone Windows Scrirpting File (*.wsf) where you don't have to put your code, the engine can remain invariant.
With the Scripting Toolkit, you can create a javascript batch that contains Ajax calls and processes Salesforce data as well as access to your own back-office data.
You can create Javascripts in your favorite development environment, or even using notepad. For example, you can use the SciTe editor, Textpad or Visual Studio.
The following are examples of appropriate uses:
The Scripting Toolkit should not be used :
The following are examples of scenarios that require case-by-case analysis:
An example of inappropriate usage is polling data every second. This would require too much API calls, and there is a risk to reach the maximum amount of API calls.
The current release of the Scripting Toolkit is the only version that receives bug fixes and enhancements. When
a new version is released, the previous version is not available anymore.
There is no official support of this Toolkit as it is submitted in the Salesforce Labs
By downloading the Scripting Toolkit, you get all the source code. You cannot redistribute this Toolkit, event if you have made changes.
You can use the toolkit for free, at your own risk (take it "as is")
Latest version : 0.4.9
For each of these files, right click on them and choose "save as"
scriptingtoolkit.wsf (34 Kb) : the Scripting Toolkit engine
myscript.js (1 Kb) : sample code using the Scripting Toolkit
connection.js (54 Kb) : the Ajax ToolKit
Put these files in the same folder.
In addition to the content of this document, there are other resources available for you as you learn to use the Scripting Toolkit or its technology :
Here is a matrix that will let you choose the best system according to your needs.
| Toolkit | Scripting Toolkit | .Net | Office | Java | Ajax | PHP | Perl |
| Language | VbScript, Javascript | C#, VB | VBA | Java | Javascript | PHP | Perl |
| Compiled | N | Y | N | Y | N | N | N |
| sControl skills (similar) | 95% | 50% | 50% | 60% | 100% | 40% | 30% |
| Browser based | N | N | N | N | Y | N | N |
| Web Server based | N | Y/N | N | Y/N | N | Y | Y/N |
| Batch (console) | Y | Y | N | Y | N | N | Y |
| Prerequisites | Windows 2k,XP, 2003 (native) | .Net Framework | MS Office | JDK 1.5 | IE/Firefox | 5.1.2 + Soap extension | Soap::Lite |
| Debugger | Visual Studio or script debugger | VS | VBA | jdb | Firefox error console | Dbg, nusphere... | Perl -d |
| IDE | Any, incl. notepad | Visual Studio | Integrated VBA editor | Eclipse | Salesforce ! | Any | |
| Obfuscator | screnc | compiled | Password protected | compiled | No | Nu-coder | Already obfuscated ;) |
| Time execution limitation | None | None (if not IIS) | None | None | Yes (browser) | Yes (web server) | None |
| Various | The only batch system ready to run (no download, no compile, just save/run) | Framework version dependant | Restricted to part of the API (COM object, not direct access to the API) | Need to compile to test | Need to load the toolkit on each run (takes time) + time to save the code on the server | Cannot be used for batch | Skill required (difficult syntax). No update since 2004 |
You can cut and paste the following sample code into the myscript.js file. This script queries data and displays it on the screen. For more information about using the API, see Salesforce's API documentation.
function Results(queryResult) { if (queryResult.size > 0) { var output = ""; var records = queryResult.getArray("records"); for (var i=0;i<records.length;i++) { var account = records[i]; output += account.Id + " " + account.Name + "\r\n"; } WScript.echo(output); }else WScript.echo("No records matched."); ToolKit.Quit(); } var callback = {onSuccess : Results, onFailure : function(e){WScript.echo(e)}}; var queryResult = sforce.connection.query("Select Id,Name from Account", callback);
The Scripting Toolkit can be run using the command-line.
Go to Start/Execute, enter "cmd" and press enter. You see a console with a prompt.
Go to the folder hosting the Scripting Toolkit and your custom script (myscript.js)
Enter the following command line :
cscript //nologo scriptingtoolkit.wsf /?
This script encapsulates Salesforce's AJAX Toolkit to provide a batch access to Salesforce
Usage : scriptingtoolkit.wsf [/user:value] [/pass:value] [/sid:value] [/server:value] [/proxyserver:value] [/proxyuser:value]
[/proxypass:value] [/debug] [/sandbox]
Options :
user : Authorized user for the API.
pass : User's password.
sid : session id, if no user/pass provided
server : the server of your org (ex:na3.salesforce.com or na3-api.salesforce.com)
proxyserver : hostname or IP of your proxy, and port (ex:proxy.internal.yourcompany.com:3128 or 192.168.0.7:3138)
proxyuser : username if using a proxy with authentication
proxypass : password if using a proxy with authentication
debug : To dump on stderr some debug info
sandbox : To authenticate on test.salesforce.com
Examples : cscript //nologo scriptingtoolkit.wsf /user:myself@mycompany.com /pass:secret
cscript //nologo scriptingtoolkit.wsf /debug /server:emea.salesforce.com /sid:Xabu.ZcS.....9AsNCSeX5jsUoLXQ=
if no user/password and no server/sid, script tries to get an existing sid from an IE browsing session
using a proxy : cscript //nologo scriptingtoolkit.wsf /proxyserver:192.168.0.7:3128 /proxyuser:jla /proxypass:jla
Proxy error ? See http://support.microsoft.com/kb/289481/en-us
Compatibility test : cscript //nologo scriptingtoolkit.wsf //Job:diagnose
If you want to check your Windows configuration and identify any issue, run this command line :
cscript //nologo scriptingtoolkit.wsf //Job:diagnose
You can use Visual Studio or Microsoft Script Debugger. You will be able to add break points and watch the value of objects.
If you don't want somebody to see your code, you can encode it using Microsoft Script Encoder.
People will not be able to modify your code. Take care as this encoding is bijective : some tools can retrieve your source code.
FOR ADVANCED USERS ONLY
The Scripting Toolkit is requesting a remote js file : connection.js
If you download this file (see the location in the source code if the Scripting Toolkit) and put it in the same folder as the Tookit, you can change the remote reference to the local file. Each time you start your batch, you will save a few seconds and bandwidth as the file will be retrieved from your local folder.
By using the Scripting Toolkit, you have access to an automatically instanciated object : ToolKit.
This object has public properties and methods.
These are available properties :
| Value | Description |
|---|---|
| className | "ScriptingToolKit" |
| User | The user retrieved from the command line parameter /user: |
| Password | The password retrieved from the command line parameter /pass: |
| Server | The server address of the proxy, retrieved from the command line parameter /server: Proxy with basic authentication or NTLM are supported, incl. BlueCoat, ISA Server etc. |
| SID | The command line parameter /sid: |
| HasToExit | For internal use, always false. If the Quit() method has been called, set to true. |
| ErrCode | 0 if no error. Can have another value if there was an error in a method call. |
These are available methods :
| Value | Description |
|---|---|
| ClearError() | Purpose : Clear error information, setting to 0 the error code.
input : none output : none |
| SetError(errorcode, errortext) | Purpose : Set the error code and error text.
input : errorcode : integer, errortext : string output : none |
| ErrText() | Purpose : Get the error text, based on the current error code, used to dump error info
input : none output : a string containing the generic error text based on the error code and custom contextual error text |
| VerifyLoginParameters() | Purpose : internal, undocumented
input : none output : none |
| Debug(text) | Purpose : Display debugging information
input : a string output : none The text is printed to stderr if the /debug parameter has been used in the command line |
| Login() | Purpose : internal, undocumented
input : none output : none |
| Quit() | Purpose : Used to stop the script
input : none output : none As callback functions can be used, the only way to know that the script has finished is to explicitely call this method. If you forget to call this method at the end of your job, the script will never end (press Ctrl+C to stop it) |
| SOQL2CSV(SOQL,Header,Sep,cr,strnull) | Purpose : export a SOQL query to a CSV file, can manage parent relationship data
input : SOQL : a SOQL query returning a result set input : Header : optional : an array of columns for the first line of the CSV file. The name should match the fields in the SOQL. The order of the array define the order of the fields in the CSV file. input : Sep : optional : a separator, default to "," input : cr : optional : the cariage return (end of line) used : \r, \n, \r\n ... default value is \r\n input : strnull : optional : a string value to use for any field value being null. default value is "" (including the double quotes) output : null if any error, or a string in the CSV format |
| CSV2Array(path,file) | Purpose : read a csv file into an array in memory
input : path : folder where the file can be found input : file : the csv file without the path output : an 2-dimension array, the first is the record, the second the field. Fields can be referenced by their name, taken from the column name If you encounter an error, ensure the following registry key is valid : HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines\Text\Format This could be either Delimited(,) or Delimited(;) |
| InsertFromArray(sObject,theArray,size) | Purpose : insert data into Salesforce from an array in memory
input : sObject : the name of the Salesforce object input : theArray, an array the same than CSV2Array() result. input : size : optional : the maximum amount of records per Ajaxcall output : true on success, false on error Prerequisite : array index name should match Salesforce field names |
| DeleteFromSOQL(SOQL,size) | Purpose : Delete Salesforce data based on a SOQL Query
input : a SOQL query selecting Id(s) input : size : optional : the maximum of amount records per Ajaxcall output : true on success, false on error |
| sObject2OracleCreateTable(sObjectName) | Purpose : Build an SQL query based on the structure of the sObject, to create an Oracle table
input : sObjectName : the name of the sObject (string) output : null if any error, or a string containing the SQL |
| StringtoFile(str,filename) | Purpose : write to a file the content of a string
input : str : the content of the file input : filename : the file name to create or overwrite output : none |
| StringtoFileUTF8(str,filename) | Purpose : write to an UTF8 file the content of a string
input : str : the content of the file input : filename : the file name to create or overwrite output : none |
| FileToString(filename,unicode) | Purpose : read the content of a file. The file should be less than 1 Mb, to keep good performance.
input : filename : the name of the file to read input : unicode : true if the file should be read as a unicode file output : the content of the file |
| StartTimer() | Purpose : Start the timer to record the duration of an operation
input : none output : none |
| StopTimer() | Purpose : Stop the timer to record the duration of an operation
input : none output : none |
| TimeDiff() | Purpose : Get the duration that has been benchmarked
input : none output : amount of milliseconds |
| SendMail(from,to,subject,body) | Purpose : Send an email using CDO. See the diagnose job to dump the configuration
input : from : sender of the email input : to : recipient of the email input : subject : title of the email input : body : content of the email, that can be text or html (format autodetected) output : true if success, false on error |
| GetFile(SOQL,filename) | Purpose : Retrieve a Salesforce remote document or attachment and save it locally
input : SOQL : a query on the Body field, returning only one id. Ex : "Select Body from Document where Id='015300000000CJuAAM'" input : filename : the file name to create or overwrite output : true if success, null on error |
Save these samples into myscript.js and run them using the command line.
WScript.echo("Hello World !"); ToolKit.Quit();
The first line uses the method to print a text to the console. The second line tells the Scripting Toolkit to end.
No access to salesforce data, but as it is using the Scripting Toolkit, an implicit login occurs to the application.
try{ var queryResult = sforce.connection.query("Select Id,Name from Account"); if (queryResult.size > 0) { var output = ""; var records = queryResult.getArray("records"); for (var i=0;i<records.length;i++) { var account = records[i]; output += account.Id + " " + account.Name + "\r\n"; } WScript.echo(output); }else WScript.echo("No records matched."); }catch(e){ WScript.echo(e); } ToolKit.Quit();
To retrieve data asynchroneously, see the sample in the previous chapter.
ToolKit.StartTimer(); var queryResult = sforce.connection.query("Select Id,name,Industry from Account"); ToolKit.StopTimer(); WScript.echo(ToolKit.TimeDiff()+" milliseconds"); ToolKit.Quit();
Timer functions are provided to identify how long it takes to run a few lines of code.
//var strTest=ToolKit.SOQL2CSV("Select type,Id,name,Industry from Account",["Name","Id","Type","Industry"],";"); var strTest=ToolKit.SOQL2CSV("Select Name,Owner.FirstName,Owner.LastName,Owner.Id,Account.Name from Opportunity"); WScript.echo(strTest); if(strTest)ToolKit.StringtoFile(strTest,"test.csv"); ToolKit.Quit();
Two steps :
WScript.echo(ToolKit.FileToString("test.csv",false)); ToolKit.Quit();
The file can be Ascii or Unicode (second parameter=true for unicode)
var describeGlobalResult=sforce.connection.describeGlobal(); var types=describeGlobalResult.types.toString().split(","); for (i=0;i<types.length;i++) { WScript.echo(types[i]); } ToolKit.Quit();
You can retrieve the list of objects which you can use in your SOQL queries.
var describeResult=sforce.connection.describeSObject("Attachment"); var props=["activateable","createable","deletable","custom","keyPrefix","label","labelPlural","layoutable","mergeable","name" ,"queryable","replicateable","retrieveable","searchable","undeletable","updateable","urlDetail","urlEdit","urlNew"]; for(x=0;x<props.length;x++){ WScript.echo(props[x]+"="+describeResult[props[x]]); } for(recordNumber in describeResult.fields){ WScript.echo("Field "+recordNumber+":"+describeResult.fields[recordNumber].name+" ("+describeResult.fields[recordNumber].label+")"); for(columns in describeResult.fields[recordNumber]){ //if(typeof(describeResult.fields[recordNumber][columns])!='function') //WScript.echo(columns+"="+describeResult.fields[recordNumber][columns]); } } ToolKit.Quit();
This script lists available fields and their attributes for one sObject.
SQL=ToolKit.sObject2OracleCreateTable("Account"); if(SQL){ WScript.echo(SQL); }else{ WScript.echo(ToolKit.ErrText()); } ToolKit.Quit();
This is a way to manage more records than a result set restricted to the batchsize.
for(x=0;x<queryResult.size;x++){ //your code here to use queryResult.records[x]); //... if(queryResult.done=='false')queryResult=sforce.connection.queryMore(queryResult.queryLocator); }
This is a way to manage more records than a result set restricted to the batchsize.
if(ToolKit.GetFile("Select Body from Document where Id='015300000000CJuAAM'","test.doc")){ WScript.echo("success!") }else{ WScript.echo(ToolKit.ErrText()); } ToolKit.Quit();
This will write the remote file locally.
The field "Body" should be in your SOQL. Only one file is retrieved per query : you can filter on the Id
var frequency=10*1000; function myScheduler(){ WScript.echo(Date()+" scheduled."); //Run your scheduled code here window.setTimeout( "myScheduler()", frequency); } window.setTimeout( "myScheduler()", frequency );
You don't need a scheduler to run a script every X minutes or hours, even every minute.
The frequency is in milliseconds :
ToolKit.SendMail("Jean-Luc Antoine <antoinejl@xxartabus.com>" ,"Jean-Luc Antoine <xxantoinexx@xxsalesforce.com>" ,"Just a test" ,"This is a text body\nwhich has two lines.");
ToolKit.SendMail("Jean-Luc Antoine <antoinejl@xxartabus.com>" ,"Jean-Luc Antoine <xxantoinexx@xxsalesforce.com>" ,"My second email" ,"<html><body><h2>Cool !</h2>Very nice HTML</body></html>");
You can send an email with only 1 line of code (arguments : from, to, subject, body). The body can be either text or html (autodetected). The technical parameters are defined by the CDO configuration : you can dump them using the diagnose mode (see above in this page).
if(ToolKit.DeleteFromSOQL("Select Id from Account where Name like 'ZZZyourprefix%'")){ WScript.echo("Succefully deleted"); }else WScript.echo(ToolKit.ErrText()); ToolKit.Quit();
A quick way to drop some records with conditional filters.
result = sforce.apex.execute('MyWebServiceClass' ,'MyMethod',{theParam:"Something", secondParam:"Something else"}); WScript.echo(result); ToolKit.Quit();
Call an Apex Web Service the same way you would do it from within an SControl. executeAnonymous() is available too.
The WebService being called would be as an example :
global class MyWebServiceClass {
webService static String MyMethod(String theParam, String secondParam) {
return 'Hello '+theParam;
}
}
var CSVResult=ToolKit.CSV2Array("c:\\temp\\","account.csv"); if(ToolKit.InsertFromArray("Account",CSVResult,200)){ WScript.echo("Succefully inserted"); }else WScript.echo(ToolKit.ErrText()); ToolKit.Quit();
This example uses a file with this content :
NAME,INDUSTRY My new account 1,Manufacturing My new account 2,Services My new account 3,Manufacturing My new account 4,Services