34

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?

51

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.

  • 2
    This is awesome and deserves more upvotes – ca_peterson Mar 25 '14 at 1:51
  • I suppose you could always award a bounty ;) nyuk nyuk nyuk @ca_peterson – bigassforce Mar 25 '14 at 2:01
  • 1
    Why is it that the best answers always seem so simple and obvious? Well done. Massively useful. – zachelrath May 5 '14 at 13:50
  • 2
    @bigassforce many thanks to you!! i struggled with this problem 3 days, i also was thinking about custom settings and all of the sudden i found this topic!! – Bogdan K Jun 17 '16 at 16:15
3

The most straightforward approach to finding an Org's Namespace Prefix in Apex / SOQL is to simply query the Organization object's NamespacePrefix field:

Organization org = [ 
   select NamespacePrefix from Organization limit 1
]; 

String ns = org.NamespacePrefix == null ? '' : org.NamespacePrefix: 

// returns your namespace in Packaging/Patch/Scratch DE Org with a namespace
// returns '' in all other orgs

If only $Organization.NamespacePrefix was a thing, that would eliminate the need for the Hierarchy Custom Setting approach for Formula Fields --- I've posted an Idea about this:

https://success.salesforce.com/ideaView?id=0873A0000003XISQA2

0

Sure. Our workaround is no fun as well. But we had to do first 3 steps as part of package installation, hence we choose this route. Otherwise I would have tried solution you suggested.

  1. Install MetaData API class. See this thread for more details - http://andyinthecloud.com/2014/11/22/apex-metadata-api-qa/

  2. Create a VF tab that will be your welcome/1st tab within the app

  3. Write JS in that VF page to first create remote site. Then have a button on VF that will call controller to create PricebookEntry field sets. Salesforce does not allow PricebookEntry field set as part of managed package, hence the workaround. Please do not ask why JS to create remote site. Please refer to thread in step 1

  4. Now as PBE field set will always be created directly in the org, we can safely rely on that to get the Namespace prefix. It will never have prefix of the org where the app was developed

Have Fun!

0

Can you not try like this without doing any extra efforts? If you know any custom field or custom object of your managed package then the following solution should work.

If you have a custom object with Custom_Object__c API Name

Schema.DescribeSObjectResult dsr = Custom_Object__c.sObjectType.getDescribe();
String namespacePrefix = dsr.getName().remove(dsr.getLocalName());

It will return something like ns__

If you have a custom field with Custom_Field__c API Name

Schema.DescribeFieldResult dfr = Account.sObjectType.fields.Custom_Field__c.getDescribe();
String namespacePrefix = dfr.getName().remove(dfr.getLocalName());

I think this way you don't need to do any query on Apex class.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.