Dhrumit Thakkar

Multiple Attachments on create records with email attachments and customized related list on view page

Blog Image

Functionalities:

1. Create record with multiple attachments.

2. Attachments content type supports to CSV, PDF, Excel, word, PNG, JPG, JPEG.

3. Alert if each attachment is more than 5 MB (Javascript code added in vf page)

4. Save record and redirect to custom view page.

5. Customized related list on view page which shows form data with attachments  in related list.

6. Sending email on save record with record details in email and attachments.

7. This will work smoothly for both classic and lightning.


So for this we will create 1 static resource, 2 Visualforce Page, 2 Apex Classes

Steps:

1. Settings -> Static Resource -> New -> Name: Jquery -> Download Jquery file from : https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js and upload it -> Save

2.  Developer Console -> Click File -> New -> VisualForce page -> Name: Attachment_Query  -> Save

- Copy below code and paste it in above created visualforce page.

Classic view




Lightning View



<!-- 1. Added lightningStylesheets attribute in apex:page to render form based on UI selection
2. You can also use lightningStylesheets = "true" directly and check there is no need to change in form design for
lightning UI
3. Rendering lightning/classic UI using below way will allow you to apply slds separately for lightning or
classic as required
-->

<apex:page standardController="Query__c" extensions="Attachment_Query_Apex"
lightningStylesheets="{! !isClassic}" id="pg">
    
    <!-- rendering for classic UI only -->     
    <apex:outputPanel rendered="{!isClassic}">     
                <apex:includeScript value="{!$Resource.jquery}"/>

        <apex:form id="myForm">
            <apex:sectionheader title="New Query" subtitle="{!objQuery.Query_Number__c}"
            help="https://help.salesforce.com/htviewhelpdoc?err=1&id=co_edit.htm&siteLang=en_US"/>
            <apex:pageBlock >
                <apex:pagemessages id="errorMessages"/>
                <apex:pageBlockSection title="Details Section" id="note" columns="2" >            
                    <apex:inputField value="{!objQuery.Name}" required="true"/>
                    <apex:inputField value="{!objQuery.Query_Number__c}" required="true" />
                    <apex:inputField value="{!objQuery.Query_Type__c}" required="true" />
                    <apex:inputField value="{!objQuery.Description__c}" required="true" />
                </apex:pageBlockSection>
                <apex:pageBlockSection title="Attachments" columns="2" >
                    <apex:inputFile value="{!attachment1.body}"
                    accept=".xlsx,.xls,image/*,.doc, .docx,.ppt, .pptx,.txt,.pdf" filename="{!attachment1.name}"
                    contentType="{!attachment1.ContentType}"/>
                    <apex:inputFile value="{!attachment2.body}"
                    accept=".xlsx,.xls,image/*,.doc, .docx,.ppt, .pptx,.txt,.pdf" filename="{!attachment2.name}"
                    contentType="{!attachment2.ContentType}"/>
                    <apex:inputFile value="{!attachment3.body}"
                    accept=".xlsx,.xls,image/*,.doc, .docx,.ppt, .pptx,.txt,.pdf" filename="{!attachment3.name}"
                    contentType="{!attachment3.ContentType}"/>
                </apex:pageBlockSection>
                <apex:PageBlockButtons >
                    <apex:commandButton value="Save" action="{!doSave}" onclick="return checkFileSize();"/>
                    <apex:commandButton value="Cancel" action="{!doCancel}"/>                        
                </apex:PageBlockButtons>
            </apex:pageBlock>             
        </apex:form>
        
        <script>
        // Below function will give alert on save if find attachment above 5 MB
       function checkFileSize() {
       var goodSize = true;
       $('input[type=file]').each(function(){
           if(typeof this.files[0] !== 'undefined') {    
var file = this.files[0], size = typeof ActiveXObject !== 'undefined' ?
getIEFileSize(file): file.fileSize || file.size;
goodSize = 5000000 > size;
              if(!goodSize) {
                alert(this.files[0].name +' is too large - please choose a file that is 5Mb or less');
                  return false;
                  //  console.log('First call');
                 //     break;
                }
                else{
                return goodSize;
              }    
            }
        });
        return goodSize;
}

        </script>   
    </apex:outputPanel>
    
    <!-- rendering for for lightning UI only -->     
    <apex:outputPanel rendered="{! !isClassic}">     
                <apex:includeScript value="{!$Resource.jquery}"/>

        <apex:form id="myFormLtng">
            <apex:sectionheader title="New Query" subtitle="{!objQuery.Query_Number__c}"
            help="https://help.salesforce.com/htviewhelpdoc?err=1&id=co_edit.htm&siteLang=en_US"/>
            <apex:pageBlock >
                <apex:pagemessages id="errorMessages"/>
                <apex:pageBlockSection title="Details Section" id="note" columns="2" >            
                    <apex:inputField value="{!objQuery.Name}" required="true"/>
                    <apex:inputField value="{!objQuery.Query_Number__c}" required="true" />
                    <apex:inputField value="{!objQuery.Query_Type__c}" required="true" />
                    <apex:inputField value="{!objQuery.Description__c}" required="true" />
                </apex:pageBlockSection>
                <apex:pageBlockSection title="Attachments" columns="2" >
                    <apex:inputFile value="{!attachment1.body}"
                    accept=".xlsx,.xls,image/*,.doc, .docx,.ppt, .pptx,.txt,.pdf" filename="{!attachment1.name}"
                    contentType="{!attachment1.ContentType}"/>
                    <apex:inputFile value="{!attachment2.body}"
                    accept=".xlsx,.xls,image/*,.doc, .docx,.ppt, .pptx,.txt,.pdf" filename="{!attachment2.name}"
                    contentType="{!attachment2.ContentType}"/>
                    <apex:inputFile value="{!attachment3.body}"
                    accept=".xlsx,.xls,image/*,.doc, .docx,.ppt, .pptx,.txt,.pdf" filename="{!attachment3.name}"
                    contentType="{!attachment3.ContentType}"/>
                </apex:pageBlockSection>
                <apex:PageBlockButtons >
                    <apex:commandButton value="Save" action="{!doSave}" onclick="return checkFileSize();"/>
                    <apex:commandButton value="Cancel" action="{!doCancel}"/>                        
                </apex:PageBlockButtons>
            </apex:pageBlock>             
        </apex:form>
     
       <!-- below style is used to center(Save, cancel)buttons and remove unnecessary first child in lightning -->
        <style>
            .pbTitle {
            display: none;   
            }
            .slds-vf-scope .pbHeader .pbButton:last-child {
            text-align: center;    
            }
        </style>


        <script>
        // Below function will give alert on save if find attachment above 5 MB
     function checkFileSize(){
        var goodSize = true;
        $('input[type=file]').each(function(){
          if(typeof this.files[0] !== 'undefined'){
             var file = this.files[0], size = typeof ActiveXObject !== 'undefined' ?
             getIEFileSize(file): file.fileSize || file.size;
             goodSize = 5000000 > size;
             if(!goodSize) {
             alert(this.files[0].name +' is too large - please choose a file that is 5Mb or less');
               return false;
               //  console.log('First call');
              //  break;     
            }
             else{
                 return goodSize;
            }    
          }
         });
        return goodSize;
    }
      </script>   
