Monday, 22 May 2017

Salesforce Certified Force.com Developer – Spring ’17 Release Exam

1. Who can edit a user's favorites in Lightning Experience?
A. The system administrator
B. The user
C. The user's manager
D. Any user in the same profile
Ans: B
---------------------
2. Which three actions does the Global Action Menu Support?
Choose 3 answers
A. Create records
B. Take notes
C. Create posts
D. Launch Visualforce
E. Delete records
Ans: A, B, D
----------------------------------
3. When an old post with a file is shared in Lightning Experience, what happens to the file in the newly shared post?
A. The file is attached to the new post
B. A link to the old post is shared.
C. The file is not shared in the new post.
D. A link to the file is shared.
Ans: C
-----------------------------
4. How many duplicate account records can be merged in Lightning Experience at once?
A.Up to 5
B.Up to 6
C.Up to 10
D.Up to 3
Ans: D 
-----------------------------
5. Which three predefined values can be set with send email action?
A.To
B.Bcc
C.Cc
D.Reply
E.Forward
Ans: A, B, C

Thursday, 2 March 2017

Creating Sobject or List of Sobject Dynamically in Apex Saelsforce

public class Utility {
    public static SObject createDynamicObject(String objectApiName) {        
        return (SObject)Type.forName(objectApiName).newInstance();
    }
    public static List createDynamicList4object(String objectApiName) {
        return (List)Type.forName('List<'+objectApiName+'>').newInstance();
    }
    /*** Usage ***
    List recs = Utility.createDynamicList4object('Contact');
    SObject rec1 = Utility.createDynamicObject('Contact');
    rec1.put('LastName','March2017-3');
    SObject rec2 = Utility.createDynamicObject('Contact');
    rec2.put('LastName','March2017-4');
    recs.add(rec1);
    recs.add(rec2);
    insert recs;
    */
}

Tuesday, 3 January 2017

Not able to access the array values which are inside of the JSON object from the Lightning Component if 'Enable Lightning LockerService Security update' is enabled

If you add the JSON response which is having the array directly to the Lightning Component Attribute, It is not working.

JSON RESPONSE for response.myData:

[{"attributes":{"type":"TEST"},"pId":"A101","entries":[{"value":"val1"},{"value":"val2"}],"myIndex":0}]

Issue: Not able to access entries and myIndex which are coming after entries (which is Array).

Example:  cmp.set('v.myData', response.myData);

Solution:

Stringify and Parse it like below -
cmp.set('v.myData', JSON.parse(JSON.stringify(response.myData)));

Saturday, 27 February 2016

Sending an email notification to Admin whenever a field is created or deleted for a custom object in salesforce

