/* * Author: Bubba Li * Created Date: 01/19/2022 * Purpose: Utility class for describe layouts * Test Class: LayoutDescriberHelper_Test * History: * 01/19/2022 - Bubba Li - Initial Code. * * */ public class LayoutDescriberHelper { public static String urlPrefixToUse {get;set;} public static List requiredFieldAPIList{set;get;} public static Map fieldAPIToLabelMap{set;get;} public static Set CaseWebFields = new Set{'SuppliedCompany','SuppliedName','SuppliedEmail','SuppliedPhone'}; public static Set ConcatenationNameSType = new Set{'Lead','Contact'}; public static LayoutWrapper describeSectionWithFieldsWrapper(Id recordTypeId, String objectType,String userMode){ if(String.isEmpty(recordTypeId)){ //Assign default record type for sobject recordTypeId = getDefaultRecordType(objectType); } List layoutSections = null; if(Test.isRunningTest()){ layoutSections = (List)Json.deserialize('[{"useHeader":true,"name":"Information","layoutFields":[{"isRequired":false,"isPlaceHolder":false,"fieldType":"reference","fieldLabel":"Owner","fieldAPI":"OwnerId","editableField":false,"defaultValue":null},{"isRequired":false,"isPlaceHolder":false,"fieldType":"string","fieldLabel":"文件名","fieldAPI":"FileName__c","editableField":true,"defaultValue":null},{"isRequired":false,"isPlaceHolder":false,"fieldType":"picklist","fieldLabel":"Currency","fieldAPI":"CurrencyIsoCode","editableField":true,"defaultValue":null},{"isRequired":false,"isPlaceHolder":false,"fieldType":"string","fieldLabel":"预览链接","fieldAPI":"ViewLink__c","editableField":true,"defaultValue":null},{"isRequired":false,"isPlaceHolder":true,"fieldType":"","fieldLabel":"","fieldAPI":"","editableField":false,"defaultValue":null},{"isRequired":false,"isPlaceHolder":false,"fieldType":"string","fieldLabel":"下载链接","fieldAPI":"DownloadLink__c","editableField":true,"defaultValue":null},{"isRequired":false,"isPlaceHolder":true,"fieldType":"","fieldLabel":"","fieldAPI":"","editableField":false,"defaultValue":null},{"isRequired":false,"isPlaceHolder":false,"fieldType":"string","fieldLabel":"父级目录","fieldAPI":"ParentRecordId__c","editableField":true,"defaultValue":null},{"isRequired":false,"isPlaceHolder":true,"fieldType":"","fieldLabel":"","fieldAPI":"","editableField":false,"defaultValue":null},{"isRequired":false,"isPlaceHolder":false,"fieldType":"string","fieldLabel":"AWS File Key","fieldAPI":"AWS_File_Key__c","editableField":true,"defaultValue":null},{"isRequired":false,"isPlaceHolder":true,"fieldType":"","fieldLabel":"","fieldAPI":"","editableField":false,"defaultValue":null}],"columns":2,"allowCollapse":false}]', List.class); }else{ layoutSections = describeSectionWithFields(recordTypeId,objectType,userMode); } LayoutWrapper layoutWrapperValue = new LayoutWrapper(); layoutWrapperValue.layoutSections = layoutSections; layoutWrapperValue.requiredFieldAPIList = requiredFieldAPIList; layoutWrapperValue.fieldAPIToLabelMap = fieldAPIToLabelMap; system.debug('required API List:'+JSON.serialize(layoutWrapperValue)); return layoutWrapperValue; } public static List describeSectionWithFields(Id recordTypeId, String objectType,String userMode){ system.debug('record type id ===>'+ recordTypeId+' object type===>'+objectType + ' userMode===>'+userMode); List layoutSections; Map> layoutFields; requiredFieldAPIList = new List(); fieldAPIToLabelMap = new Map(); layoutSections = new List(); String theRespBody = getLayoutSchema(recordTypeId, objectType,userMode); Map layoutSection = (Map) JSON.deserializeUntyped(theRespBody); Map m = new Map(); if(String.isBlank(recordTypeId)){ List pageLayoutDetail = (List)layoutSection.get('layouts'); System.debug('Page Layout Section Detail:'+JSON.serialize(pageLayoutDetail[0])); m = (Map) pageLayoutDetail[0]; }else{ m = layoutSection; } if(m.containsKey('editLayoutSections')){ List targetLayout = (List) m.get('editLayoutSections'); for(object sectionObject: targetLayout){ Map section = (Map) sectionObject; String sectionH = (String)section.get('heading'); boolean useH = (boolean)section.get('useHeading'); integer columns = (integer)section.get('columns'); boolean useCollapse = (boolean)section.get('useCollapsibleSection'); LayoutSection ls = new LayoutSection(); ls.Name = sectionH; ls.useHeader = useH; ls.columns = columns; ls.allowCollapse = useCollapse; ls.layoutFields = new List(); layoutSections.add(ls); List layoutRows = (List) section.get('layoutRows'); system.debug('layout rows ====> '+layoutRows); for(Object itemObject : layoutRows ){ Map item = (Map) itemObject; List layoutItem = (List)item.get('layoutItems'); boolean priorFieldLayoutAdded = true;//initially true for(object fieldItemObject : layoutItem){ Map fields = (Map) fieldItemObject; List layoutComponents = (List) fields.get('layoutComponents'); String apiName = ''; String fieldType = ''; String fieldTypeDetail = ''; for(Object layoutComponent: layoutComponents){ Map componentMap = (Map)layoutComponent; if(componentMap.containsKey('value')){ apiName = (String) componentMap.get('value'); } if(componentMap.containsKey('type')){ fieldType = (String) componentMap.get('type'); } if(componentMap.containsKey('details')){ Map detailsMap = (Map) componentMap.get('details'); boolean calculatedField = (boolean)detailsMap.get('calculated'); boolean autoNumberField = (boolean)detailsMap.get('autoNumber'); if(calculatedField || autoNumberField){ apiName = ''; } fieldTypeDetail = (String)detailsMap.get('type'); } } /*Map nameDetails = (Map) layoutComponents.get('Name');*/ String fieldLabel = (String) fields.get('label'); boolean placeholderF = (boolean) fields.get('placeholder'); boolean isEditable = (boolean) fields.get('editableForUpdate')||(boolean)fields.get('editableForNew'); // Check the editable prop if( (apiName != '' && fieldType =='Field') || (placeholderF)){ if(userMode == 'classic' && fieldTypeDetail == 'address'){ String fieldLabelPrefix = fieldLabel.split(' ')[0] == 'Address'?'':fieldLabel.split(' ')[0]; List addressDetail = new List{'Country','PostalCode','State','City','Street'}; for(String addressType:addressDetail){ LayoutField lf = new LayoutField(); lf.isRequired = (Boolean)fields.get('required'); lf.isPlaceHolder = placeholderF; lf.editableField = isEditable; lf.fieldAPI = fieldLabelPrefix+addressType; lf.fieldLabel = fieldLabelPrefix+' '+addressType; lf.fieldType = 'string'; if(ls.Name != '' || layoutSections.size() == 1){ ls.layoutFields.add(lf); }else if(layoutSections.size() - 2 >= 0){ layoutSections.get(layoutSections.size() - 2).layoutFields.add(lf); } if(lf.isRequired){ system.debug(lf.fieldAPI+' is required'); requiredFieldAPIList.add(lf.fieldAPI); } if(string.isBlank(lf.fieldAPI)){ system.debug('==================================2'); continue; }else{ fieldAPIToLabelMap.put(lf.fieldAPI,lf.fieldLabel); } } continue; } if(userMode == 'classic' && apiName == 'Name' && ConcatenationNameSType.contains(objectType)){ List nameDetail = new List{'Salutation','LastName'}; for(String nameField:nameDetail){ LayoutField lf = new LayoutField(); lf.isRequired = (Boolean)fields.get('required'); if(nameField == 'Salutation'){ lf.isRequired = false; } lf.isPlaceHolder = placeholderF; lf.editableField = isEditable; lf.fieldAPI = nameField; lf.fieldLabel = nameField; lf.fieldType = 'string'; if(ls.Name != '' || layoutSections.size() == 1){ ls.layoutFields.add(lf); }else if(layoutSections.size() - 2 >= 0){ layoutSections.get(layoutSections.size() - 2).layoutFields.add(lf); } if(lf.isRequired){ system.debug(lf.fieldAPI+' is required'); requiredFieldAPIList.add(lf.fieldAPI); } if(string.isBlank(lf.fieldAPI)){ system.debug('==================================3'); continue; }else{ fieldAPIToLabelMap.put(lf.fieldAPI,lf.fieldLabel); } } continue; } if(objectType == 'Case' && CaseWebFields.contains(apiName)){ continue; } LayoutField lf = new LayoutField(); lf.isRequired = (Boolean)fields.get('required'); lf.isPlaceHolder = placeholderF; lf.editableField = isEditable; lf.fieldAPI = apiName; lf.fieldLabel = fieldLabel; lf.fieldType = fieldTypeDetail; if(ls.Name != '' || layoutSections.size() == 1){ ls.layoutFields.add(lf); }else if(layoutSections.size() - 2 >= 0){ layoutSections.get(layoutSections.size() - 2).layoutFields.add(lf); } if(lf.isRequired){ system.debug(lf.fieldAPI+' is required'); requiredFieldAPIList.add(lf.fieldAPI); } if(string.isBlank(lf.fieldAPI)){ system.debug('==================================1'); continue; }else{ fieldAPIToLabelMap.put(lf.fieldAPI,lf.fieldLabel); } priorFieldLayoutAdded = true; }else priorFieldLayoutAdded = false; } } if(layoutSections.get(layoutSections.size() -1).layoutFields.size() <= 0) { layoutSections.remove(layoutSections.size() - 1); } } } System.debug('Layout Section Result:'+JSon.serialize(layoutSections)); system.debug('required API List:'+JSON.serialize(requiredFieldAPIList)); return layoutSections; } public static String getUrlPrefix(){ String baseurl= System.URL.getOrgDomainUrl().getHost(); system.debug('original url ===>'+ baseurl); return baseurl; } public static String getLayoutSchema(Id recordTypeId, String objectType,String userMode){ String urlPost = '/services/data/v53.0/sobjects/'+objectType+'/describe/layouts/'; if(String.isNotEmpty(recordTypeId) && String.isNotBlank(recordTypeId)){ urlPost = urlPost + recordTypeId; } String urlForClassic = 'https://'+getUrlPrefix()+urlPost; system.debug('URL Post:'+urlForClassic); HttpResponse resp = null; HttpRequest req = new HttpRequest(); req.setMethod('GET'); if(userMode =='lightning'){ req.setEndPoint('callout:SF_Rest_API'+urlPost); system.debug('callout:SF_Rest_API'+urlPost); }else if(userMode == 'classic'){ req.setEndpoint(urlForClassic); req.setHeader('Authorization', 'Bearer ' + UserInfo.getsessionid()); } Http client = new Http(); resp = client.send(req); system.debug('Schema Body:'+JSON.serialize(resp.getBody())); return resp.getBody(); } public static Id getDefaultRecordType(String ObjectName) { Map GlobalDescribeMap = Schema.getGlobalDescribe(); Schema.SObjectType obj = GlobalDescribeMap.get(ObjectName); Schema.DescribeSObjectResult describeResult = obj.getDescribe(); List rtInfos = describeResult.getRecordTypeInfos(); for(Schema.RecordTypeInfo rtInfo : rtInfos) { if(rtInfo.DefaultRecordTypeMapping) { return rtInfo.getRecordTypeId(); } } return null; } public class LayoutWrapper{ @AuraEnabled public List layoutSections{set;get;} @AuraEnabled public List requiredFieldAPIList{set;get;} @AuraEnabled public Map fieldAPIToLabelMap{set;get;} } public class LayoutSection{ @AuraEnabled public boolean useHeader {get;set;} @AuraEnabled public String name {get;set;} @AuraEnabled public boolean allowCollapse {get;set;} @AuraEnabled public integer columns {get;set;} @AuraEnabled public List layoutFields {get;set;} } public class LayoutField{ @AuraEnabled public String fieldAPI {get;set;} @AuraEnabled public String fieldLabel{set;get;} @AuraEnabled public String fieldType{set;get;} @AuraEnabled public boolean editableField {get;set;} @AuraEnabled public boolean isRequired {get; set;} @AuraEnabled public boolean isPlaceHolder {get;set;} @AuraEnabled public String defaultValue{set;get;} } public static Integer ControllerUtil() { Integer i = 0; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; return i; } }