See the important note below
Recently we encountered some difficulties when trying to allow custom VisualForce pages be displayed in pages in our managed package. The intention was to allow users to create their own VisualForce page that would allow them enhance the information that they could see within our application. Initially we assumed that this would be a simple case of having an iframe that gets its src attribute from a custom setting value, however, this was more complicated than expected.
In this post we will explain how to get a valid base URL so you can add custom VisualForce pages to iframes in a page contained in a managed package.
Initially we thought a simple <apex:iframe/> element would work for including content from a custom (non-packaged) page called UserCustomPage.page, in an iframe on the managed package page called MyPackagePage.page:
The iframe in the page did not load correctly though:
When we inspected the iframe using the browser’s developer tools we see that the src attribute is https://tasblog.na11.visual.force.com/apex/UserCustomPage and it is the reference to the namespace of our managed package, in this case tasblog, that is the problem because the UserCustomPage is not in the package so the URL is invalid to access the page that is not in the package.
The System.URL.getSalesforceBaseUrl().toExternalForm() method in the Apex documentation suggests that it returns the URL of the Salesforce instance so we thought that we might be able to use this to construct a URL to the custom page. Again we use a managed package with a namespace of TASBLOG and in the package we want to have a VisualForce page called MyPackagePage.page with a controller called MyPackageController.cls that will contain an iframe with the content of a custom VisualForce from the page called UserCustomPage.page that is not in the package.
But the method works a little differently than we had expected. We had hoped that by just appending the name of the custom page to the result of the base url method would give us a valid URL to the page but this is not the case. When the method is called from within managed package code it returns a URL that references the package namespace just like the URL we got from the <apex:iframe/> and again the iframe did not load correctly.
A number of others have also experienced similar problems with this System.URL.getSalesforceBaseUrl().toExternalForm() approach for getting the base URL:
- developer.salesforce.com System.URL.getSalesforceBaseUrl()
- developer.salesforce.com getSalesforceBaseUrl returns http: not https:
- salesforce.stackexchange.com URL.getSalesforceBaseUrl() returns VF Subdomain URL
- stackoverflow.com Retrieve salesforce instance URL instead of visualforce instance
A number of the forum responses suggest using text replacements or regular expressions to attempt to get the correct version of the base URL but in practice we found this did not always work consistently as some of our customers have custom domains that can make it more complicated.
The <apex:output/> element gives a link with a URL that is the Salesforce org base URL and not the namespaced URL. This technique will also work with orgs that use custom domains. By putting the link in a hidden element we can avoid any unnessary clutter on the page and we can retrieve the URL we want with jQuery and then change the src of the iframe. This will give us the solution we want; a custom page that is not in a package being displayed within an iframe of a packaged page: