Monday, July 15, 2019

Tips and Tricks : How to hide other feature in Chatter Feed in Visualforce page WITHOUT using JQuery ?

Business Requirement

To only enable Post features in Chatter Feed in Visualforce. Other features such as Poll, Upload File etc should be invisible to user  so that user only able to Post in Chatter Feed.

This is just to give idea that you can hide some of standard css element using style css with !IMPORTANT tag if it 's still not update as you want to or you can hide it using javascript during onload.

Example to hide some standard elements using style .


1
2
3
4
5
6
7
8
/**code to hide file upload in chatter comment section **/
.cxcontentcommentactiontext {
display:none;
}

.cxcontentcommentactionimg {
display:none;
}

Example to hide some of element in Chatter Feed using Javascript
1
2
3
4
5
6
7
window.onload = function() {
//this is to hide File upload and any feature except post in Chatter feed.
   const chatterList = document.querySelectorAll('.publisherFeedItemTypeChoices li');
   chatterList[1].style.display='none';
   chatterList[2].style.display='none';
   chatterList[3].style.display='none';
}

Basically if you can see which Element you want to hide. End result should be like below.


Monday, July 01, 2019

Tips and Tricks : Test class for Invocable method


Issue :

I got 100% coverage in my sandbox but when run validation for deployment it returns 0% coverage

It turn out that in my sandbox, I am depending on Process Builder to Invocable Apex class, as long I manipulate test data that fire Process Builder it will call Invocable class. This is not useful when deploying it to Production although it gets deployed together with Process Builder.

The correct way is to direct call Invocable method inside test class itself.

Example of class :


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
global class MyCustomObject_StatusUpdate_Util{
 @InvocableMethod(label='Update Quote Status')
  public static void updateQuote(Request[] requests) {
        Set<Id> setOppId = new Set<Id>();
        List<SBQQ__Quote__c> listQuoteToUpdate = new List<SBQQ__Quote__c>();

        for(Request request : requests) {
           setOppId.add(request.recordId);
        }
       
        if(setOppId.size()>0){
            List<SBQQ__Quote__c> listQuote =[Select Id,BypassValidationRule__c,SBQQ__Status__c from SBQQ__Quote__c where SBQQ__Opportunity2__c IN :setOppId  ];

             for(SBQQ__Quote__c quote : listQuote)  {
                 quote.BypassValidationRule__c = quote.BypassValidationRule__c?false:true;
                 quote.SBQQ__Status__c =requests[0].quoteStatus;
                 listQuoteToUpdate.add(quote);
             } 
            if(listQuoteToUpdate.size()>0){
                 Database.SaveResult [] updateResult = Database.update(listQuoteToUpdate, false);
            }
         }
     }

 global class Request{
        @InvocableVariable(label='Opportunity ID' required=true)
        public Id recordId;

        @InvocableVariable(label='Quote Status' required=true)
        public String quoteStatus;
      
    }
}

Example of test class :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static testMethod void testCallInvocableClass() {
        
     Test.startTest();
     My_Custom_Object__c myObject =[Select Id,Approval_Status__c FROM My_Custom_Object__c WHere Opportunity__c =:quote.SBQQ__Opportunity2__c];

    myObject.Approval_Status__c ='Approved';
    //this will work only in sandbox, not in deployment.Logically this will fire process builder
    update myObject;

 //to makesure test class is cover, need to call invocable class directly, which in this scenario I need to instantiate
 // inner class 
    List<MyCustomObject_StatusUpdate_Util.Request> listRequest =new List<MyCustomObject_StatusUpdate_Util.Request>();
    MyCustomObject_StatusUpdate_Util.Request req = new MyCustomObject_StatusUpdate_Util.Request();
    req.recordId=quote.SBQQ__Opportunity2__c;
    req.quoteStatus='In Progress';
    listRequest.add(req);
    
    //the call the method directly - here my method is updateQuote
    MyCustomObject_StatusUpdate_Util.updateQuote(listRequest);
   
    listQuote =[select id,SBQQ__Status__c  from SBQQ__Quote__c where SBQQ__Opportunity2__c=:listOpp[0].Id ];
    system.assertEquals('In Progress', listQuote[0].SBQQ__Status__c)
  
    Test.stopTest();
  
  }

Monday, June 24, 2019

Tips and Tricks : Make apex:column value display conditionally

When try to make apex:column display conditionally there high possibility we try to code like below which will prompt Syntax error. Missing ')' .Unfortunately this is documented in any where so far.



<apex:column headerValue="Actual Approver" value="{!IF(item.objectType=='Qualification',item.bid.Actual_Final_Approver__c,item.quote.Actual_Final_Approver__c)}"></apex:column>

apex:column cannot being rendered directly in value tag but we can render it different way like below
    <apex:column headerValue="Actual Approver">
                    <apex:outputField rendered="{!item.objectType=='Qualification'}"
                                      value="{!item.bid.Actual_Final_Approver__c}"/>
                    <apex:outputField rendered="{!item.objectType=='Quote'}"
                                      value="{!item.quote.Actual_Final_Approver__c}"/>
                </apex:column>
The render is done not in value tag in column but use render tag in component like outputText, outputField inside apex:column. Note that using outputField for lookup field will generate standard mini page layout when we mouse over on the link.

Reference : apex:column

Monday, February 25, 2019

Tips and Tricks : Avoid $$ in ANT password

Today we faced weird issue. ANT throwing Invalid User Password although we know exactly what our password.We able to login directly into Salesforce site but we failed to login via ANT.

It delays our deployment process because we struggle to figure out why.

  • Double check the password [Done]
  • Reset the password[Done]
  • Check proxy[Done]


Thankfully I found this tips : Password Issue in ANT . I suspect it may due to this

Property Expansion
When Ant encounters a construct ${some-text} the exact parsing semantics are subject to the configured property helper delegates.
$$ Expansion
In its default configuration Ant will expand the text $$ to a single $ and suppress the normal property expansion mechanism for the text immediately following it, i.e., $${key} expands to ${key} and not value even though a property named key was defined and had the value value. This can be used to escape literal $ characters and is useful in constructs that only look like property expansions or when you want to provide diagnostic output like in Read More...

Anyone has better explanation please comment.For this time being, it's better avoid $$ or more than two $ in password.

Wednesday, January 02, 2019

Summer 18 : Better way to retrieve RecordTypeId

Salesforce Summer ‘18 has added new method to get Developer Name for Record Types which give me such a huge relieve.

my sketch
Previously, we were only able to get RecordType using Id or Name. Usually in our code we always need to retrieve the Id but retrieving Id with Name (Label) using getRecordTypeInfosByName is not good idea as Name can be changed easily.

To solve the issue, we created one Global class to run SOQL just to retrieve ID, DeveloperName. It exposed our code to SOQL limit and we need to be careful with the design when we decided to use Static because the global class can be called by different method in the same transaction.

Now we can forget about that *tedious part*. I started to clean up my code to use Salesforce method instead of using SOQL to retrieve record type.

Caution : Update your class API version to the latest version to prevent error as this new method not recognized in old version.

Retrieve RecordType ID using DeveloperName


Id newRecordType = Schema.SObjectType.Opportunity.getRecordTypeInfosById().get(opportunityId).getDeveloperName();

Retrieve RecordType DeveloperName using ID


String recordType= Schema.SObjectType.Opportunity.getRecordTypeInfosByDeveloperName().get(recordTypeDeveloperName).getRecordTypeId();

Please update the underlined word above with correct value.