Jun 27, 2011

Display Picture in Contact

What if I want to display my image on contact, and I frequently change it also. The answer in general is, create a visualforce page page and override it to native contact detail page.


This doesn't sounds good to me, am not happy overriding native pages provided by Salesforce. So I finally decided to put my head together and think how I can do this in another way.


Idea came in mind that what if I use Notes and Attachments on Contact object. Yes! I have uploaded some of my pictures in Notes and Attachment and provided a button on detail page to select one of them.




When you click on the "Set Profile Picture" a pop up gets open where all valid images are shown with radio option as shown in above screen. Selected image is displayed on contact like this,



How can you implement this?? Follow some simple steps.

Make sure you copy all Labels, Names etc correctly.


1) Create field of Type : "Text" with Field Label : "StoreImageUrl" and Field Name : "StoreImageUrl" (Lenght - 255) -> Make it visible to all profiles but do not show this on any page layout. This is for internal use only.


2) Create another field of Type : "Formula" with Field Label : "&nbsp" and Field Name : "Image" with return type "Text"


Formula will contain
IF( StoreImageUrl__c != null , IMAGE( StoreImageUrl__c , '' , 100 , 100 ) , IMAGE('' , '' , 0 , 0))
Make it visible for all profiles and show it at top on all layouts.


3) Create an Apex Class
public class DisplayImagesController
{
    //Parent Contact
    private String ContactId ;
    //Image selected from UI
    public String selectedImage {get; set;}
    
    public DisplayImagesController()
    {
        //Fetching contact Id
        ContactId = ApexPages.CurrentPage().getParameters().get('Id') ;
        selectedImage = '' ;
    }
    
    public boolean validateImage(String image)
    {
        String Regex = '([^\\s]+(\\.(?i)(jpg|png|gif|bmp))$)';
        Pattern MyPattern = Pattern.compile(Regex);
        Matcher MyMatcher = MyPattern.matcher(image);
        return MyMatcher.matches() ;
    }
    
    public List<SelectOption> getItems()
    {
        List<SelectOption> options = new List<SelectOption>(); 
        
        //All attachments related to contact
        List<Attachment> attachLst = [SELECT Id , Name FROM Attachment WHERE ParentId =: ContactId] ;
        
        //Creating option list
        for(Attachment att : attachLst)
        {
            String imageName = att.name.toLowerCase() ;
            if(validateImage(imageName))
            {
                options.add(new SelectOption(att.Id , att.Name));
            }
        }
        
        return options ;
    }
    
    public PageReference SaveImage()
    {
        //Contact to update
        List<Contact> conToUpdate = new List<Contact>() ;
        conToUpdate = [select id,StoreImageUrl__c from contact where id =: ContactId] ;

        //Inserting image parth
        if(conToUpdate.size() > 0)
        {
            conToUpdate[0].StoreImageUrl__c = 'https://c.ap1.content.force.com/servlet/servlet.FileDownload?file=' + selectedImage ;
            update conToUpdate[0] ;
        }
        return null ;
    }
}


4) Create a visualforce page with Label and Name "DisplayImages"
<apex:page id="pg" controller="DisplayImagesController" sidebar="false" showHeader="false">
<script>
    function validateRadio()
    {
        var elLength = document.getElementById('pg:frm:pb:pbs:pbsi:selectR');
        var childInputElements = elLength.getElementsByTagName('input');
        var flag = false ;
        for(var i=0; i < childInputElements.length; i++)
        {
            if(childInputElements[i].checked == true)
                {
                    flag = true ;
                    break
                }
        }
        if(!flag)
        {
            alert('Please select one Picture') ;
            return false ;
        }
        else
        {
            Validate() ;
        }
    }
