Outputting a URL that has keys that only differ by case

There is a problem with the code in Hack to find field ids – allows a default UI “New” page to be pre-populated related to case sensitivity. The field ids are case sensitive so it is possible (and if your object has many fields likely) that field ids may only differ by case. Such fields are not correctly defaulted in the “New” page.

While specifications like RFC 3986 suggest that query string keys can be assumed to be case sensitive, the PageReference class in Force.com works hard to ensure that query string keys are not case sensitive. These tests illustrate the problem:

@isTest
static void testMap() {
    Map<String, String> m = new Map<String, String>();
    m.put('00N50000002ldUw', 'abc');
    m.put('00N50000002ldUW', '123');
    // Assertions are as expected
    System.assertEquals('abc', m.get('00N50000002ldUw'));
    System.assertEquals('123', m.get('00N50000002ldUW'));
}
@isTest
static void testParameters() {  
    PageReference p = new PageReference('/');
    Map<String, String> m = p.getParameters();
    m.put('00N50000002ldUw', 'abc');
    m.put('00N50000002ldUW', '123');
    // Assertions illustrate that keys that differ by case are not distinguished
    System.assertEquals('123', m.get('00N50000002ldUw'));
    System.assertEquals('123', m.get('00N50000002ldUW'));
}

There does not appear to be a work-around using PageReference; building the entire URL string manually and then passing it into the constructor doesn’t help, nor does replacing the alphabetic characters with their URL encoded form. The other standard practice of replacing a (case sensitive) 15 digit id with the (case insensitive) 18 character version doesn’t work in this case because the “New” page appears to be doing literal string matching of the ids and so is expecting the 15 character version.

A way of avoiding the PageReference class is to build the URL as a string and then use JavaScript to redirect to the URL. These changes to the code in Hack to find field ids – allows a default UI “New” page to be pre-populated fix the case problem and result in all fields being defaulted. The controller returns a URL string:

private String createUrl() {
    String url = '/' + DescribeSObjectResultCache.get(Abc__c.SObjectType).getKeyPrefix()
            + '/e'
            + '?retURL=' + ApexPages.currentPage().getParameters().get('retURL')
            + '&nooverride=1'
            ;
    Map m = createDefaultValues();
    for (String key : m.keySet()) {
        url += '&' + key + '=' + EncodingUtil.urlEncode(m.get(key), 'UTF-8');
    }
    return url;
}
public String url {
    get {
        if (url == null) {
            url = createUrl();
        }
        return url;
    }
    private set;
}

and the page is rendered but immediately redirects to the URL supplied by the controller:

<apex:page standardController="Abc__c" extensions="AbcNewController" showHeader="false" sidebar="false" standardStylesheets="false">
<script type="text/javascript">
window.onload = function() {
    window.location = '{! url }';
}
</script>
</apex:page>
Advertisements

14 thoughts on “Outputting a URL that has keys that only differ by case

  1. Good to see you solving the problems, and corner cases. We should for sure post the idea to have field id’s available in apex describe information ! If you haven’t posted this as idea, we can post and vote ?

  2. Our org does a lot of pass through values from standard objects to custom objects and we have been using hard-coded ids and have continually run into the case sensitive id issue. I love your solution to the problem but am having a proble with _lkid fields. Can you give me an example of how you handled these type fields when creating your maps? I can’t see where there are 2 different labels for these.

  3. Thank you for the quick reply. I now have everything working but am having a hard time figuring out how to throw messages back to the visual force page so I can show errors to my users with a link that would take them back to the page they started on. Before I used your solution I used
    Return

    to display the error and provide the link. Do you have any suggestions of how to do this or of a good resource for apex/javascript/visualforce integration resources.

    • I see that the code I had put in my previous comment is no longer there, it just shows the ‘Link’ Return. I can get that, what I can’t seem to get it to do is thrown the actual error message back to the page. My code in my controller is throwExceptionMsg(InValidMsg,’warning’); which calls
      public void throwExceptionMsg(String message, String severity){
      if (severity.equalsIgnoreCase(‘warning’)) {
      System.debug(‘Throwing this warning message : ‘ + message);
      ApexPages.Message msg= new ApexPages.Message(ApexPages.Severity.WARNING, message, message);
      ApexPages.addmessage(msg);
      } else { //assume error
      ApexPages.Message msg= new ApexPages.Message(ApexPages.Severity.ERROR, message, message);
      ApexPages.addmessage(msg);
      }
      }

      and then my page looks like

      Return

      window.onload = function() {
      //alert(“{! url}”);
      if (“{! url }”) {
      window.location = ‘{! url }’;
      }
      }

      Any ideas as to what I am doing wrong??

  4. OK I understand better now.

    This mechanism is moving to a completely new page and so is discarding the state from the preceding page including the messages you are adding. If you do need to pass messages between the two pages one way would be to add them as a parameter in the URL you create and for the new page to extract them and turn them back into added messages. From a quick Google, there is a very basic example here http://salesforce.stackexchange.com/questions/15268/visual-force-error-message-in-a-new-page.

  5. I was able to get what I needed by making a couple of changes to your code. Below is the change to the page followed by the contrller changes. Thanks for all your help!!

    if (“{! url }”) {
    window.location = ‘{! url }’;
    }

    Return

    and these changes to the controller:
    public CRM_OverrideNew(ApexPages.StandardController controller) {
    this.controller = controller;
    url = createurl();
    }
    public string url{get;set;}

    replaced:
    public String url {

    get {
    if (url == null) {
    url = createUrl();
    }
    return url;
    }
    private set;
    }
    Now when the user pushes the button to create a new custom request, fields are autopoulated with values when the form opens and if an error occurs, the messages is diplayed on a page with a link back to the page the button was on.

  6. I was not able to pre-populate all the fields I wanted on my custom form and couldn’t understand why. I discovered that the html of the page I am populating doesn’t show all of the labels on the form. I found this out by debugging and outputting the result of String html = p.getContent().toString(); , yet if I look at the html of the page by viewing the source all the labels are there. Any ideas as to why the entire html not being retrieved with String html = p.getContent().toString(); ??

    • I am the system administrator so field level security shouldn’t be an issue. I like the suggestion for the tooling API but I am not familiar with API and WSDL development

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s