</apex:outputPanel>   
</apex:page>



3. Developer Console -> Click File -> New -> Apex Classes -> Name: Attachment_Query_Apex -> Save

- Copy below code and paste it in above created visualforce page.

public class Attachment_Query_Apex {
    
    public Query__c objQuery {get;set;}
    List<Attachment> listToInsert = new List<Attachment>();
    public Attachment attachment1 {get;set;}
    public Attachment attachment2 {get;set;}
    public Attachment attachment3 {get;set;}
    
    
    // below code is used to fetch classic/lightning theme as per user's current UI    
    public Boolean getIsClassic(){
        return (UserInfo.getUiThemeDisplayed() == 'Theme3' &&
        ApexPages.currentPage().getParameters().get('beLightning') == null);
    }
    
    
    public Attachment_Query_Apex(ApexPages.StandardController controller){
        objQuery = new Query__c();
        this.attachment1 = new Attachment();
        this.attachment2 = new Attachment();
        this.attachment3 = new Attachment();
        this.listToInsert = new List<Attachment>();
    }
    
    // This method will get called when you click on save button.     
    public pagereference doSave(){
        try{
            
            // insert form data first so that we can use recordID as a parentID for attachments
            insert objQuery;
            
            listToInsert.add(attachment1);
            listToInsert.add(attachment2);
            listToInsert.add(attachment3);
            
            for(Attachment a: listToInsert){
                System.debug('Name' + a.Name);
                System.debug('ContentType ' + a.ContentType);
                System.debug('Id ' + a.Id);
                if(a.Name != null){
                    try{
                        // passing currently created recordId to attachment's parentId
                        a.parentId = objQuery.Id;        
                        insert a;
                        //    a.body = null;
                    }
                    catch (Exception e){
                        a.Body = null;
                        ApexPages.addMessage(new ApexPages.message(ApexPages.severity.ERROR,
                        'Error uploading attachment:'+e));
                        return null;
                    }
                   
// null attachment body in finally after inserting attachment in try to avoid
                    // 'view state' error

                    finally {
                        a.body = null;
                    }    
                }
            }
            
            System.debug('inside doSave');

            // calling sendEmailToUser method to send an email with all filled information with attachments if any
            sendEmailToUser(objQuery);
            pagereference pg = new pagereference('/'+objQuery.id);
            pg.setRedirect(true);
            return pg;
        }
        
        catch(exception e) {
            Query__c objQuery = new Query__c();
            System.debug('Hi I am last catch');
            ApexPages.Message myMsg = new ApexPages.Message(ApexPages.Severity.FATAL, e.getMessage());
            System.debug('myMsg: '+myMsg);
            apexpages.addmessage(mymsg);
            return null;
        }
        
    }    
    public Pagereference doCancel(){
        Schema.Describesobjectresult result = Schema.Sobjecttype.Query__c;
        string objId = result.getKeyPrefix()+ '/o';
        return (new Pagereference('/'+objId).setredirect(true));
    }
    
    
/* 1. Below method is used to send an email for created query with Query details, attachments if any, subject
* 2. You can customize below method as per your requirement: you can add setToAddress, you can add setCCAddress,*/

     
    public static List<Messaging.SingleEmailMessage> sendEmailToUser(Query__c queryCreated) {
        List<Query__c> queryList = [SELECT Name, Query_Number__c, Query_Type__c,
        Description__c FROM Query__c WHERE Id =: queryCreated.id ];
        system.debug('queryList : '+queryList);
        Query__c aQuery = queryList[0];
        System.debug('aQuery : ' + aQuery);
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        String userName = UserInfo.getFirstName();
        String emailId = UserInfo.getUserEmail();
        System.debug('emailId : ' + emailId);
        String[] toAddresses = new String[]{};
            toaddresses.add('dhrumit.thakkar@gilead.com');
        mail.setToAddresses(toAddresses);
        System.debug('mail.setToAddresses' +mail);

        // set email subject using below mail method
        mail.setSubject('Query Report');
        
        // set email body using below mail method
        String baseURL = URL.getSalesforceBaseUrl().toExternalForm();
        mail.setHtmlBody(
            userName+' has created a(n) '+aQuery.Query_Type__c+' Query'+' in Salesforce. </br> </br>' +
            
            'Query #'+aQuery.Query_Number__c+'+: <a href='+baseURL+'
            /apex/Attachment_Query_View?id='+aQuery.Id+'&sfdc.override=1'+'>'+aQuery.Name+'</a></br>'+
            'Query Type: '+aQuery.Query_Type__c+'</br> </br>'+
            
            'Name: '+aQuery.Name+'</br>'+
            'Description: '+aQuery.Description__c+'</br> </br>'
        );
        
        
/* getting all attachments which are inserted in doSave method
         * and attaching those in email -- start */

        
        List<Messaging.Emailfileattachment> fileAttachments = new List<Messaging.Emailfileattachment>();
        for (Attachment a : [select Name, Body, BodyLength from Attachment where ParentId = :aQuery.Id])
        {
            Messaging.Emailfileattachment efa = new Messaging.Emailfileattachment();
            efa.setFileName(a.Name);
            efa.setBody(a.Body);
            fileAttachments.add(efa);
        }
        mail.setFileAttachments(fileAttachments);
        /* attachment code -- end */
        
        List<Messaging.SingleEmailMessage> listOfEmailsToSend = new List<Messaging.SingleEmailMessage>{mail};
            Messaging.sendEmail(listOfEmailsToSend);
        System.debug(listOfEmailsToSend);
        return listOfEmailsToSend;     
    }
}


