8

I'm attempting to create the New button functionality on a visualforce page that will be hosted as a section of detailed page layout. This vf page will surface child records of a parent detail page record . Think of this as a manually constructed 'related list' ( which also includes a New button for adding records )

My requirement: Pressing the New button should take one to the standard salesforce New page for the child record with the parent record lookup pre-populated.

I suspect the answer can be found in the construction of an appropriate url but this is where I hit a slight snag. The vf page will be part of a managed package so I will have no obvious way (to my mind at least) of grabbing the custom field Id of the parent lookup field. (ie hard coded lkids are not an option)

All ideas are very much appreciated

8

A Tooling API Solution

I've been able to try out a hunch I've had since I noticed CustomField support in the Tooling API and have worked up a means to dynamically query the Custom Field Id's via the supported Salesforce Tooling API. My fresh from the press blog entry here goes into more details, though here is a code sample taken from the accompanying GitHub repo.

// Constructs the Tooling API wrapper (default constructor uses user session Id)
ToolingAPI toolingAPI = new ToolingAPI();

// Query CustomObject object by DeveloperName (note no __c suffix required)
List<ToolingAPI.CustomObject> customObjects =
     toolingAPI.queryCustomObject('DeveloperName = \'Test\'').records;

// Query CustomField object by TableEnumOrId (use CustomObject Id not name for Custom Objects)
ToolingAPI.CustomObject customObject = customObjects[0];
Id customObjectId = customObject.Id;
List<ToolingAPI.CustomField> customFields =
     toolingAPI.queryCustomField('TableEnumOrId = \'' + customObjectId + '\'').records;

// Dump field names (reapply the __c suffix) and their Id's
System.debug(customObject.DeveloperName + '__c : ' + customObject.Id);
for(ToolingAPI.CustomField customField : customFields)
     System.debug(
          customObject.DeveloperName + '__c.' +
          customField.DeveloperName + '__c : ' +
          customField.Id);

This results in ....

00:27:38.845 (845882069)|USER_DEBUG|[45]|DEBUG|Test__c : 01IG00000021cXoMAI

00:27:38.846 (846249350)|USER_DEBUG|[47]|DEBUG|Test__c.A_Number__c : 00NG0000009Y0I9MAK

00:27:38.846 (846305290)|USER_DEBUG|[47]|DEBUG|Test_c.Colours_c : 00NG0000009prwyMAA

00:27:38.846 (846328856)|USER_DEBUG|[47]|DEBUG|Test_c.Date_c : 00NG0000009BrnxMAC

00:27:38.846 (846513094)|USER_DEBUG|[47]|DEBUG|Test_c.Message_c : 00NG0000009Y0IOMA0

00:27:38.846 (846535746)|USER_DEBUG|[47]|DEBUG|Test_c.MultiPick_c : 00NG000000AcULrMAN

00:27:38.846 (846558753)|USER_DEBUG|[47]|DEBUG|Test_c.MyCheckbox_c : 00NG0000009Y5C8MAK

00:27:38.846 (846741056)|USER_DEBUG|[47]|DEBUG|Test_c.RichText_c : 00NG0000009XaRJMA0

00:27:38.846 (846816183)|USER_DEBUG|[47]|DEBUG|Test_c.Text_c : 00NG0000009prxwMAA

Summary. There CustomField object has a little amount of queryable fields this way, basically DeveloperName, TableEnumOrId (custom object Id or standard object name) and NamespacePrefix. These should allow you to dynamically query for most use cases. Keep in mind the solution uses a callout from Apex to the Tooling API (REST version), so you will need to have a Remote Site setting for the Tooling API end point added to the org.

  • 3
    Yesssssssssssss! ToolingApi.CustomField.Id - great hunch! – bigassforce Jan 5 '14 at 1:40
4

You need to squirt parameters into the URL of the 'new record' page. CF00N000000000000AAA for the text and CF00N000000000000AAA_lkid for the Id. Where the 00N... corresponds to the field's id.

The 00N prefix is the Custom Fields & Relationships object type. It cannot be queried through standard objects, custom objects or the metadata API. Widely encountered problem: vote on the idea now.

But to actually determine the parameters, there's a couple ways I've seen:

  1. eyeballing the page source for the 00N000000000000AAA strings and baking them into a URL,

  2. creating a Visualforce Page to insert the detail record and immediately redirect to its edit page,

  3. scraping (yeah... ouch) the 'New' HTML to extract the lkid bits and prepare a URL on the fly

The second option will have a habit of failing security review. Here's candidate code for the third:

public String urlForDetailOnMaster(String masterFriendlyName, String masterFifteenCharId) {
    //replace Master__c and Detail__c with your API names as appropriate
    String masterLabel = Schema.SObjectType.Detail__c.fields.Master__c.Label;
    String newDetailUrl = '/' + Schema.SObjectType.Detail__c.KeyPrefix + '/e';

    //this regular expression will find a form field label referencing the field id
    String expr
        = Pattern.quote('<label for="CF') + '(00N[A-Za-z0-9]{12})' //capture it
        + Pattern.quote('"><span class="requiredMark">*</span>' + masterLabel + '</label>')
    ;

    //pull down the markup of a 'new' page (butchery)
    String html = new System.PageReference(newDetailUrl).getContent().toString();
    Matcher m = Pattern.compile(expr).matcher(html);
    m.find();

    //here's your 00N
    String fieldId = m.group(1);

    //construct a URL which will display the parent lookup pre-populated
    System.PageReference pr = new System.PageReference(newDetailUrl);
    pr.getParameters().putAll(new Map<String,String>{
        'CF' + fieldId => masterFriendlyName,
        'CF' + fieldId + '_lkid' => masterFifteenCharId
    });

    return pr.getUrl();
}

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.