MultiFile upload / Drag and drop Attachments

Attaching multiple files as attachments to records is a pain. Its takes lot of time plus lot of navigation. Today, I want to share a small piece of code that I have written as home page component which will inject a small section in the ‘Notes and Attachments’ related list which will allow the users to load multiple file either selecting it from the input file selection or the users can drag and drop the files.

dragndrop

So how do you implement this in your org.

1) Add the main script to a js file and add it to static resource.

2) Create a new Custom Link.

3) Reference the js file in the custom link you created in the previous step. We will also need jQuery, so we will add that as well.

4) Add that custom link a home page component of type Links.

5) Add the home page component to the home page layout and you’re done. For more details on this hacking method, you can check this link here

So here we go: ->

1) Create a javascript file with the below code and add it as a static resource with the name attachmentjs

jQuery(function(){

    /*Checks when the notes and attachment section is loaded and then initiates the process.*/
    var timeInterval = setInterval(function(){
        if(jQuery("div.bRelatedList[id$=RelatedNoteList]").find("div.pbHeader").find("td.pbButton").find("input[name=attachFile]").length > 0){
            addAttachButton();
            clearInterval(timeInterval);
        }
    },100);


});

/*Adds the drop zone div in the notes and attachment section. Event listener are added to listen when files are dropped in the zone.*/
function addAttachButton() {

    var attachmentDiv = jQuery("div.bRelatedList[id$=RelatedNoteList]");
    insertButton();

    attachmentDiv.find("div.pbHeader").find("td.pbButton").after(
        jQuery("<td>", {
            style   : "width:35%;cursor:pointer;"
        }).append(jQuery("<div>",{
                style : "height: 30px;  width: px;border-color: orange;border: 3px solid orange;border-style: dotted;border-radius: 4px;text-align: center;vertical-align: middle;line-height: 2;color: Red;font-family: monospace;font-size: 14px;",
                id : "dropDiv"
            }).append(jQuery("<span>",{id:"clickHere"}).text('Drop files here / click here!') , jQuery("<span>",{id:"uploadingMessage",style:"display:none;"}).text('Uploading your Files, please wait!'))
        )
    );

    jQuery("#dropDiv").on('click',function(){
        if(jQuery("#uploadingMessage:hidden").length > 0) {
            if(jQuery("#multiUploadButton").length > 0) {
                jQuery("#multiUploadButton")[0].click();
            }else {
                insertButton();
                jQuery("#multiUploadButton")[0].click();
            }
        } else {
            alert('Your files are being uploaded. Please Wait!');
        }
    });

    function handleFileSelect(evt) {
        evt.stopPropagation();
        evt.preventDefault();
        var files = evt.dataTransfer.files; // FileList object.
        processFiles(files);
    }    
    function handleDragOver(evt) {
        evt.stopPropagation();
        evt.preventDefault();
        evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
    }

    var dropZone = document.getElementById('dropDiv');
    dropZone.addEventListener('dragover', handleDragOver, false);
    dropZone.addEventListener('drop', handleFileSelect, false);
}


function processFiles(files) {
    hideDiv();
    var insertedFilesArray = [];
    var fileInsertionCounter = 0;

        for(i=0;i<files.length;i++) {
                    
            var reader = new FileReader();
            reader.onload = (function(newFile,pId) {
                return function(e) {
                    var attachmentBody = e.target.result.substring(e.target.result.indexOf(',')+1,e.target.result.length);
                        
                    jQuery.ajax({
                        type: "POST",
                        url: "https://"+window.location.host+"/services/data/v33.0/sobjects/Attachment/",
                        contentType:"application/json; charset=utf-8",
                        headers: { 
                            "Authorization" : "Bearer "+getCookie('sid')
                        },
                        data    : JSON.stringify({'name':newFile.name,'body':attachmentBody,'parentId':pId,'isprivate':false})
                    }).success(function(result) {
                        fileInsertionCounter++;
                        if(fileInsertionCounter == files.length){
                            showDiv();
                        }
                    }).fail(function(err){
                        alert('Unable to Insert file \n Contact your Adminstrator with this error message \n' + JSON.stringify(err));
                        fileInsertionCounter++;
                        //if all the files are uploaded then show the zone div.
                        if(fileInsertionCounter == files.length){
                            showDiv();
                        }
                    });
                };
            })(files[i],jQuery("div.bRelatedList[id$=RelatedNoteList]").attr('id').split('_')[0]);
            
            reader.readAsDataURL(files[i]);
        }
}

