There are ways to surface the namespace itself from within Apex (doesn't help us in JavaScript, yet).
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
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
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.
you forgot to use @namespaceAccessible
annotation, in order to make public class accessible across different packages in one namespace. Here is a documentation
The @namespaceAccessible annotation marks public or protected Apex in
a package as available to other packages with the same namespace.
Unless explicitly annotated, Apex classes, methods, interfaces, and
properties defined in a 2GP package aren’t accessible to other
packages with which they share a namespace. There is no impact on Apex
that isn’t packaged.
you want to have like the following
@namespaceAccessible
public class MyPublicClass{
@namespaceAccessible
public MyPublicClass(){
//...
}
}
Best Answer
One of the approaches that I could think of is to utilize sort of a "marker interface" which determines if the trigger needs to be executed or not based on a flag. In any case, you need to handle this within your trigger logic even if you are able to identify the context of the running trigger. It's more a design choice how you will want to achieve this, but something as below will work in such cases.
Let's say I define a simple class as:
And in my trigger, I would use this class to branch the execution logic something as:
Now for all my test class written, I would have set this flag to true
IMarkerInterface.RUN_TRIGGER = true;
in my test methods, so that all my local test classes will always execute this logic.The ones from managed package will always skip the trigger logic, because they would not have had set this value.