Andy in the Cloud

From BBC Basic to Force.com and beyond…

25 thoughts on “Spring’14 Pre-Release : Updating Layouts in Apex

  1. Pingback: Apex Metadata API and Spring’14 : Keys to the Kingdom! | Andy in the Cloud

  2. Hi, Thanks for the detailed information.

    But, I am facing an issue in adding the field on page layout and getting the below message when I run the code from “Developer Console “.

    EnergyAcuity.MetadataServiceExamples.MetadataServiceExamplesException: Error occured processing component null. Required field is missing: fullName (FIELD_INTEGRITY_EXCEPTION).

    Please help me .

  3. Thanks alot Andreaw, actually mine is managed package and we need to prefix the name space with layout name also.I am able to add the field on the page layout now

  4. One more question, will it be possible with Standard object also , say I want to do the same activity on Account Object as well.
    I tried below combination but still it failed with same error :
    1. MetadataService.Layout layout =
    (MetadataService.Layout) service.readMetadata(‘Layout’,
    new String[] { ‘Account-EnergyAcuity__Account(Support) Layout’ }).getRecords()[0];

    2. MetadataService.Layout layout =
    (MetadataService.Layout) service.readMetadata(‘Layout’,
    new String[] { ‘Account-Account(Support) Layout’ }).getRecords()[0];

    3. MetadataService.Layout layout =
    (MetadataService.Layout) service.readMetadata(‘Layout’,
    new String[] { ‘EnergyAcuity__Account-EnergyAcuity__Account(Support) Layout’ }).getRecords()[0];

    Here EnergyAcuity is namespace and Account(support) is a pagelayout which is not a part of any managed package aswell.

    Please let me know your thoughts

  5. Andy,

    Your blogs and code have really helped me out quite a lot over the past few weeks as we are building out a new managed package. Thank you very much and very sincerely. I do have a question but before I ask, I thought to share a code snippet which creates custom fields **and** adds these to a layout with a known name, e.g. configuration by convention. I did quite a lot of searching on your blogs and google and found piecemeal solutions but perhaps this handy method might help other developers.


    private static void addFieldsAndUpdateLayouts(Set newFooBarIDs, String sessionId)
    {
    MetadataService.MetadataPort service = new MetadataService.MetadataPort();
    service.SessionHeader = new MetadataService.SessionHeader_element();
    service.SessionHeader.sessionId = sessionId;

    List bar =
    [SELECT Name, ReferenceObjectName__c
    FROM FooBar__c
    WHERE Id IN :newFooBarIDs];

    List addMetas = new List();
    List updateMetas = new List();
    for(FooBar__c attribute : bar)
    {
    String fieldApiName = StringUtil.underscoreField(attribute.Name);

    // Add lookup to Price Object
    MetadataService.CustomField myCustomField = new MetadataService.CustomField();
    myCustomField.fullName = 'FooObject__c' + '.' + fieldApiName;
    myCustomField.deleteConstraint = 'SetNull';
    myCustomField.externalId = false;
    myCustomField.label = attribute.Name;
    myCustomField.referenceTo = attribute.ReferenceObjectName__c;
    myCustomField.relationshipLabel = 'Foo Objects';
    myCustomField.relationshipName = 'FooObjects';
    myCustomField.required = false;
    myCustomField.trackHistory = false;
    myCustomField.trackTrending = false;
    myCustomField.type_x = 'Lookup';
    addMetas.add(myCustomField);

    // Read and Update Layout
    MetadataService.Layout layout =
    (MetadataService.Layout) service.readMetadata('Layout',
    new String[] { 'FooObject__c-Foo Object Layout' }).getRecords()[0];
    for( MetadataService.LayoutSection section : layout.layoutSections)
    {
    if(section.label == 'Foo Bar') // By Convention
    {
    if(section.layoutColumns == null)
    {
    MetadataService.LayoutColumn newLayoutColumn = new MetadataService.LayoutColumn();
    section.layoutColumns = new List { newLayoutColumn };
    }

    if(section.layoutColumns[0].layoutItems == null)
    {
    section.layoutColumns[0].layoutItems = new List();
    }

    MetadataService.LayoutItem newLayoutItem = new MetadataService.LayoutItem();
    newLayoutItem.field = fieldApiName;
    section.layoutColumns[0].layoutItems.add(newLayoutItem);

    break;
    }
    }
    updateMetas.add(layout);
    }

    // TODO - will likely need to synchronize tho I haven't seen a problem yet...lucky
    service.createMetadata(addMetas);
    service.updateMetadata(updateMetas);
    }

    • Thanks so much for sharing this! This is what open source is all about! Can I ask that you update the MetadataServiceExamples.cls with this? Using GitHub you can raise a Pull Request and I can merge. Failing that, if you prefer just raise a GitHub Issue and paste to that and I’ll gladly add, thanks for the contribution!

  6. Hello Andy,

    I am trying to delete custom fields in v31 using code very similar to this example:

    MetadataService.MetadataPort service = createService();
    List results =
    service.deleteMetadata(
    ‘CustomField’, new String[] { ‘Test__c.TestField__c’ });
    handleDeleteResults(results[0]);

    But Salesforce is returning the following error:

    13:31:45.006 (1006981199)|FATAL_ERROR|System.CalloutException: Web service callout failed: WebService returned a SOAP Fault: null: type and fullNames must be specified for items to delete faultcode=sf:UNKNOWN_EXCEPTION faultactor=

    Any idea off the top of your head what might be the problem here?

    Thanks,
    json

    • That is most odd, i’m traveling at the moment, but this looks good. Can you raise for further investigation via the GitHub Issue list and I’ll take a further look probably later today or early next week. Thanks again for your kind words and contribution!

  7. What about creating a new layout? Is that possible with Apex?

  8. It seems there is a restriction on creating new layouts based on managed package custom objects. Your otherwise awesome samples work on a new object I created, but failed on a managed package object.

  9. Pingback: Unable to cast type Metadata to type Layout | DL-UAT

  10. Hi,

    Does anybody have an idea why i can’t cast Metadata object to any of it`s subclasses?
    I get an exception on this line:

    MetadataService.Layout layout =
    (MetadataService.Layout) service.readMetadata(‘Layout’,
    new String[] { ‘Test__c-Test Layout’ }).getRecords()[0];

    I’m using C#. Thanks!

  11. Hi Andy,

    I tried to use readMetadata method to read layout, which give me result without data (means returns null for values). But When I use “Zipping method”, it returns layout data properly. I don’t get why its not working. I tried to use it in another test org and it works fine there.

    Can you please suggest why its happening or Am I missing something?

    I am using this:
    MetadataService.MetadataPort service = createService();
    List layouts = (MetadataService.Layout)service.readMetadata(‘Layout’, new String[] {‘Account-Account Layout’}).getRecords(0);

    Serialised response:
    {“fullName”:null,”type_att_info”:[“xsi:type”],”type”:”Layout”,”summaryLayout_type_info”:[“summaryLayout”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”1″,”false”],”summaryLayout”:null,”showSubmitAndAttachButton_type_info”:[“showSubmitAndAttachButton”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”1″,”false”],”showSubmitAndAttachButton”:null,”showSolutionSection_type_info”:[“showSolutionSection”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”1″,”false”],”showSolutionSection”:null,”showRunAssignmentRulesCheckbox_type_info”:[“showRunAssignmentRulesCheckbox”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”1″,”false”],”showRunAssignmentRulesCheckbox”:null,”showKnowledgeComponent_type_info”:[“showKnowledgeComponent”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”1″,”false”],”showKnowledgeComponent”:null,”showInteractionLogPanel_type_info”:[“showInteractionLogPanel”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”1″,”false”],”showInteractionLogPanel”:null,”showHighlightsPanel_type_info”:[“showHighlightsPanel”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”1″,”false”],”showHighlightsPanel”:null,”showEmailCheckbox_type_info”:[“showEmailCheckbox”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”1″,”false”],”showEmailCheckbox”:null,”runAssignmentRulesDefault_type_info”:[“runAssignmentRulesDefault”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”1″,”false”],”runAssignmentRulesDefault”:null,”relatedObjects_type_info”:[“relatedObjects”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”-1″,”false”],”relatedObjects”:null,”relatedLists_type_info”:[“relatedLists”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”-1″,”false”],”relatedLists”:null,”relatedContent_type_info”:[“relatedContent”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”1″,”false”],”relatedContent”:null,”quickActionList_type_info”:[“quickActionList”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”1″,”false”],”quickActionList”:null,”multilineLayoutFields_type_info”:[“multilineLayoutFields”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”-1″,”false”],”multilineLayoutFields”:null,”miniLayout_type_info”:[“miniLayout”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”1″,”false”],”miniLayout”:null,”layoutSections_type_info”:[“layoutSections”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”-1″,”false”],”layoutSections”:null,”headers_type_info”:[“headers”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”-1″,”false”],”headers”:null,”fullName_type_info”:[“fullName”,”http://www.w3.org/2001/XMLSchema”,”string”,”0″,”1″,”false”],”fullName”:null,”field_order_type_info”:[“fullName”,”customButtons”,”customConsoleComponents”,”emailDefault”,”excludeButtons”,”feedLayout”,”headers”,”layoutSections”,”miniLayout”,”multilineLayoutFields”,”quickActionList”,”relatedContent”,”relatedLists”,”relatedObjects”,”runAssignmentRulesDefault”,”showEmailCheckbox”,”showHighlightsPanel”,”showInteractionLogPanel”,”showKnowledgeComponent”,”showRunAssignmentRulesCheckbox”,”showSolutionSection”,”showSubmitAndAttachButton”,”summaryLayout”],”feedLayout_type_info”:[“feedLayout”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”1″,”false”],”feedLayout”:null,”excludeButtons_type_info”:[“excludeButtons”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”-1″,”false”],”excludeButtons”:null,”emailDefault_type_info”:[“emailDefault”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”1″,”false”],”emailDefault”:null,”customConsoleComponents_type_info”:[“customConsoleComponents”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”1″,”false”],”customConsoleComponents”:null,”customButtons_type_info”:[“customButtons”,”http://soap.sforce.com/2006/04/metadata”,null,”0″,”-1″,”false”],”customButtons”:null,”apex_schema_type_info”:[“http://soap.sforce.com/2006/04/metadata”,”true”,”false”]}

  12. Hi Andy,

    I am not able to figure out field api name from alias as Metadata api return field alias instead of api name while reading fields in related list of page layout using readMetadata().

    Can you please suggest a way by which field api name can be figured out from alias?

    Thanks

  13. Can yuo please tell me how to delete roles using metadata api.
    Right now I am getting an error saying that type is not a valid datatype for metadata to delete.
    Web service callout failed: WebService returned a SOAP Fault: null: type is not a valid metadata type for deleting faultcode=sf:UNKNOWN_EXCEPTION faultactor=

  14. With the help of below code I am trying to update layout like remove related list. After using NameSpace in my ORG, I am facing this problem, earlier I were not using NameSpace in my ORG than code was running perfectly.

    I have a Fund__c object as a Parent and Fundraising__c as a child object. In my related list Fundraising__c I added custom field Legal_Name__c as a column. If I remove this custom field, code run perfectly. But with namespace its not working throwing error.

    Please provide me solution its urgent.

    Thanks in advance.

    Please find the code below.

    MetadataService.MetadataPort service = new MetadataService.MetadataPort();
    service.SessionHeader = new MetadataService.SessionHeader_element();
    service.SessionHeader.sessionId = UserInfo.getSessionId();

    MetadataService.Layout layout = (MetadataService.Layout)service.readMetadata(‘Layout’,new String[] {‘navpeIIll__Fund__c-navpeIIll__Fund Layout’}).getRecords()[0];
    system.debug(‘layout===========’+layout);
    List lstMD= layout.relatedLists;
    system.debug(‘lstMD===========’+lstMD);

    for(integer i=0;i<lstMD.size();i++){
    MetadataService.RelatedListItem mdsR= lstMD[i];
    system.debug('lstMD==========='+mdsR);
    if(mdsR.relatedList== 'navpeIIll__Fundraising__c.navpeIIll__Fund__c'){
    layout.relatedLists.remove(i);
    system.debug('layout====50======='+layout);
    }
    }
    MetadataServiceExamples.handleSaveResults(service.updateMetadata(new MetadataService.Metadata[] { layout })[0]);

  15. Hi Andy, Is there a way to get the name of the layout based on logged in user and record type?

    In above example, we hard code name of the layout to get it’s metadata information.
    MetadataService.Layout layout =
    (MetadataService.Layout) service.readMetadata(‘Layout’,
    new String[] { ‘Test__c-Test Layout’ }).getRecords()[0];

    My use case is to read logged in user’s standard detail component of lightning record page and use this to recreate custom detail component.

    • I am curious why you want to recreate the standard detail layout tbh but I know your not asking that. But still check out the Lightning components that help you render this and customize that layout such as lightning:inputField. Meanwhile check out the Salesforce User Interface API or in Apex you can get this via a Apex Describe. 👍🏻

Leave a comment