/*Inserts the input file button. This is hidden from the view.*/
function insertButton() {
    if(jQuery("#multiUploadButton").length == 0) {
        var attachmentDiv = jQuery("div.bRelatedList[id$=RelatedNoteList]");
        attachmentDiv.find("div.pbHeader").find("td.pbButton").append(
            jQuery("<input>",
                {id     : "multiUploadButton",
                value   : "Multiple Upload",
                type    : "file",
                multiple: "multiple",
                style   : "display:none;"
                })
        );
        jQuery("#multiUploadButton").on('change',function(){
            processFiles(document.getElementById("multiUploadButton").files);
        });
    }
}

function hideDiv() {
    jQuery("#uploadingMessage").show();
    jQuery("#clickHere").hide();
}

function showDiv() {

    jQuery("#uploadingMessage").hide();
    jQuery("#clickHere").show();
    RelatedList.get(jQuery("div.bRelatedList[id$=RelatedNoteList]").attr('id')).showXMore(30);
}

function getCookie(c_name){
    var i,x,y,ARRcookies=document.cookie.split(";");
    for (i=0;i<ARRcookies.length;i++){
        x=ARRcookies[i].substr(0,ARRcookies[i].indexOf("="));
        y=ARRcookies[i].substr(ARRcookies[i].indexOf("=")+1);
        x=x.replace(/^\s+|\s+$/g,"");
        if (x==c_name){
            return unescape(y);
        }
    }
}

2) Create a custom link called ‘Attacher Custom Link’ and the add the below code. Make sure you select Behaviour as ‘Execute Javascript’.

  {!REQUIRESCRIPT('//code.jquery.com/jquery-2.1.3.min.js')}
  {!REQUIRESCRIPT('/resource/attachmentjs')}

… follow all the steps mentioned above and you should be set. I have tested this on Latest Chrome and firefox browsers and this is working just fine.

10 thoughts on “MultiFile upload / Drag and drop Attachments”

  1. Hi- I have been looking for this functionality. Recently, I found your solution and I went through your steps implementing it in a sandbox but I haven’t had luck making it work. Please can you confirm if this works for specific object or overall org and it would be great if you can demo it. I am sure alot of people would take benefit from this solution.

    Thank you for posting it.
    Mark

      1. Hi- thank you so much for volunteering to help. I checked the browser console and I found only this error “GET https://na17.salesforce.com/17181/logo180.png 404 (Not Found)”. I did some research online regarding the logo180.png and according to this post “https://success.salesforce.com/answers?id=90630000000D6qzAAC” it is a good thing.
        Not sure what I am missing.

        Cheers!
        Mark

      2. Hi Mark, That error should be fine. Can you add console.log() statements in some functions to check if they are called at all? For example in the addAttachButton() method just add a line console.log(‘inside addattchbutton funciton’); and see if its getting printed in the browser console. Also make sure you have added the home page component to the page layouts and that page layout is assigned to your profile and make sure you first check it on the standard pagelayout like accounts

      3. Hi- I did add some console.log() statements within functions but unfortunately it is not getting printed.
        Also, I have double checked that the custom link is added to custom link component and the component is added to the right home page layout which is assigned to system administrator profile that I currently use.
        I think the code is getting invoked all together. not sure why?

        Again, thank you for helping me sort this out. I know if we make it work it will be a huge help and a gap to fill on my end.

    1. Hi Junglee-

      The funny thing is I got it working when I added jQuery injector plugin to my chrome browser and pasted the above code in console. The box appeared on Attachment related list and when I dropped a file there it was attached. I love this enhancement. However, I still haven’t figured it out, yet, as to why it is not working with the custom link on a home page component.
      P.S. custom components were not enabled on every page and I enabled it but still not working. I added both URLs to VF page and added VF page to a custom link but still not working. 😦

  2. Hi Mark,

    This solution was based on the fact the requireScript will run on the page load. But Salesforce shut it down (http://docs.releasenotes.salesforce.com/en-us/winter16/release-notes/rn_forcecom_general_requirescript.htm#rn_forcecom_general_requirescript)..

    I have quite a few solutions implemented and they all are going down the drains.

    For this particular one, we can have button on the detail page and then invoke the scripts.

    Thanks
    Junglee

    1. Hi Junglee,

      Thank you for responding. This is so sad news. So, that is why it wasn’t working. Now, it makes sense.

      Also, thank you for sharing the link to release doc.

      Even if you have a detail page button on the page layout user has to click it to invoke the scripts, right? If yes, then this solution sadly won’t work.

      Mark

Leave a comment