Thursday, 24 July 2014

Adding a picture to a record in Salesforce

The concept of "Profile pictures" is pretty common these days, and default pictures of products or locations are standard fare for most CMS systems and websites. Getting a default picture displayed directly on a Salesforce record can be a little tricky. In this blog post I am going to talk through how I added my own, user manageable contact picture directly on the Contact record page (but you can re-purpose it however you like to other object types).

[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!

Wednesday, 9 July 2014

Salesforce Summer is here!

Summer is almost upon us, and I don't just mean that every now and then the rain stops over here in England, I mean in just a couple of weeks the Salesforce release cycle will turn another notch, and one-by-one, all of our Salesforce instances will be the eager recipient of a whole flurry of new and exciting features. So we get to stare up and down the setup menu for the intreging little "New!" text next to menu items, and then spend a whole afternoon exploring the brilliant things to see and do.


If you want to find out everything there is to know about the coming release, I can recommend you read the Salesforce Summer'14 Release Notes - but seeing as they are 340 pages long, and you might have a busy day lined up ahead of you, let me take you though some of the most interesting bits from my point of view.

Incidentally, the release dates for each org are available on the Salesforce Trust Scheduled Maintenance page and you should be getting the white prompt page forewarning you of your instance update now when you log in.


So, first of all, I think we need to look at the most important aspect of the new release, because it is a feature that will affect many of us every day, many times a day.

It's the new logo.

It's a Snorkel. I Love it!

But moving on.


The key developer changes to my project work in this Salesforce Release are:

Longer text fields - The maximum length of Long and Rich Text fields has been increased from 32,768 to 131,072 characters - they still defaults to the usual length, and existing fields will not be changed. Exciting times for super-verbose users.

More lookups and external IDs  - Salesforce are always happy to deliver bigger limits as soon as the technology and hardware facilitates it, and it seems that with Summer'14 the database layer is able to deliver more capacity for relationships and external IDs (which, of course, means more indexes) so you can now have 40 (instead of 25) relationships and 7 (instead of 3) external IDs.

THREE TIMES THE API CALLS - Sorry, I had to shout that one. At least twice in recent memory, I have seen a developer edition max out it's API calls in 24 hours and seen said developer skulk off to the pub coffee shop because they can no longer work. Especially when the Developer Console was ridiculously API hungry, or someone was running tests from Sublime Text. Well, now they can work three times as long, because Developer Editions now get 15,000 API calls instead of the old 5,000.



Improved Setup search -  lets you directly search for custom objects and fields in the Setup menu search, saving us all that time going through "Create > Objects > ObjectName > Scrollscrollscroll"

Publisher Actions for Chatter Off Organisations - This is a common one for me, we have a lot of clients who are yet to accept the leap to enabling Chatter in their organisation. Before now this meant a lot of Salesforce1 and the incredible power of Publisher Actions was denied to them as well. Not any more! Publisher actions to directly update records straight from the button are now available to those businesses.

Proper find in the Developer Console - At last, a proper find, find next and find/replace tool will be available in the developer console! And even better, real search "across all files" will let you find that snippet of code you can't remember where you put it. They are also releasing the ability to re-format your indentation properly, but as a legitimate developer, I always indent my work correctly first time, every time... (yeahright). You can get to all of this through a new "Edit" menu item appearing soon.

No limit on the Describe methods - Did I mention Salesforce like to smash down their own governor limits? Well, here's another one for the scrap heap. Take a quick look at this search result page on the Salesforce Stack Exchange and you will see that people have been battling with this ceiling for a little while now. So... should we get a SFSE moderator to just delete all these questions now?! :D

Full name functionality - Found that you add a custom field "Middle name" to Contact for every other human-contact-heavy client case? Well, now (via a switch on from Salesforce) this is a standard field for Lead, Contact and Account sObjects (it is also on the Name and User objects).

...and that's just the big stuff...

 In the whole new domain of mobile applications, developers will see awesome new features in both SalesforceA and Salesforce1. I noticed for Spring'14 the Developer Certification now recommends you go through the mobile release training materials too, because the Salesforce1 Mobile App is not just a bolt on, or a piece of configuration, it is it's entirely own development suite now. My user highlight for Salesforce1 this release is Approval processes - Just like with change sets, approval processes are always rocking up to the party a little bit late, but they have finally made it into Salesforce1, users can now submit their records for approval from within the app. A slightly glaring hole plugged beautifully (and quickly).

My developer highlight for Summer'14 Salesforce1 (and I feel I have saved the best for last) is called the Account and Community Switcher - which is a complicated way of saying you can switch between orgs within Salesforce1 without having to log the whole app out. This is especially apt for me, as you can see from this tweet.. "Hmm. I wanna test this new app on @salesforce1... But I simply can't log out of my work org on my phone?? "Logout > Yes" just does nothing?!" I have something of a problem switching orgs on my phone (short of uninstalling the app and reinstalling it).

To swap orgs, you simply open the left hand nav, and at the top there is a picklist which will list all of the other orgs connected to the user currently logged in to the app on the device, and you can just tap another account to jump across. Sandbox heaven!! Thank you Salesforce.


(Of course - 6 months ago I would have been over the moon that Salesforce1 is now properly supported on Blackberry (and Windows) phones, but pfft who still has a Blackberry?!)


Final thought for the day, I wonder what the Southern Hemisphere think of the Salesforce release cycle names then, "Summer?" - "Not here mate!" pretty sure we already have all the Winter'14 features down in Oz..