[Incidentally I am aware of Social-Contact settings, but more often than not this doesn't provide a "suitable" photo of a contact - my facebook picture certainly isn't! - and it only works for Accounts and Contacts of course]
Here is the goal we are after then:
You can see the cool picture associated with the Contact there, and a button to "Upload new photo". Lets walk through the other cool functions of this simple feature, and then I'll tell you how I did it (copy-pastas can just scroll down for the code snippets).
When you first create/arrive at a contact page without a picture, you are presented with this:
Clicking the "Upload photo" button presents this standard form:
Which in turn allows a user to select a local file and "Save" it to the Salesforce record. Now whenever they view the record, they will be presented with this photo, and a button to overwrite this file with a new photo, which takes them via the same form as above.
The process actually taken here is that the form uploads a file to the "Notes and Attachments" of the record, with a specific filename (set in the Controller). The controller is careful to make sure there is only ever one of these files (in the obscure case another file is added manually, the first is retrieved by the page, and next time the profile picture is updated, it will clear out all spurious files)
By doing this, we don't need any new "rich text fields" on our record, or indeed anything more complicated than a Visualforce snippet and Apex Controller extension.
The visualforce page:
<apex:page standardController="Contact" extensions="ContactPhotoUploadController" showHeader="false" standardStyleSheets="true" sidebar="false"> <apex:form id="contentForm"> <div style="height:170px;"> <apex:pageBlock mode="maindetail"> <apex:pageblocksection columns="1" rendered="{!displaying}"> <apex:image height="150" value="{!URLFOR($Action.Attachment.Download, currentPicture)}" rendered="{!currentPicture != null}"/> <apex:outputPanel rendered="{!currentPicture == null}"><em>No picture currently available</em></apex:outputPanel> </apex:pageblocksection> <apex:pageblocksection columns="1" rendered="{! !displaying}"> <p>Use the button to below to select a new file and then press "Save"</p> <apex:inputFile value="{!profilePicFile}" /> <p>Or press Cancel to return.</p> </apex:pageBlockSection> </apex:pageBlock> </div> <apex:commandButton value="Upload new photo" action="{!toggle}" rerender="contentForm" rendered="{!displaying && currentPicture!=null}"/> <apex:commandButton value="Upload photo" action="{!toggle}" rerender="contentForm" rendered="{!displaying && currentPicture==null}"/> <apex:commandButton value="Cancel" action="{!toggle}" rendered="{! !displaying}"/> <apex:commandButton value="Save" action="{!saveFile}" rendered="{! !displaying}"/> </apex:form> </apex:page>
This handles the display (or not) of the picture, form and relevant buttons.. and also sets the height that we will be working with (which is relevant when we come to add this to the page layout in a second).
Here is the controller extension that facilitates the uploading of the file and retrieving it when the page is loaded:
public with sharing class ContactPhotoUploadController { Private Static FINAL String fixedFileName = 'profilePhoto.jpg'; public boolean displaying { get; set; } public Contact pageContact; public Blob profilePicFile { get; set; } public Id currentPicture { get; set; } /** Constructor, grab record, and check/load an existing photo */ public ContactPhotoUploadController(ApexPages.StandardController controller) { pageContact = (Contact)controller.getRecord(); List<attachment> currentPictures = [SELECT Id FROM Attachment WHERE parentId = :pageContact.Id AND name = :fixedFileName LIMIT 1]; if(currentPictures.size() != 0) { currentPicture = currentPictures.get(0).Id; } displaying = true; } /** toggle switches between the photo display and photo upload form */ public void toggle() { displaying = !displaying; } /** saveFile clears any existing profile picture, retrieves the data from the form, and saves it under the relevant filename*/ Public Pagereference saveFile() { // first, we cannot have any conflicting files List<attachment> savedPicture = [SELECT Id, name, body FROM Attachment WHERE parentId = :pageContact.Id AND name = :fixedFileName]; if(savedPicture.size() > 0) { delete savedPicture; } // Now, we save the new blob Attachment a = new Attachment(parentId = pageContact.Id, name = fixedFileName, body = profilePicFile); insert a; currentPicture = a.Id; displaying = true; return null; } }
Once you have this basic Visualforce page up and running, all you need to do is go over and configure your Contact (in this case) Page Layout, and add the Visualforce page to the screen, and set it to a height of 30px greater than the style height of the div - this is to fit the buttons in underneath.
Boom, there you go, now users can add and edit a default photo on any kind of record page (you just need to tweak the controller to use the standard controller of another object).
Please note, in this blog post example I have neglected to address any kind of permissions or security considerations.. so remember if you decide to implement this to think about which users should be able to load, or upload photos to records. That's the kind of rich, useful developer love you would get from a Desynit based implementation ;-)
If you have any queries about this, or would like a complete implementation of this brought to your Salesforce org. Please don't hesitate to get in touch with me directly, or give Desynit a call and we can sort you out!