4.  Developer Console -> Click File -> New -> VisualForce page -> Name: Attachment_Query_View  -> Save




- Copy below code and paste it in above created visualforce page.

<!-- 1. Added lightningStylesheets attribute in apex:page to render form based on UI selection 
2. You can also use lightningStylesheets = "true" directly and check there is no need to change in form design for
lightning UI
3. Rendering lightning/classic UI using below way will allow you to apply slds separately for lightning or classic
as required
-->


<apex:page extensions="Attachment_Query_Apex_View" standardController="Query__c" tabStyle="Query__c"
           lightningStylesheets="{! !isClassic}">
    
    <!-- rendering for for classic UI only -->
    <apex:outputPanel rendered="{!isClassic}">
        <apex:sectionHeader title="Case" subtitle="{!objQuery.Query_Number__c}"
help="https://help.salesforce.com/htviewhelpdoc?err=1&id=co_edit.htm&siteLang=en_US"/>
        <apex:form id="f1">
            <apex:pageBlock >
                <apex:pageBlockButtons >
                    <apex:commandButton value="Edit" action="{!edit}"/>
                    <apex:commandButton value="Back" immediate="true"/>
                </apex:pageBlockButtons>
                <apex:pageBlockSection columns="2">
                    <apex:outputField value="{!objQuery.Name}"/>
                    <apex:outputField value="{!objQuery.Query_Number__c}"/>
                    <apex:outputField value="{!objQuery.Query_Type__c}"/>
                    <apex:outputField value="{!objQuery.Description__c}"/>
                </apex:pageBlockSection>
            </apex:pageBlock>
        </apex:form>
            
