Sunday 4 October 2015

Workaround for removing the Save&New button from Standard pages after Winter'16 release

As you all know earlier Salesforce replaced the rich text editor in HTML Area home page components with a new version that supports more markup but doesn’t allow HTML to be manually entered.
So as a workaround, we used custom links in home page components to invoke our scripts, See the below link for details.

http://salesforce.stackexchange.com/questions/38918/end-of-javascript-sidebar-workarounds

Since this workaround was only supported till Summer'15 and we are now in Winter'16 so this wont work because now sidebar script does not automatically invoke and requires a manual click on the link to invoke it. See this link for details http://docs.releasenotes.salesforce.com/en-us/winter16/release-notes/rn_forcecom_general_requirescript.htm

With the new UI, the sidebar is going to go away. So, the less reliance we have on the sidebar components, the better it would be for our transition.

However, there isn't any other way to hide the "Save & New" button but to replace it by using a Visualforce page. Salesforce has been taking security as the highest priority and been striving to avoid any kind of hacking activities (including Javascript workarounds) which could incur potential security thread to customers. That's the reason why Javascript is no longer supported within a home page component.

After discussing with Salesforce, the workaround I've found is to override the "New" button of an object using a Visualforce page which has a Controller to check if the "Save & New" is clicked. If it's clicked then ignore the "New" action and just do "Save". So even the "Save & New" button is still visible but it would just behave like the "Save" button.
The following are the sample code which works on the Account Object. To see it in action simply goto Setup | Customize | Accounts | Buttons, Links and Actions | Edit the "New" button by the following Visualforce page.

Visualforce Page:

1
2
<apex:page standardController="Account" extensions="SObjectNewActionOverrideController" action="{!onLoadPage}">
</apex:page>
Controller:
public class SObjectNewActionOverrideController {

    public String debug{get;Set;}
    private String SObjectPrefix = null;
    public SObjectNewActionOverrideController(ApexPages.StandardController controller){
        this.SObjectPrefix = controller.getRecord().getSObjectType().getDescribe().getKeyPrefix();
    }
    
    public PageReference onLoadPage(){
        this.debug = JSON.serializePretty(ApexPages.currentPage().getParameters());
        String retURL = ApexPages.currentPage().getParameters().get('retURL');

        if(ApexPages.currentPage().getParameters().get('save_new')=='1'
            && retURL != null
            && retURL.startsWith('/'+SObjectPrefix)
            && retURL.indexOf('/', 4) < 0
            && !retURL.contains('?')
            && retURL.length() >= 15){
            PageReference pg = new PAgeReference(retURL);
            pg.setRedirect(true);
            return pg;
        }else{
            PageReference pg = new PAgeReference('/'+this.SObjectPrefix+'/e');
            for(String key : ApexPages.currentPage().getParameters().keyset()){
            if(key == 'save_new' || key == 'sfdc.override') continue;
                pg.getParameters().put(key, ApexPages.currentPage().getParameters().get(key));
        }

        pg.getParameters().put('nooverride','1');
        pg.setRedirect(true);
        return pg;
        }
    }
}

Saturday 6 June 2015

Using Comparable Interface to sort by Date

I have come across the requirement in which I have to sort the list of Apex Class based on Date.

If you want to add sorting support for Lists that contain non-primitive types, that is, lists of user-defined types. you must implement the Comparable interface with its compareTo method in your class.

To implement the Comparable interface, you must first declare a class with the implements keyword.
For sorting by date, we can use daysBetween(Date) which returns the number of days between that called the method and specified date.

Code Snippet of Apex Class:  
global class PaymentPlanList implements Comparable{
    
    public static List <PaymentPlanList> lstPaymentPlans {get;set;}
    public String Type {get;set;}
    public Decimal Amount {get;set;}
    public Date ToDate {get;set;}
    public Date FromDate {get;set;}
    
    public PaymentPlanList(PaymentPlan__c pp){
        Type = pp.Name;
        Amount = pp.Amount__c;
        ToDate = pp.To_Date__c;
        FromDate = pp.From_Date__c;
    }
    
    public static List<PaymentPlanList> GetPayments(){
        if(lstPaymentPlans == null ){
            lstPaymentPlans = new List<PaymentPlanList>();
            for(PaymentPlan__c payment : [Select Name,Amount__c,From_Date__c,To_Date__c from PaymentPlan__c]){
                lstPaymentPlans.add(new PaymentPlanList(payment));
            }            
        }
       return lstPaymentPlans;
    }
    
    global Integer compareTo(Object compareTo) {
       //assuming if their is no date, it would be TODAY
       Date fromDatee = compareTo != null ? ((PaymentPlanList)compareTo).FromDate : System.today();           
       return fromDatee.daysBetween(this.FromDate); // Sorting by Date
    }
}
Sorting list of Apex Class and binded to VF page:
public class PaymentPlan{
    public List <PaymentPlanList> PaymentPlans {get;set;}
    public PaymentPlan(){
        PaymentPlans = PaymentPlanList.GetPayments(); // Calling Method of Outer Class for populating List of user-defined type
        PaymentPlans.sort(); // Adds sorting support for Lists that contain non-primitive types, that is, Lists of user-defined types.
    }
}
Visuaforce Page:
<apex:page controller="PaymentPlan" >
    <apex:pageBlock >
        <apex:pageBlockTable value="{!PaymentPlans}" var="pp">
            <apex:column headerValue="Payment Type" value="{!pp.Type}" style="width:25%"/>
            <apex:column headerValue="Amount" value="{!pp.Amount}" style="width:25%"/>
            <apex:column headerValue="From Date" style="width:25%">
                <apex:outputText value="{0,date,MM/dd/yy}"> 
                    <apex:param value="{!pp.FromDate}"/> 
                </apex:outputText> 
            </apex:column>
            <apex:column headerValue="To Date" style="width:25%">
                <apex:outputText value="{0,date,MM/dd/yy}"> 
                    <apex:param value="{!pp.ToDate}"/> 
                </apex:outputText> 
            </apex:column>
        </apex:pageBlockTable>
    </apex:pageBlock>