S.No Component
1 Create New Object
Singular Label: ObjectsExistingFieldsInfo
Plural Label: ObjectsExistingFieldsInfos
Object Name: ObjectsExistingFieldsInfo
Standard Field
Field Label: ObjectsExistingFieldsInfo
Data Type: Auto Number
Display Format: ObjInfo-{000}
2 Create New Field
Object Name: ObjectsExistingFieldsInfo
Field Label: Field API Name
Field Name: Field_API_Name
Data Type: Text (255)
3 Create New Field
Object Name: ObjectsExistingFieldsInfo
Field Label: Fields Count
Field Name: Fields_Count
Data Type: Number (18,0)
4 Create New Field
Object Name: ObjectsExistingFieldsInfo
Field Label: Fields Info
Field Name: Fields_Info
Data Type: Long Text Area
   global class FieldsValidator implements Database.Batchable<Sobject> {
    global Database.queryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator('select name, fields_info__c, fields_count__c from ObjectsExistingFieldsInfo__c');
    }
    global void execute(Database.BatchableContext bc, List<SObject> sobjLst) {
        String[] types = new String[]{'Account','Contact'};
        String emailBody = '';
        // Make the describe call
        Schema.DescribeSobjectResult[] results = Schema.describeSObjects(types);
        Map<String,List<Field>> objFieldsDesMap = new Map<String,List<Field>>();
        // For each returned result, get some info
        for(Schema.DescribeSobjectResult res : results) {
            Map<String, Schema.SObjectField> objectFields = res.fields.getMap();
            for(Schema.SObjectField fDes : objectFields.values()) {
                if(fDes.getDescribe().isCustom()) {
                    system.debug(fDes.getDescribe().getName()+'@@@'+fDes.getDescribe().getType());
                    String fieldAPIName = String.valueOf(fDes.getDescribe().getName());
                    String fieldDataType = String.valueOf(fDes.getDescribe().getType());
                    if(objFieldsDesMap.containsKey(res.getName())) {
                        objFieldsDesMap.get(res.getName()).add(new Field(fieldAPIName,fieldDataType));
                    }
                    else {
                        objFieldsDesMap.put(res.getName(),new List<Field>{new Field(fieldAPIName,fieldDataType)});
                    }
                }       
            }
        }
        Map<String, ObjectsExistingFieldsInfo__c> objectInfoMap = new Map<String, ObjectsExistingFieldsInfo__c>();
        for(ObjectsExistingFieldsInfo__c objInfo : [select Field_API_Name__c , Fields_Count__c, Fields_Info__c from 
        ObjectsExistingFieldsInfo__c where Field_API_Name__c in: types]) {
            objectInfoMap.put(objInfo.Field_API_Name__c,objInfo);
        }
        for(String type : types) {
            if(objectInfoMap.get(type).fields_count__c == null) {
                objectInfoMap.get(type).fields_count__c = objFieldsDesMap.get(type).size();
                objectInfoMap.get(type).fields_info__c = JSON.serialize(objFieldsDesMap.get(type));
            }
            else {
                System.debug('entering to else condition...');
                System.debug('entering to for loop..');
                List<Field> exstingFields = new List<Field>();
                List<Field> newFields = new List<Field>();
                Map<String,Field> bkupexstingFields = new Map<String,Field>();
                Map<String,Field> bkupnewFields = new Map<String,Field>();
                exstingFields = (List<Field>)JSON.deserialize(objectInfoMap.get(type).fields_info__c, List<Field>.class);
                System.debug('exstingFields: '+exstingFields);
                newFields.addAll(objFieldsDesMap.get(type));
                System.debug('objFieldsDesMap: '+objFieldsDesMap);
                System.debug('newFields: '+newFields);
                for(Field f : exstingFields) {
                    bkupexstingFields.put(f.apiName,f);
                }
                for(Field f : newFields) {
                    bkupnewFields.put(f.apiName,f);
                }
                for(Field f : exstingFields) {
                    bkupnewFields.remove(f.apiName);
                }
                for(Field f : newFields) {
                    bkupexstingFields.remove(f.apiName);
                }
                System.debug(bkupnewFields+'@@@'+bkupexstingFields);
                emailBody += type + ' : ';
                //for new fields
                for(Field f :bkupnewFields.values()) {
                    emailBody += f.apiName + ' with datatype '+f.dataType+' is newly created.'; 
                }
                objectInfoMap.get(type).fields_count__c = objFieldsDesMap.get(type).size();
                objectInfoMap.get(type).fields_info__c = JSON.serialize(objFieldsDesMap.get(type));
            }
        }
        /*** Sending Email ***/
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        mail.setToAddresses(new List<String>{system.label.Dev_Admin});
        mail.setSubject('Fields Update');
        mail.setPlainTextBody(emailBody);
        update objectInfoMap.values();
        Messaging.sendEmail(new Messaging.SingleEmailMessage[]{mail});
    }
    global void finish(Database.BatchableContext bc) {
    } 
    public class Field {
        String apiName, dataType;
        public Field(String apiName, String dataType) {
            this.apiName = apiName;
            this.dataType = dataType;
        }
    }
}
  
Notes-
  • We cannot create trigger on 'Object' or 'Field'.
  • Alternative is creating a Batch Class and schedule it as per the requirement.
  • To store the history of the fields we can user either list custom settings or custom object.
  • List Custom Settings is not supporting Text Area Long.
  • To store the history of the fields we need Text Area Long (255 character for text area data type is not sufficient.) field which is possible with the custom object.

Friday, 30 January 2015

Generic Pagination with Visualforce Component

Creating Generic Pagination with Visualforce Component