</script>
<apex:form id="frm">
    <apex:actionFunction name="Validate" action="{!SaveImage}" onComplete="alert('Picture is Applied'); window.close();" reRender=""/>
    <apex:pageBlock id="pb" title="Contact" tabStyle="Contact">

        <apex:pageBlockButtons >
            <apex:commandButton rendered="{!IF(Items.size > 0 , true , false)}" value="Save" onClick="return validateRadio() ;"/>
            <apex:commandButton value="Cancel" onClick="window.close() ;"/>
        </apex:pageBlockButtons>

        <apex:pageBlockSection id="pbs" title="All Images" rendered="{!IF(Items.size > 0 , true , false)}">
            <apex:pageBlockSectionItem id="pbsi">
                <apex:selectRadio id="selectR" value="{!selectedImage}">
                    <apex:selectOptions id="MyRadio" value="{!Items}" />
                </apex:selectRadio>
            </apex:pageBlockSectionItem>
        </apex:pageBlockSection>

        <apex:pageBlockSection rendered="{!IF(Items.size > 0 , false , true)}">
            <apex:outputLabel value="No attachments to display or there is no valid image in attachments."/>
        </apex:pageBlockSection>

    </apex:pageBlock>
</apex:form>
</apex:page>


5) Create a Detail Page button with Label : "Set Profile Picture" and Name : "Set_Profile_Picture", Behavior : "Execute Java Script" and Content Source : "OnClick JavaScript"
window.showModalDialog('/apex/DisplayImages?Id={!Contact.Id}','') ; 
window.location.href = window.location.href ;


6) Edit the contact layout and add "Set Profile Picture" button.


7) Create a contact and add some images in notes and attachments, click on "Set Profile Picture". Select the image and click save.


*Note : I hate using JS with IE.


Also you may have to change a line of the Apex code above : "https://c.ap1.content.force.com/servlet/servlet.FileDownload?file=' + selectedImage ;"


in "SaveImage()" method.


Above path can be different on different organizations, so you just need to make sure that this is correct by :
View the attachment uploaded and copy the path from the URL except the ID.You can also create a custom setting to store this path.


Once above steps are done we are all set to go.


Cheers.