</apex:page>
If you notice the list is sorted by Date.
Feel free to post comments if you have any questions. Cheers !!!

Wednesday 27 May 2015

Email Service to Parse CSV

Howdy. I had requirement for parsing CSV in email service, thought I should post it on my blog.So lets get started.

Step :1

First you need to create Apex class that acts as an inbound email handler which simply needs to implement the Messaging.InboundEmailHandler interface. The interface defines a single method which is handleInboundEmail.

Code Snippet: 
global class myHandler implements Messaging.InboundEmailHandler {
    
    global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email, Messaging.InboundEnvelope envelope) {
    Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();
    Messaging.InboundEmail.BinaryAttachment[] tAttachments = email.binaryAttachments; // // Since Attachment is CSV we are taking as BinaryAttachments. 
    list<Account> lstAccount = new list<Account>(); // list of accounts.
    String csvbody='';
    
    list<list<String>> allFields = new list<list<String>>(); // list of rows in csv
    list<String> lines = new list<String>(); // Rows of CSV
    list<String> headers = new list<String>(); // Field names   
    list<String> fields = new list<String>(); // list of fields 
    
    if(tAttachments !=null){
        for(Messaging.InboundEmail.BinaryAttachment btt : tAttachments){
            if(btt.filename.endsWith('.csv')){
                csvbody = btt.body.toString();//Take the blob body of the CSV binary attachment and extract it to a string object, then process it.
                try {
                    // Replace instances where a double quote begins a field containing a comma    
                    // In this case you get a double quote followed by a doubled double quote    
                    // Do this for beginning and end of a field 
                    csvbody = csvbody.replaceAll(',"""',',"DBLQT').replaceall('""",','DBLQT",');
                    // now replace all remaining double quotes - we do this so that we can reconstruct    
                    // fields with commas inside assuming they begin and end with a double quote 
                    csvbody = csvbody.replaceAll('""','DBLQT');
                    lines = csvbody.split('\n');
                }catch (System.listException e){
                    System.debug('Limits exceeded?' + e.getMessage());
                }
            }
        }
        
        integer rowNumber = 0;
        
        for(String line : lines) {
        // check for blank CSV lines (only commas) 
            if (line.replaceAll(',','').trim().length() == 0) break;
                fields = line.split(',', -1);
                allFields.add(fields);
                if(rowNumber == 0){
                    // for getting the field names. 
                    for(list<String> rows : allFields){    
                        for(String header : rows){
                            headers.add(String.valueof(header.trim()));
                        }   
                    break;
                    }
                
                    rowNumber++;
                    continue;
                }
        
            }
        
            for(Integer i = 1 ; i < lines.size() ; i++){
                Account a = new Account();
                a.put(headers[0] , allFields[i][0]);
                a.put(headers[1] , allFields[i][1]);
                a.put(headers[2] , allFields[i][2]);
                lstAccount.add(a);
            }
            insert lstAccount;
        }
        return result;
    }
}
Step 2:

Next, you need to define the email service. This establishes an email address, which you can then tie to the Apex class that you've just written. Define the email service by logging into your environment.
  • Log into Force.com and select on Setup > App Setup > Develop > Email Services
  • Select New Email Service
After setting up your email service, you'll see something like this 


That's it. So simple isn't it :) To test your service, send email to the email address which is specified in your email service as below


After the sending email you will see Account created in your Salesforce Org (if you have setup everything correctly).


Note: This class assumes that you have three column in your excel sheet as below


You can extend the functionality as per your columns in excels. Please feel free to comment if you have any questions. 


Happy Coding. Cheers!