GenericPaginationComponent
  1. <!--
  2. Description
  3. *===========
  4. * VF Component for the Generic Pagination.
  5. * It accepts three parameters -
  6. 1. Records (List of records belongs to any object)
  7. 2. Fields (List of fields of a sobject to display in the table)
  8. 3. Title (Title of the section)
  9. * Author:
  10. * =======
  11. * www.srinusfdc.com
  12. -->
  13. <apex:component controller="GenericPaginationComponentContrl">
  14. <!-- Attributes to accept the parameters -->
  15. <apex:attribute name="Records" Type="Sobject[]" assignTo="{!sObjLst}" required="true" description="Accepts list of records of any object and assign to a variable in controller class"/>
  16. <apex:attribute name="Fields" Type="String[]" required="true" description="Accepts list of field API names of a sobject in string format"/>
  17. <apex:attribute name="Title" Type="String" required="true" description="Accepts the title of the section"/>
  18. <!-- Table which displays records along with pagination -->
  19. <apex:form >
  20. <apex:pageBlock >
  21. <apex:pageBlockSection columns="1" title="{!Title}" id="pbSec">
  22. <apex:pageBlockTable value="{!SobjRecords}" var="sObj">
  23. <!-- Dispalys the multiple columns based on the user input -->
  24. <apex:repeat value="{!Fields}" var="fld">
  25. <apex:column value="{!sObj[fld]}"/>
  26. </apex:repeat>
  27. </apex:pageBlockTable>
  28. <!-- Dispalys pagination buttons -->
  29. <apex:panelGrid columns="5">
  30. <apex:commandButton value="First" action="{!con.first}" disabled="{!!con.hasPrevious}" status="pagStatus" reRender="pbSec"/>
  31. <apex:commandButton value="Previous" action="{!con.previous}" disabled="{!!con.hasPrevious}" status="pagStatus" reRender="pbSec"/>
  32. <apex:commandButton value="Next" action="{!con.next}" disabled="{!!con.hasNext}" status="pagStatus" reRender="pbSec"/>
  33. <apex:commandButton value="Last" action="{!con.last}" disabled="{!!con.hasNext}" status="pagStatus" reRender="pbSec"/>
  34. <apex:actionStatus startText="Fetching..." id="pagStatus"/>
  35. </apex:panelGrid>
  36. </apex:pageBlockSection>
  37. </apex:pageBlock>
  38. </apex:form>
  39. </apex:component>

GenericPaginationComponentContrl
  1. /* Description:
  2. * ============
  3. * Class for the VF Component GenericPaginationComponent *
  4. * Author:
  5. * ========
  6. * www.srinusfdc.com
  7. */
  8. public class GenericPaginationComponentContrl {
  9. //Stores the records which are supplied to the 'Records' attribute.
  10. public Sobject[] sObjLst {get;set;}
  11. /*
  12. 1. Implementing the pagination with ApexPages.StandardSetController.
  13. 2. We can utilize the built in methods available for the ApexPages.StandardSetController to build the pagination.
  14. 3. Following are the built in mehods we can utilize -
  15. a. first()
  16. b. previous()
  17. c. next()
  18. d. last()
  19. e. getHasPrevious() - returns boolean value.
  20. f. getHasNext() - returns boolean value.
  21. g. setPageSize(IntegerValue)
  22. */
  23. public ApexPages.StandardSetController con {
  24. get {
  25. //initializing con with the records.
  26. if(con == null)
  27. con = new ApexPages.StandardSetController(sObjLst);
  28. //Setting the pagination size
  29. con.setPageSize(5);
  30. return con;
  31. }
  32. set;
  33. }
  34. //Method which returns subset of records from the sObjLst.
  35. public List<sobject> getSobjRecords() {
  36. //Type Casing the records and returning to display on the page.
  37. return (List<sobject>)con.getRecords();
  38. }
  39. }

User can see this component in Visualforce Component Reference, see the below screenshot -






*************
Using the above Generic Pagination from a Visualforce Page

GenericPaginationUsagePage
  1. <!--
  2. Description
  3. *===========
  4. * VF Page for using 'GenericPaginationComponent' component.
  5. * Displaying three different object lists by reusing the logic of 'GenericPaginationComponent' component.
  6. * Author:
  7. * =======
  8. * www.srinusfdc.com
  9. -->
  10. <apex:page controller="GenericPaginationUsageContrl" tabStyle="Account">
  11. <!-- Default name space is : C ; Referring the Visualforce Component -->
  12. <c:GenericPaginationComponent records="{!accLst}" fields="{!accFieldLst}" title="Accounts"/>
  13. <c:GenericPaginationComponent records="{!conLst}" fields="{!conFieldLst}" title="Contacts"/>
  14. <c:GenericPaginationComponent records="{!oppLst}" fields="{!oppFieldLst}" title="Opportunities"/>
  15. </apex:page>


