Namespace Prefix in JavaScript and Apex within Installed Package

When you have a dedicated Visualforce Page and can use modern JavaScript Remoting techniques, this is seldom an issue. But I’m looking at some historical code in a managed package like this:

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

var result = sforce.apex.execute(
    "ns.WebServiceClass",
    "someWebServiceMethod",
    {"arg1": "foo", "arg2": "bar"}
);

alert(result);

The ns is baked in. While this JavaScript can be deployed to another development org, the web service invocation will not execute as the class doesn’t have a namespace prefix in this environment.

Same problem applies to Custom Button or Link URLs. Surely URLFOR('/apex/PageName') would resolve the namespace prefix; after all the button is a managed component with the namespace on it. But it doesn’t work the same magic that new PageReference('/apex/PageName') does in Apex.

I’ve seen a few approaches including (but not limited to):

  • maintaining a list of manual deployment steps,
  • using some precompile task (eg Ant) to tokenize the namespace prefix,

Can you gurus share any other techniques?

Answer

There are ways to surface the namespace itself from within Apex (doesn’t help us in JavaScript, yet).

  1. Deriving the namespace prefix itself:

    String rawPrefix = MyClass.class.getName().substringBefore('MyClass').substringBefore('.');
    //this gives '' in any development org
    //and gives 'ns' in the packaging org
    
  2. Getting a single token which can be used to qualify Apex Classes:

    String dotPrefix = MyClass.class.getName().substringBefore('MyClass');
    //this gives '' in any development org
    //and gives 'ns.' in the packaging org
    
  3. Getting a single token to qualify Salesforce Objects:

    String barPrefix = SObjectType.MyObject__c.Name.substringBefore('MyObject__c');
    //this gives '' in any development org
    //and gives 'ns__' in the packaging org
    

As long as there’s a Class or SObject in the package, this gets a handle on the namespace prefix. But doesn’t help for Custom Button code which only has formula context. I tried a similar approach:

var ns = "{!$ObjectType.MyObject__c.Name}";
// I had thought the above would give MyObject__c in any development org
// and would give ns__MyObject__c in the packaging org
// and SUBSTR() and LEFT() and ISBLANK() etc etc etc could be used to deal with the prefix

var result = sforce.apex.execute(
    ns + "WebServiceClass",
    "someWebServiceMethod",
    {"arg1": "foo", "arg2": "bar"}
);

However $ObjectType is unavailable in Formula context much like $Page and $Resource, grrr!

But Hierarchy Custom Settings are available in formula context. So in combination, one could use the Apex code above to derive the namespace prefix. Then use a Post Install Script to automatically populate an org-wide Custom Setting instance to make it accessible in formula context.

public void onInstall(InstallContext context) {
    //fetch custom setting or create it for the first time
    NamespaceSettings__c ns = NamespaceSettings__c.getOrgDefaults();
    if (setting == null) ns = new NamespaceSettings__c()

    //ns
    ns.RawPrefix__c = MyClass.class.getName().substringBefore('MyClass').substringBefore('.'),

    //ns.
    ns.DotPrefix__c = MyClass.class.getName().substringBefore('MyClass'),

    //ns__
    ns.BarPrefix__c = MyObject.Tax__c.Name.substringBefore('MyObject')

    //write the values away
    upsert ns;
}

Then the webservice invocation can look more like this:

var result = sforce.apex.execute(
    "{!$Setup.NamespaceSettings__c.DotPrefix__c}WebServiceClass",
    "someWebServiceMethod",
    {"arg1": "foo", "arg2": "bar"}
);

Which meets the requirements of: all code namespace agnostic, no precompile, and no manual steps after install. Having shared this would be grateful for any improvements or to hear lessons learned etc.

Attribution
Source : Link , Question Author : Matt and Neil , Answer Author : Matt and Neil

Leave a Comment