+ Start a Discussion
r_boyd_848r_boyd_848 

Jumping through the Namespace hoops

Here's one for all you builders of managed packages. I wanted to place a Custom Button the on Accounts page that you recalculate a value. To do this I needed to call a class, this can be achieved by setting the Custom Button to call some Javascript and then wire up an Ajax call. The code is straightforward

 

 

{!REQUIRESCRIPT("/soap/ajax/21.0/connection.js")}
{!REQUIRESCRIPT("/soap/ajax/21.0/apex.js")}

var bal = sforce.apex.execute("PaymentEngine","getAccountBalance", {id:"{!Account.Id}"});

//more code

 

 

The class needs to be Global and the method static and prefixed with WebService. All good.

 

HOWEVER if this is App is converted into a managed package the call will fail as the class requires that it has the Namespace prepended to it. The Ajax Toolkit allows you to add the Namespace via sforce.connection.defaultNamespace ="myNamespace". But I have to know the namespace in the first place. Moving between dev, deployment and product orgs it is not feasible to do this. Inspired by this article I created the following:

 

 

{!REQUIRESCRIPT("/soap/ajax/21.0/connection.js")}
{!REQUIRESCRIPT("/soap/ajax/21.0/apex.js")}

var result = sforce.connection.describeGlobal();
var TOKEN_OBJ = "Patient__c";  //ensure we get our Apps Namespace not someoneelses
var sobjects = result.getArray("sobjects");
var ns = "";

for(var i=0; i<sobjects.length; i++) {
     var sobjName = sobjects[i].name;
     //sforce.debug.log('Object Found: ' + sobjName);
     //describeResult = sforce.connection.describeSObject(sobjName); //dont call this makes it reallt slow
   //  sforce.debug.log('Is Custom: ' + describeResult.custom);

         var str = sobjName.split("__");
          //if there are 3 values there is a namespace
         if (str.length == 3){
             sforce.debug.log(sobjName + ' has namespace');
             //Is it one of ours?
             var sobj = str[1].valueOf() +'__c';
             if(sobj == TOKEN_OBJ) {
                    ns = str[0].valueOf();
             }else{
// just for debugging and testing
  // sforce.debug.log(sobjName + ' is not in my namespace'); } }else{

  // sforce.debug.log(sobjName + ' has no namespace'); } } if(ns != ""){ // sforce.debug.log(('I have a namespace called ' + ns');
sforce.connection.defaultNamespace =ns;
}else{
    // sforce.debug.log(('I have no namespace called');
}
//now do stuff
var bal = sforce.apex.execute("PaymentEngine","getAccountBalance", {id:"{!Account.Id}"});
window.alert("Balance updated to: " + bal );

 

So it works, but its crazy. Please Mr Salesforce can you make it easier to get the Namespace via AJAX, Javascript, jQuery. How about sforce.connection.getNamespaceFor('MyCustomObject__c');

 

 

 

 

 

 

Best Answer chosen by Admin (Salesforce Developers) 
r_boyd_848r_boyd_848

Here's a further refinement

 

 

var sfClass ='';
var sfField='';

if(ns != ""){
    sforce.connection.defautNamespace = ns;
    sfClass = ns+".";
    sfField = ns+"__";
}

var bal = sforce.apex.execute(sfClass+"PaymentEngine","getAccountBalance", {id:"{!Account.Id}"});
var account = new sforce.SObject("Account");

account.id = "{!Account.Id}";


account[sfField+"account_balance__c"] = bal;
result = sforce.connection.update([account]);

 

The sforce.apex.execute requires a . notation and the field a __ so this ensures the syntax is correct. I got failures in a managed package even though the namespace was set in default namespace

 

All Answers

joshbirkjoshbirk

It's an interesting use case - and that's pretty clever solution.  I'm not sure what the update schedule is on the AJAX toolkit, but I'll poke around and see if we can add in a more utilitarian function.

 

Out of curiousity, have you seen any of the JavaScript Remoting being released in Summer '11?  It might save you the API call.

r_boyd_848r_boyd_848

I've read through the stuff on remote scripting but didn't see anything special concerning namespacing, so didn't go much further. Still it, looks like there is some interesting stuff in it.

 

In general we're using a lot of jQuery and extensive use of the Ajax toolkit so the inability to get access to the namespace is a bit of a headache. We have been using a custom setting to store the Namespace value and then setting a property on the custom/extension controller to get the value. We'd then use property value as a field merge

 

 

<script type="text/javascript" >
		jQuery.noConflict();
		strNamespace = '{!namespace}'; //property on controller
		jQuery(document).ready(function()
//etc.......

// function snippet
function save(adminData){
    			var today = new Date();
    			var invoiceQueue = new sforce.SObject(strNamespace+"InvoiceQueue__c");

    			invoiceQueue[strNamespace+'Patient__c'] = '{!patientId}';
    			invoiceQueue[strNamespace+'Medical_Centre__c'] = '{!currentMedicalCentre}';
    			var invResult = sforce.connection.create([invoiceQueue]);

//etc...

 

 

This worked fine, even if its hack, until today when I wanted to add a custom button to the standard Accounts page.

 

We'd be more then happy to share code, use cases and assist in anyway we can

 

 

r_boyd_848r_boyd_848

Just to round this off. Here is the final code:

 

 

 

{!REQUIRESCRIPT("/soap/ajax/21.0/connection.js")}
{!REQUIRESCRIPT("/soap/ajax/21.0/apex.js")}

var result = sforce.connection.describeGlobal();
var TOKEN_OBJ = "Patient__c";  //known custom sObject in your namespace
var sobjects = result.getArray("sobjects");
var ns = "";

for(var i=0; i<sobjects.length; i++) {
     var sobjName = sobjects[i].name;
      var str = sobjName.split("__");
          //if there are 3 values there is a namespace
         if (str.length == 3){
             var sobj = str[1].valueOf() +'__c';
             //compare with token to ensure its in our namespace
if(sobj == TOKEN_OBJ) { ns = str[0].valueOf(); } } } if(ns != ""){ sforce.connection.defautNamespace = ns; ns = ns+"__"; //IMPORTANT FOR DML to work }
//with default namespace set this will work var bal = sforce.apex.execute("PaymentEngine","getAccountBalance", {id:"{!Account.Id}"}); var account = new sforce.SObject("Account"); account.id = "{!Account.Id}"; account[ns+"account_balance__c"] = bal; //NOTE syntax result = sforce.connection.update([account]); window.location.reload();

 

Notice the syntax used to get the Account_Balance___ field to update. 'account.account_balance__c' worked fine in the unmanaged packaged but failed in the managed package. At first I thought the issue was related to the namespace so I tested with the Name field - but this failed as well. One of our other developers encountered the same issue and realized that using an array syntax would work i.e. account[ns+"CustomField___c"] ='abc';. For standard fields omit the ns (account["Name"] = 'abc';). Make sure you declare the namespace variable ns as an empty string.

 

 

 

r_boyd_848r_boyd_848

Here's a further refinement

 

 

var sfClass ='';
var sfField='';

if(ns != ""){
    sforce.connection.defautNamespace = ns;
    sfClass = ns+".";
    sfField = ns+"__";
}

var bal = sforce.apex.execute(sfClass+"PaymentEngine","getAccountBalance", {id:"{!Account.Id}"});
var account = new sforce.SObject("Account");

account.id = "{!Account.Id}";


account[sfField+"account_balance__c"] = bal;
result = sforce.connection.update([account]);

 

The sforce.apex.execute requires a . notation and the field a __ so this ensures the syntax is correct. I got failures in a managed package even though the namespace was set in default namespace

 

This was selected as the best answer