GenericPaginationUsageContrl
  1. /* Description:
  2. * ============
  3. * Class for the VF Page GenericPaginationUsagePage *
  4. * Author:
  5. * ========
  6. * www.srinusfdc.com
  7. */
  8. public class GenericPaginationUsageContrl {
  9. //Declaring variables to store list of records.
  10. public List<Account> accLst {get;set;}
  11. public List<Contact> conLst {get;set;}
  12. public List<Opportunity> oppLst {get;set;}
  13. //Declaring variables to store list of Field API names in string format.
  14. public List<String> accFieldLst {get;set;}
  15. public List<String> conFieldLst {get;set;}
  16. public List<String> oppFieldLst {get;set;}
  17. //Default Constructor
  18. public GenericPaginationUsageContrl() {
  19. //Querying the records from the database.
  20. accLst = [select Name, AccountNumber, Fax, Phone, Industry from Account limit 100];
  21. conLst = [select Name, AccountId, Email, Phone from Contact limit 100];
  22. oppLst = [select Name, AccountId, Amount from Opportunity limit 100];
  23. //Preparing the list of fields to display in the table.
  24. accFieldLst = new List<String>{'Name', 'AccountNumber', 'Fax', 'Phone', 'Industry'};
  25. conFieldLst = new List<String>{'Name', 'AccountId', 'Email', 'Phone'};
  26. oppFieldLst = new List<String>{'Name', 'AccountId', 'Amount'};
  27. }
  28. }

Result

Live Demo

Friday, 26 December 2014

Fetching Record Type Id with Dynamic Apex

recordTypeInfo Utility Method
  public class MyUtility {
   /*
      @ Description -
      To fetch the RecordType Information of a certain Object.
      @Usage -
      Using this approach, we can avoid writing SOQL queries in apex class/test class to fetch the record type Id.
   */

   /*** recordTypeInfo Utility Method ***/
   //Below method will take sObject API as input.
   public static Map recordTypeInfo(String objectApiName){
    
    Map sObjectMap = Schema.getGlobalDescribe() ;
    Schema.SObjectType sObjType = sObjectMap.get(ObjectApiName) ;
    Schema.DescribeSObjectResult sObjTypeDescribe = sObjType.getDescribe() ;
    
    //returns all the record types info for a certain object
    return sObjTypeDescribe.getRecordTypeInfosByName();
    
   }
  }
  //---End of the Logic---Please ignore remaining lines---
 

recordTypeInfo Utility Method - Usage
  /*** recordTypeInfo Utility Method - Usage ***/

  //To retrieve the recordTypeId of a RecordType 'Sample Record Type' which belongs to 'Case' object.
  Id sampleRtId = MyUtility.recordTypeInfo('Case').get('Sample Record Type').getRecordTypeId();

  //Result: 'sampleRtId' will fetch the recordTypeId of 'Sample Record Type' record type.
 

Thursday, 5 June 2014

Avoid duplicate string values in the collection 'Set' or 'Map Keys' while adding strings with different cases

When, Set allows duplicates?
Examples:

Set<String> nameSet = new Set<String>;
nameSet.add(srinu);
nameSet.add(SRINU);
nameSet.add(Srinu);

Map<String,String> cityMap = new Map<String,String>;
cityMap .put('Newyork', USA);
cityMap .put('newyork',US);
cityMap .put('NEWYORK','America');

From the above examples, nameSet and cityMap allows all the three values because set and map key treat the values with different case as different values that means set string values are case-sensitive.

How to avoid?
Use the below method before adding string value to set or map key.

//Generic method to search a string from set of strings and which will ignore case-sensitive
public static Boolean isStrExists(Set<String> strSet,String searchStr) {
        Boolean isTrue = false;
        if(strSet != null && strSet.size() > 0) {
            for(String strVal : strSet) {
                if(strVal.equalsIgnoreCase(searchStr)) {
                    isTrue = true;
                    break;
                }
            }
        }
        return isTrue;
    }

How to use above method?
if(!isStrExists(nameSet,'SRINU'))
    nameSet.add('SRINU');