<!-- Below style sheet will get applied to related list added below to hide Action column(Edit|View|Del)
from related list --> 
  
      
        <style>
            .actionColumn{ display:none; visibility:hidden;}
        </style>
        
        
       
<!-- 1. adding related list using below way will give you acces to customize it.
For example: I have removed Action section(Edit, Delete, View), New File, New Notes button from related
list
You will get more idea from added view page screenshot. I have also changed related list name to
Attachments.
2. If you do not want to hide Action section, New File, New Notes button than you can simply add Notes
and Attachment related list from page-layout in Object.
-->

        <apex:relatedList subject="{!Query__c.Id}" list="CombinedAttachments">
            <apex:facet name="header">
                <table>                    
                    <tr><td class="pbTitle"><h3>Files</h3></td></tr>
                </table>
            </apex:facet>
        </apex:relatedList>
    </apex:outputPanel>
    
    
    <!-- rendering for for lightning UI only -->
    <apex:outputPanel rendered="{! !isClassic}">       
        <apex:sectionHeader title="Case" subtitle="{!objQuery.Query_Number__c}"
        help="https://help.salesforce.com/htviewhelpdoc?err=1&id=co_edit.htm&siteLang=en_US"/>
        <apex:form id="f1Ltng">
            <apex:pageBlock >
                <apex:pageBlockButtons >
                    <apex:commandButton value="Edit" action="{!edit}"/>
                    <apex:commandButton value="Back" immediate="true"/>
                </apex:pageBlockButtons>
                <apex:pageBlockSection columns="2">
                    <apex:outputField value="{!objQuery.Name}"/>
                    <apex:outputField value="{!objQuery.Query_Number__c}"/>
                    <apex:outputField value="{!objQuery.Query_Type__c}"/>
                    <apex:outputField value="{!objQuery.Description__c}"/>
                </apex:pageBlockSection>
            </apex:pageBlock>
        </apex:form>
        
        
       
<!-- Below style sheet will get applied to related list added below to hide Action column(Edit|View|Del)
from related list -->
                    
        <style>
            .actionColumn{ display:none; visibility:hidden;}
            .firstChild{display: none;}
            
           
<!-- below style is used to center(Edit, Back)buttons and remove unnecessary first child in
                 lightning -->

            .pbTitle {
            display: none;   
            }
            .slds-vf-scope .pbHeader .pbButton:last-child {
            text-align: center;    
            }
        </style>
        
        
       
<!-- 1. adding related list using below way will give you acces to customize it.
For example: I have removed Action section(Edit, Delete, View), New File, New Notes button from
related list
You will get more idea from added view page screenshot. I have also changed related list name to
Attachments.

2. If you do not want to hide Action section, New File, New Notes button than you can simply add Notes
and Attachment related list from page-layout in Object.
-->

        
        <apex:relatedList subject="{!Query__c.Id}" list="CombinedAttachments">
            <apex:facet name="header">
                <table>
                    &nbsp;
                    <tr><td class="firstChild"></td>
                        <td class="pbTitle"><h3>Attachments</h3></td>
                    </tr>
                </table>
            </apex:facet>
        </apex:relatedList>
    </apex:outputPanel>
</apex:page>



5. Developer Console -> Click File -> New -> Apex Classes -> Name: Attachment_Query_Apex_View -> Save

- Copy below code and paste it in above created visualforce page.

public without sharing class Attachment_Query_Apex_View {
    public Query__c objQuery {get;set;}
    
    // below code is used to fetch classic/lightning theme as per user's current UI
    public Boolean getIsClassic(){
        return (UserInfo.getUiThemeDisplayed() == 'Theme3'
        && ApexPages.currentPage().getParameters().get('beLightning') == null);
    }    

    // getting all field data on view page for current record selection from list view page
    public Attachment_Query_Apex_View(Apexpages.StandardController controller){
        objQuery = (Query__c)controller.getRecord();
        objQuery = [SELECT Name, Query_Number__c, Query_Type__c, Description__c FROM Query__c
        WHERE Id =: objQuery.id];
        System.debug('objQuery : ' + objQuery);
    }
}

VIDEO

MOST VIEW POST

RECOMMENDED POST

POST COMMENT

Please to continue.