27 comments:

  1. I simply can't get this Apex page to work, any tips? Billy@medleyhealth.com

    ReplyDelete
  2. Some problem with the Syntax Highlighter. I have corrected it now. Let me know if there is any other issue.

    ReplyDelete
  3. hmm I am very close, now displaying a broken image graphic.

    Could you please clarify this part of the guide?

    "Also you may have to change a line of the Apex code above : "https://c.ap1.content.force.com/servlet/servlet.FileDownload?file=' + selectedImage ;"


    in "SaveImage()" method. "

    Thank you!!!

    ReplyDelete
  4. When you upload the image in notes and attachment salesforce creates a public URL for it. You can use the URL anywhere. Something like hosting an image on imageshack.

    So just click on "View" of the file uploaded in notes and attachment. URL will look something like this

    "https://c.ap1.content.force.com/servlet/servlet.FileDownload?file=XXXXXXXXXXXXXXX"

    My concerns was just to verify your base URL.

    ReplyDelete
  5. Hmm ya ok well when I view it is:

    https://c.cs4.content.force.com/servlet/servlet.FileDownload?file=

    in my Salesforce. I went to the Apex code you have and changed the lines https://c.ap1.content.force.com/servlet/servlet.FileDownload?file=

    to

    https://c.cs4.content.force.com/servlet/servlet.FileDownload?file=

    on line 52 but it did not work unfortunately.

    Any thoughts??

    ReplyDelete
  6. this is great, got it to work in contacts now trying to figure it out for Person Account pages

    ReplyDelete
  7. trying to get this from my Sandbox to my Production but I am getting this:

    "Average test coverage across all Apex Classes and Triggers is 0%, at least 75% test coverage is required."

    ReplyDelete
    Replies
    1. Did you get a test class that have a code coverage bigger than 75%?
      I created a test class, but only reached 70%

      Delete
    2. My Test Class so far

      @isTest(SeeAllData=True)
      private class Facelift_Test_DisplayImages {

      static testmethod void Facelift_Ctr_DisplayImages_Test()
      {
      Running_Action__c ra = new Running_Action__c(Name='My Action');
      insert ra;

      Attachment attach=new Attachment();
      attach.Name='https://myscreenshot.png';
      Blob bodyBlob=Blob.valueOf('Unit Test Attachment Body');
      attach.body=bodyBlob;
      attach.parentId=ra.Id;
      attach.ContentType = 'image/jpeg';
      attach.IsPrivate = false;
      attach.Description = 'Test';
      insert attach;

      Facelift_Ctr_DisplayImages controller = new Facelift_Ctr_DisplayImages();

      controller.selectedImage=(String)attach.get(Schema.Attachment.Name);
      controller.SaveImage();

      System.assertNotEquals(null,controller.getItems());
      system.assertEquals(true,controller.validateImage(controller.selectedImage));
      system.assertEquals(0, controller.getItems().size());
      }
      }

      Delete
  8. Great Job with well explanation of Steps

    ReplyDelete
  9. Hi,
    I trying to generate and attach pdf file in acccount object...

    Attachment a = new Attachment();
    String str = "Salesforce";
    byte[] body = str.getBytes();
    a.setName("Signature");
    a.setParentId("0019000000A0AVGAA3");
    a.setContentType("application/pdf");
    a.setBody(body);
    SObject obj[] = new SObject[1];
    obj[0] = (SObject) a;
    connection.create(obj);
    System.out.println("Completed");

    Code attached the PDF file....but when i Open it "Failed to Load Pdf" in Google Chrome and
    File does not begins with %pdf in IE/Mozilla.

    Please help me out..

    Regards
    Saurabh

    ReplyDelete
  10. I like this solution. How hard would it be to alter the concept slightly to allow users to do something similar in Opportunity. Open notes and attachments, select a pdf and then send it as an attachment in an email?

    It seems like the two would be somewhat similar in code to start with (albeit the ending code would be quite different).

    ReplyDelete
  11. Hi Ankit,

    When i use attachments, my higher people in Role Hierarchy can view this image, but will my subordinates be able to view this image?

    ReplyDelete
  12. Hai
    how to display existing records from the object through vf page,can any one explain?

    ReplyDelete
  13. in the get(Exmp-getMethodName) method you query the records from object and display in VF page by calling-{!MethodName}...

    ReplyDelete
  14. Every thing is going fine i am facing problem at &nbsp field. how to display it at top of page layout.

    ReplyDelete
    Replies
    1. I'm also having the same issue with the field placement - it simply sits on the page layout and doesn't override the contact default image... Isn't that what it is supposed to do?
      Here's a screenshot:
      https://dl.dropboxusercontent.com/u/85602335/screenshotSFDC.png

      Delete
  15. Good idea!

    How about using AttachmentTrigger to set "StoreImageUrl"? So user just needs one step to do every thing: upload a image file ...

    ReplyDelete
  16. So i just did it in a rich text field because i had the image url. It was pretty straightforward. I just wrapped the url in a image tag

    ReplyDelete
  17. Has anyone else encountered the error:
    "A problem with the OnClick JavaScript for this button or link was encountered: undefined is not a function"
    Any ideas on how to resolve?

    ReplyDelete
    Replies
    1. Replace custom javascript code as below

      window.open('/apex/DisplayImages?Id={!Contact.Id}','_self');

      Delete
    2. i replaced this javascript when i am clicking on this page is refereshing but popup is not coming for selecting picture.

      Delete
    3. i replaced this javascript when i am clicking on this page is refereshing but popup is not coming for selecting picture.

      Delete
  18. hi,

    i uploaded a image in notes and attachments,but when i am clicking on the set picture button no pop up is displaying on the screen.How to overcome this.

    thanks,
    Suresh

    ReplyDelete
  19. This comment has been removed by the author.

    ReplyDelete