https://cdnjs.cloudflare.com/ajax/libs/SyntaxHighlighter/3.0.83/scripts/shAutoloader.js

Tuesday, 23 December 2014

Return of the $5 eBonanza

5 Dollar  Social Media

If you’ve ever visited this blog before, you’ll know that I am the author of the Visualforce Development Cookbook. There’s even a handy link to allow you to purchase on the top right of every page.  If you haven’t purchased a copy to date, there’s really no excuse now, as this year sees the return of the Packt $5 eBonanza.  

That’s right, your eyes aren’t deceiving you, you have the opportunity to buy my book in eBook format (or more than one - its the perfect Christmas gift for old and young alike). Even better, this isn’t limited to my book - in fact its every eBook and video available from Packt!

The offer runs until 6th January 2015 so there's plenty of time to make a considered selection - its the perfect opportunity to pick a new technology to learn for the coming year.

You can find out more information at:  http://bit.ly/1wHPcbd

Saturday, 13 December 2014

Visualforce Markup and JavaScript Includes

When using JavaScript in Visualforce pages it is often useful to set the initial state via the Visualforce controller and then use JavaScript to manipulate the page layout and state based on user interaction.

Visualforce Markup and merge fields in JavaScript that are directly in the Visualforce page work fine - consider the following Visualforce page that uses unobtrusive JavaScript to attach click event handlers to the name elements of a table of opportunities. (Yes, I know this is a pretty contrived example!).

<apex:page standardController="Opportunity" recordSetVar="opps">
  <apex:includeScript value="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.min.js"/>
  <script>
  $(document).ready(function() {
    <apex:repeat value="{!opps}" var="opp">
      $('[id$="nameOppCol{!opp.id}"]').on('click',
        function()
    	{
    	   window.open('/{!opp.Id}',
    	               'Opportunity','height=500,width=800,left=100,top=100');
    	}
      );
    </apex:repeat>
  });
  </script>
  
  <apex:pageBlock title="Opps Table">
    <apex:pageblocktable value="{!opps}" var="opp">
  	  <apex:column headerValue="Name">
  	    <div id="nameOppCol{!opp.Id}">
    	  <apex:outputField value="{!opp.Name}" />
  	    </div>
  	  </apex:column>
  	  <apex:column headerValue="Amount">
  	    <div id="amountOppCol{!opp.Id}">
    	  <apex:outputField value="{!opp.Amount}"/>
    	</div>
  	  </apex:column>
  	  <apex:column headerValue="Close Date">
  	    <div id="closeDateOppCol{!opp.Id}">
  	      <apex:outputField value="{!opp.CloseDate}"/>
  	    </div>
  	  </apex:column>
    </apex:pageblocktable>
  </apex:pageBlock>
</apex:page>

The JavaScript relies on a couple of Visualforce markup components to set up the click handlers - an <apex:repeat> to iterate the opportunities and a merge field to access the id of the opportunity, which identifies the <div> element that the click handler should be bound to:

<apex:repeat value="{!opps}" var="opp">
  $('[id$="nameOppCol{!opp.id}"]').on('click',
    function()
    {
       window.open('/{!opp.Id}',
                   'Opportunity','height=500,width=800,left=100,top=100');
    }
  );
</apex:repeat>

The page displays a list of opportunities:

Screen Shot 2014 12 13 at 12 34 41

and clicking on an opportunity name opens a popup window to display the opportunity details:

Screen Shot 2014 12 13 at 12 36 06

However, when getting the page ready for a production environment, its always good practice to place the JavaScript into its own static resource file (its also better to minify it, but for ease of understanding I’m skipping that part), resulting in a Visualforce page that simply includes the static resource:

<apex:page standardController="Opportunity" recordSetVar="opps">
  <apex:includeScript value="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.min.js"/>
  <apex:includeScript value="{!$Resource.oppJS}"/>
  
  <apex:pageBlock title="Opps Table">
    <apex:pageblocktable value="{!opps}" var="opp">
  	  <apex:column headerValue="Name">
  	    <div id="nameOppCol{!opp.Id}">
    	  <apex:outputField value="{!opp.Name}" />
  	    </div>
  	  </apex:column>
  	  <apex:column headerValue="Amount">
  	    <div id="amountOppCol{!opp.Id}">
    	  <apex:outputField value="{!opp.Amount}"/>
    	</div>
  	  </apex:column>
  	  <apex:column headerValue="Close Date">
  	    <div id="closeDateOppCol{!opp.Id}">
  	      <apex:outputField value="{!opp.CloseDate}"/>
  	    </div>
  	  </apex:column>
    </apex:pageblocktable>
  </apex:pageBlock>
</apex:page>

 However, when accessing this page the click event handlers no longer work and inspecting the page shows JavaScript errors. Clicking through to the included resource shows that the Visualforce markup hasn’t been replaced with the appropriate HTML elements:

Screen Shot 2014 12 13 at 12 49 02

When the JavaScript markup is in the Visualforce page, it will be processed by the Visualforce rendering engine and replaced with HTML elements, text etc as required, before being delivered to the browser.

However, when the JavaScript is moved into a static resource and included, it bypasses the Visualforce rendering engine. The containing page is processed as usual and delivered to the browser, but only then are the referenced JavaScript resources included, so any Visualforce markup, merge fields etc remain in their literal form. The browser's JavaScript then attempts to process the JavaScript, hits the Visualforce markup and generates a syntax error. 

Friday, 28 November 2014

London Salesforce Developers - Certified Technical Architect Panel

Certified Technical Architect Panel

The November meetup of the London Salesforce Developers had a different format to usual, when a panel of Salesforce Certified Technical Architects convened to answer questions from the community about the Technical Architect certification process.

The meetup was hosted at Cloud Sherpas and MC’d by Francis Pindar, with most of the organisation coming (as usual) from Anup Jahdav.  The panel consisted of 6 CTAs - Luke Emberton, Wes Nolte, Andy Mahood, Seb Wagner, Francesco Iervolino and myself - Keir Bowden.

Ta1

Appearing at these events always requires a balancing act as the review board is subject to NDAs both from a candidate and judge perspective, so we aren’t able to go into any details of the board and have to make sure that we talk at a level that is useful but protects the certification - having worked so hard to achieve the credential the last thing any of us want is to devalue it.

We weren’t entirely sure how much interest there would be in an event of this nature, given that TA is the highest level of certification and not necessarily something everyone would have in their career plan.  As it turned out, this event had one of the best turnouts I can remember, as this shot from the back of the room shows.

Ta2

The key points from the panel with regard to preparation for the board were:

  • Identify the key areas from the study guide and try to get yourself on projects that have requirements of this nature - e.g. integration, complex security and sharing, single sign on
  • Its an intense experience to remember to take a step back and breathe from time to time
  • This isn’t something that you can prepare for simply by studying - you need real world experience
  • Pre-sales work is useful preparation for being peppered with questions on a solution you have just thought of!

Related Posts

Saturday, 22 November 2014

Responsive Images in Visualforce

Introduction

When building applications to run on the Salesforce1 platform across multiple devices, utilising responsive web design techniques is a must.  A responsive application adjusts its layout according to the capabilities of the device used to view.

One of the biggest challenges in responsive web design is image handling - how to efficiently display appropriately sized images. 

Download and Shrink

A 2012 survey found that 86% of sites delivered the same content regardless of the device. In my opinion one of the main reasons for this is the download and shrink approach to images. In this case, a single large image is embedded inside a block container (such as a <div> element) and styled to take up 100% of the width.  As the block container changes size based on the capabilities of the device, the browser will automatically scale the image. 

The following page is an example of download and shrink:

<apex:page sidebar="false" showheader="false">
  <style>
    .respImageContainer{
    	width:150px;
    	padding-top:10px;]
    }
    
	@media ( min-width: 768px ) {
    	.respImageContainer
    	{
    		width:320px;
    	}
	}
	
	@media ( min-width: 1024px ) {
    	.respImageContainer
    	{
    		width:677px;
    	}
	}
  </style>
  
  <div class="respImageContainer">
    <img style="width:100%" src="{!$Resource.S1DevWeekLarge}" />
  </div>
</apex:page>

If the device has a minimum width of 1024px, the <div> element containing the image has a width of 677px, which is the actual size of the image:

Screen Shot 2014 11 22 at 17 42 48

If the device has a minimum width of 768px, the <div> element containing the image has a width of 320px:

Screen Shot 2014 11 22 at 17 43 02

If the device has a minimum width of 320px, the <div> element containing the image has a width of 150px :

Screen Shot 2014 11 22 at 17 43 21

The original image is 677 x 259 pixels, 175343 in total. In the final screen shot, the image is resized to 150 x 57 pixels, 8550 in total.  This means that most of the image is discarded when displayed, which is extremely inefficient use of bandwidth. There is also another problem here, known as art direction - the map in the image in the final screenshot is really too small to be of any use, so it would be better to zoom in on the spaceman as the Salesforce1 developer week logo.

HTML5 Picture Element

In the fullness of time image handling will be provided by the HTML5 picture element, an example of which is:

<picture>
  <source media="(min-width: 1024px)" src=“large_image”></source>
  <source media="(min-width: 768px)" src=“med_image”></source>
  <source src=“small_image”></source>
  <img src="fallback_image"></img>
</picture>

as can be seen, the <picture> element contains a number of <source> elements, each of which refers to a different image. The <source> element optionally contains a media query which must be satisfied in order for the image to be chosen. In the example code above:

  • if the device has a minimum width of 1024px, the large_image file will be used. 
  • If the device has a minimum width of 768px, the medium_image file will be used.
  • Otherwise the small_image file will be used.

Note that the <picture> element also contains a regular HTML <img> element - if the browser doesn’t support the <picture> element, the fallback_image file will be used. The use of the fallback <img> element does present a challenge, as browsers typically try to pre-fetch images before the page has been completely loaded and parsed.  This can lead to the situation where the fallback image is downloaded, only to be discarded as the image file identified can be used instead.

Unfortunately browser support is still patchy for the <picture> element; at the time of writing (November 2014) only Chrome and Opera provide support by default. 

Picturefill

Picturefill, by Scott Jehl, is a Polyfill for the HTML5 picture element, that uses JavaScript to load the appropriate picture based on additional HTML5 data- elements on <div> or <span> elements:

<apex:page standardStyleSheets="false" showHeader="false">
  <style>
    .respImageContainer {
    	padding-top:10px;
    }
  </style>
<apex:includeScript value="{!$Resource.PictureFill_1_2}" /> <div class="respImageContainer"> <span data-picture="1" data-alt="S1 Dev Week"> <span data-src="{!$Resource.S1DevWeekSmall}"></span> <span data-src="{!$Resource.S1DevWeekMed}"
data-media="(min-width: 768px)"></span> <span data-src="{!$Resource.S1DevWeekLarge}"
data-media="(min-width: 1024px)"></span> </span> </div> <script> window.picturefill(); </script> </apex:page>

The image source is defined by the data-src attribute, while the media query is present in the data-media attribute.  I’ve included the Picturefill JavaScript as a static resource as I couldn’t find it on any of the major content delivery networks.  This adds a Picturefill function to the window element which is called to process the page and load any images. This results in appropriately sized images being delivered to each page, at 1024px:

Screen Shot 2014 11 22 at 17 49 29

at 768px:

Screen Shot 2014 11 22 at 17 44 12

and at 320px an image containing only the spaceman is rendered:

Screen Shot 2014 11 22 at 17 43 43

Picturefill has two versions - one that works against <div> elements and one that works against <span> elements.  I tend to use the latter as I've found that the <div> elements can interfere with the styling introduced by Bootstrap, which is my responsive framework of choice. The downside to this approach is that it introduces latency when loading images, as the page is rendered before the JavaScript executes, and only then are the images loaded.

Resources

Saturday, 15 November 2014

Visualforce, Required Fields and HTML5

A few weeks ago I encountered some unexpected behaviour around action regions and required fields which after some investigation turned out to be a consequence of using the HTML5 doctype. In this post I’ll present an example of the use of an action region in conjunction with required fields and show how it is impacted by HTML5.

Regular Form

As a very simple example, I have a page that allows the user to enter the name of an account and some additional information in a table.  The user can click the ‘Add Row’ button to add a new row to the additional information:

Screen Shot 2014 09 27 at 08 07 03

The markup for this page is a regular apex:form (the controller isn’t relevant to this post, but is available in the Resources section at the end of this post):

<apex:page controller="RequiredCtrl" tabstyle="Account">
  <apex:pageMessages id="msgs"/>
  <apex:form>
    <apex:pageBlock mode="maindetail">
      <apex:pageBlockButtons location="top">
        <apex:commandButton value="Save" action="{!save}" />
      </apex:pageBlockButtons>
      <apex:pageBlockSection title="Account Information">
        <apex:pageBlockSectionItem>
          <apex:outputlabel value="Name"/>
          <apex:inputfield value="{!Acc.Name}" />
        </apex:pageBlockSectionItem>
      </apex:pageBlockSection>
      <apex:pageBlockSection title="Related Information" columns="1">
        <apex:pageBlockSectionItem>
          <apex:commandButton value="Add Row" action="{!addRow}"/>
        </apex:pageBlockSectionItem>
        <apex:pageBlockTable value="{!rows}" var="row">
          <apex:column headerValue="Value 1">
            <apex:inputText value="{!row.val1}" />
          </apex:column>
          <apex:column headerValue="Value 2">
            <apex:inputText value="{!row.val1}" />
          </apex:column>
          <apex:column headerValue="Value 3">
            <apex:inputText value="{!row.val3}" />
          </apex:column>
        </apex:pageBlockTable>
      </apex:pageBlockSection>
    </apex:pageBlock>
  </apex:form>
</apex:page>

Clicking the 'Add Row’ button causes the post back to fail as there is a required field missing:

Screen Shot 2014 09 27 at 07 51 34

Adding an Action Region

An action region demarcates a limited section of the form to submit back with the post back. Note that this also requires a rerender component to turn the post back into an Ajax request.  By enclosing my table in an output panel containing an action region, the required field no longer has an impact as it is outside the action region (note that I’m also rerendering my page messages component, as without that any errors will be swallowed as detailed in this post):

<apex:outputPanel id="rows">
  <apex:actionRegion>
    <apex:pageBlockSection title="Related Information" columns="1">
      <apex:pageBlockSectionItem>
        <apex:commandButton value="Add Row" action="{!addRow}" rerender="rows,msgs"/>
      </apex:pageBlockSectionItem>
     ...
    </apex:pageBlockSection>
  </apex:actionRegion>
</apex:outputPanel>

I can now click the ‘Add Row’ button and a new row is generated without errors:

Screen Shot 2014 09 27 at 08 00 34

Using the HTML5 Doctype

If I then decide I’d like to take advantage of some HTML5 features and turn on the HTML5 doctype on the page:

<apex:page controller="RequiredCtrl" tabstyle="Account" doctype="html-5.0">
  ...
</apex:page>

Clicking the ‘Add Row’ button suddenly fails again, but with a different style of error decorator:

Screen Shot 2014 09 27 at 08 01 32

Digging into the rendered HTML shows that an HTML5 specific attribute has been generated:

<input id="..." required="required" size="20" type="text" value="">

this attribute is handled directly by the browser, which obviously has no idea that there is a bunch of JavaScript lying in wait to trap the post back and turn it into an Ajax request, so it just checks the input element and blocks the post back if it is blank.

immediate=“true"

 The same behaviour can be seen when the immediate attribute is set to true for a command button, action function etc., for exactly the same reasons described above.

Workarounds

  • The easiest workaround, if you aren’t relying on any HTML5 features, is to turn off the HTML5 doctype.
  • If you need HTML5 features, make the field non-required by specifying the required attribute as false in your Visualforce markup if you can (you can’t do this with standard object names unfortunately)
  • Manage validation and error messages yourself - see the Field Level Error Messages … links in the Resources section.
  • If neither of the above are suitable, use JavaScript to remove the required attribute from the rendered HTML. This is pretty fragile though, as it requires your JavaScript to know which fields are part of an action region and which aren’t.

Resources

Wednesday, 5 November 2014

Musings of a Trailheader

Screen Shot 2014 11 02 at 16 09 03

Introduction

Trailhead is a new interactive learning initiative for Salesforce developers - you can read more about it at my introductory post, or on any of what seems like hundreds of write ups in the developer community blogosphere. Having been through all of the available exercises and had a couple of days to reflect, here are a few of my thoughts on the system and some ideas for maximising the awesomeness.

Challenges

Challenges and automated checking are going to be key factors in the success of this program. The Salesforce developer community are a competitive bunch, and anything involving badges and points gets a lot of interest.  With 1.8 million developers already using the platform and more signing up on a daily basis, there’s no way a manual system would be able to scale, and any delays would likely lead to user frustration.  I’d like to find out more about how this works - it looks like it purely relies on API access to the developer edition rather than any Salesforce-only under the hood stuff, but I’d also be interested to know if there’s a package or framework that allows pure configuration, or if each challenge requires coding from scratch. Maybe a topic for a future Developerforce blog post.

Challenges that rely on having completed the follow-along exercises in the unit would make sure that even experienced developers had been through all of the details, rather than skipping straight ahead to the challenge.

A leaderboard would be a cool addition to this. I’d be inclined to avoid a lifetime top 10 type of board, as once a couple of months have passed it would become all but impossible for new Trailheaders to appear on it.  Instead, something that shows the top scorers for the last month, or biggest increase in points in the last week etc would give everyone a chance to see their name up in lights. An awesome spin on this would be to segment based on developer group membership, which would allow us to recognise the top performers in our groups each time we get together.

Another idea, which I’m still in two minds about myself, is timed challenges.  In this scenario, accessing the detail of a challenge starts a timer and the number of points available drops down over time. This should encourage Trailheaders to read all of the related material to ensure they are fully prepared for the challenge before attempting it, although if the points drop too quickly it might be off-putting.

Build a Complete Application by Following a Trail

A trail whose units/modules combined to leave the Trailheader with a complete and functional application would be amazing.  I’m well aware that the amount of effort and attention to detail involved would be enormous. It would also need to cover a wide range of topics, although judicious use of code shares/unmanaged packages could get the basics in place quickly.

Organised Trailhead Events

This covers a couple of scenarios:

  • Trailhead content and challenges specifically (or initially) produced for Developer Group Events. For example, last year we had Salesforce1 Developer Week across the globe, with mini-hacks for participants. Tying this into a new trail would help to drive interest and attendance.
  • Elevate/Hands On Workshops run via Trailhead - having run one of the Hands On Workshops at Dreamforce, it seems to me that this would be a good fit for the format.
I also think the existing content would be great for “Intro to Force.com” type events and I’m thinking about how we can leverage this in the London Salesforce Developer Group.

Get the Community (More) Involved

Judging by the number of tweets/blog posts/Facebook shares, the developer community are already pretty taken with Trailhead, but that’s purely from a consumer standpoint. Rather than just suggesting topics it would be great if we could build trails/modules/units (most likely the latter, as it looks like there’s a huge amount of effort involved in a trail!). With attribution for the author on the item, I’m sure there would be a deluge of submissions! This would also allow related technologies to be introduced, such as working with JavaScript frameworks or Responsive Design.

To be fair, I’d be amazed if this wasn’t already being considered at some level. It probably won’t happen for a little while, as Trailhead is shiny and new and I’m sure that the Salesforce developer relations team are learning as much from this as the Trailheaders. 

That’s my thinking for now - I’m sure other ideas will occur to me, in which case I’ll update this post if and when then do.

Related Posts

Tuesday, 4 November 2014

Apex Ternary Operator and Types

Recently I was refactoring some of my code that in a Visualforce controller that generated a list of select options to produce a custom picklist of records.  Depending on the use case, I needed the selectoption values to be either the id of the record or the name.  Initially I had two separate methods that did pretty much the same thing:

public List<SelectOption> getOptionsByName(List<Account> accs)
{
    List<SelectOption> results=new List<SelectOption>();
	for (Account acc : accs)
    {
        results.add(new SelectOption(acc.Name, acc.Name));
    }

    return results;
}

public List<SelectOption> getOptionsById(List<Account> accs)
{
    List<SelectOption> results=new List<SelectOption>();
	for (Account acc : accs)
    {
        results.add(new SelectOption(acc.Id, acc.Name));
    }

    return results;
}

as you can see, this isn’t a very good example of the principle of DRY, so it seemed a trivial task to alter these functions to delegate to a single optionBuilder that could handle either scenario:

public List<SelectOption> getOptionsByName(List<Account> accs)
{
	return buildOptions(accs, false);
}

public List<SelectOption> getOptionsById(List<Account> accs)
{
	return buildOptions(accs, true);
}

private List<SelectOption> buildOptions(List<Account> accs, boolean byId)
{
    List<SelectOption> results=new List<SelectOption>();
    for (Account acc : accs)
    {
        results.add(new SelectOption(byId?acc.Id:acc.Name, acc.Name));
    }

    return results;
}

This refactored code worked fine when I executed the getOptionsById method, but when I tried getOptionsByName, I got the following, unexpected error:

15:50:59.047 (47206263)|FATAL_ERROR|System.StringException: Invalid id: test

which indicated that the ternary operator:

byId?acc.Id:acc.Name

was trying to promote the acc.Name String to an Id. Looking in the Apex Developer’s Guide didn’t throw a whole lot of light on the issue: 

Screen Shot 2014 07 27 at 15 58 41

as always, when presented with behaviour in Apex which isn’t documented, I reverted to the Java documentation (Apex being based on Java), to see if there was any further information.  There is, but its not the most enthralling read - if you are interested in the fine detail, check out http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.25. If that’s TL and you DR, what it boils down to is that the ternary operator has a single type and the operands are converted to this single type as required, which in my case appeared to mean that as the Id was the first type encountered, that was taken as the type of the ternary operator, even though the parameter type for the method call is a String. So while the ternary operator is a short-hand for an if-then-else statement, it is one that converts:

byId?acc.Id:acc.Name

to 

Id result;
if (byId)
{
    result=acc.id;
}
else
{
    result=acc.Name;
}

Swapping the operands round fixes the problem nicely - the type of the first operand encountered is a String and as the Id primitive supports conversion to a String, everything works as expected:

!byId?acc.Name:acc.Id, acc.Name

For the sake of completeness, this is now shorthand for:

String result;
if (!byId)
{
    result=acc.Name;
}
else
{
    result=acc.id;
}

Monday, 3 November 2014

Book Review - Salesforce.com Customization Handbook

5986EN Salesforce com Customization Handbook Cover

(Disclaimer : I didn’t purchase my copy of this book - I was sent a copy to review by Packt Publishing)

The Salesforce.com Customization Handbook is written by a couple of people whose names will be familiar to members of the Salesforce community - Rakesh Gupta and Sagar Pareek, who previously collaborated (see what I did there) on Developing Applications with Salesforce Chatter.

The book covers a number of common areas of configuration/customisation, including user management, security settings, email administration, business process automation, data management and reports and dashboards. From the perspective of someone getting started with Salesforce, (who has maybe been dropped in at the deep end and become the de facto administrator when their company decided to buy licenses) this is a useful guide to the setup areas that should be concentrated on, including some non-obvious candidates such as truncating custom objects and mass transferring approval processes.

Where the book doesn’t live up to expectation is explaining the concepts behind what is being configured, or work through much in the way of real world examples, especially where there are multiple options.  For example, the chapter on Setting Up Deployment Processes introduces change sets, the Force.com IDE, and packages (both managed and unmanaged). What it doesn’t do is explain in what circumstances each of these would be an appropriate choice, or cover the advantages/disadvantages of one mechanism over another.  There’s also too much content that looks to be directly dropped in from the Salesforce help, or that simply repeats itself (that the Account Mailing Address field is used to store the company’s Mailing Address, for example, doesn’t really add any value).

In summary, this is a useful book for those getting started with Salesforce setup and customisation. However, with a little more focus on educating as well as informing it could have been so much more.

You can find out more about this book and purchase a copy on the Packt website.

Sunday, 2 November 2014

Trailhead - Interactive Force.com Developer Training

Screen Shot 2014 11 02 at 16 09 03

One of the big announcements at the Dreamforce 14 Developer Keynote was Trailhead - interactive training for developers new to the platform or wanting to dig deeper.  Things at BrightGen are always a little hectic after Dreamforce, but as I’m now on a week’s vacation in beautiful North Norfolk I’ve finally had some time to take a look at it.

Overview

Trailhead consists of Trails that cover aspects of the Salesforce1 platform.  At the time of writing (November 2014) there are three Trails - Getting Started with the Force.com Platform, Intro to Visual App Development and Intro to Programmatic Development. Trails comprise of Modules (key topics for the trail - so Intro to Programmatic Development, for example, has modules for Apex Triggers and Apex Testing), which are in turn made up of Units. A Unit introduces a specific concept, such as a Visualforce Standard Controllers or Apex Bulk triggers, which is explained in depth in conjunction with follow along exercises.

Gamification

Most units finish with a challenge - an exercise to be completed in your developer edition which is then verified by the Trailhead system. You give Trailhead access to your developer edition as a connected application and grant access via oauth - this is the kind of functionality I’m always keen to see in these kind of systems, as it means it is able to scale without detracting from the user experience by making people wait to have their exercises assessed.  

Successful completion of a challenge gains you points, and once all units in a module have been completed you are awarded a badge. These appear on your Salesforce Developers profile page, gaining you bragging rights and displaying your expertise to potential employers.

Screen Shot 2014 11 02 at 16 04 21

So What’s it Like?

First off, its probably worth saying that the current Trailhead content isn’t really targeted at the likes of me - its more for people getting started with the area of the platform covered by the module.  That said, its important to me that I go through everything in detail, so that I can identify what the benefits are for our customers and staff.

In short, Trailhead is awesome. I wish there had been something like this available when I started out the with platform a shade over 6 years ago - I’d have been productive in an even shorter time (although at the time I’d have been skeptical that was possible!). At BrightGen we specialise in taking developers from .Net/Java and cross training them onto Salesforce and Trailhead will allow us to give them a flying start.

TIL

  • Follow the instructions to use a new developer edition - some challenges are documented as not possible with a namespace, but at least one other fails as well!
  • The Import Wizard really doesn’t like CSV files from a Mac - I tried everything I could think of to no avail, so I ended up cheating and loading the data using Jitterbit. It shows how long its been since I used the Import Wizard, as I’ve been on a Mac for around 3 years now!
    Update 04/11/2014 : this appears to have been a temporary problem - I’ve since been able to process the file in the same dev org via multiple browsers. If you encounter the same error try waiting a day or two!
  • The related resources are well worth a read before the challenge - they’ll cover the additional features that you need to use.
  • If you get the message ‘there was an error checking the challenge’, just try again - this never happened twice in a row for me.
  • If you switch to a  new developer edition part way through (because you didn’t follow the instructions and sign up for a new one, for example!), make sure to load any data from previous exercises, as a later challenge may rely on accessing it
  • If a challenge says it is relying on a specific number of records satisfying a set of criteria, make sure there are exactly that number.  I had one additional record and the challenge failed.
    Update 04/11/2014 : I’ve retested this in another developer edition and it is now working as expected.  Most odd as this failed easily 10 times in a row over the weekend until I deleted the additional record and then it worked first time.  Coincidence or magic?
  • Flow has come on a fair bit since I last used it.

In Conclusion

What are you waiting for?  Trailhead is a great way to learn so get started!

One More Thing

Don’t forget its Meet the BrightGen Team at Salesforce Tower on November 12th - we’ll have pizza, beer and some experts to chat with, so make sure to signup now to avoid disappointment.

Monday, 6 October 2014

Meet the BrightGen Team

Brightgen logo 300px

On November 12th 2014, BrightGen will be hosting a meet the team event at our London offices on the 18th floor of Salesforce Tower. This is an informal networking event where you can find out more about what its like to work for BrightGen and meet some of the people who work here, both old-timers and new-joiners.

Doors open at 6:30pm and we’ll be providing pizza, beer and a great view of London:

Ht

 I’ll be joined by a couple of other members of staff to give a short presentation covering:

  • An overview of BrightGen
  • Moving over to Salesforce Development from .Net/Java
  • Joining as a graduate

So if you are a Salesforce Developer looking for a new challenge, or a .Net/Java developer thinking about making the jump to Salesforce development, or just plain curious about what life is like with a rapidly expanding Platinum Partner, come along and join us for the evening.  If this isn’t for you, but you know someone who might be interested, send them along to find out more.

Space is limited for this event, and to keep security happy we need to know who is coming along in advance, so reserve your space here.  

I look forward to seeing you in November.

Sunday, 5 October 2014

London Salesforce Admins - First Awesome Meetup

The inaugural meetup of the London Salesforce Admins community (with an excellent turnout for the first event) took place on Thursday 2nd October at Dotmailer’s London offices - a great space overlooking London Bridge and the River Thames.

After some welcome drinks and networking, Matt Morris kicked things off with a short talk on the format and content for the evening:

Mm

The first talk was a comprehensive look at the GA features of Winter 15 from Mike Gill:

Ag

after a short break for “refreshments” (beer!) and some more networking, we were back for Francis’ Five Minute Feature with Francis Pindar, showing how to create images using formula fields:

Fp

and finally on the formal talks, Winter 15 hidden gems from Chris Edwards, giving a heads up on some of the pilot/beta features in the new release that will hopefully make it to GA before too long:

Ce

The presenting side of the evening was then wrapped up with the Open Mike session (so called as its hosted by Mike Gill!), where anyone can step up and talk for a couple of minutes on a subject of their choosing. If you’ve been wanting to take the plunge in public speaking but have been nervous of a length session, this is a great way to get your feet wet.

I spoke to a number of people during the subsequent networking and the consensus was that the organisers have found a definite gap to fill - the Salsforce User Group is quite high level and user-oriented, while the London Salesforce Developers tends to be focused on code  rather than clicks. 

The next meetup is on Thursday November 6th, and will no doubt have some news and features from Dreamforce, so if you are working as (or want to work as) a Salesforce Adminstrator in the London area make sure to add it to your diary.

On a related note, the October meetup of the London Salesforce Developers has been cancelled as it was scheduled a little close to Dreamforce, but we’re back in November.

Monday, 29 September 2014

My Dreamforce 14 Sessions

Screen Shot 2014 09 28 at 08 30 40

Dreamforce is only a couple of weeks away now, and once again I’m (co-)presenting a few session in the dev zone:

Monday 13th October 12:45 - 13:15 - Tech Talks 301: Stage Presence

This is part of a series of talks aimed at encouraging new speakers - from the overview "You'll learn how to overcome your anxiety and present with confidence”.  Very few people are born to present and, believe it or not, I wasn’t one of them. I’m co-presenting this session with Mark Passavoy of Appirio and I’ll share my top tips for great sessions.

https://success.salesforce.com/Ev_Sessions#/session/a2q30000000iWJJAA2

Tuesday 14th October 10:00 - 10:40 - Responsive Design with Visualforce and Twitter Bootstrap

Responsive design is a key requirement when building Visualforce pages nowadays, especially with the advent of Salesforce1 and the need for apps to display appropriately on any device. Come along to this session to learn what responsive design is all about, the key techniques and tools, along with a demo built using Twitter Bootstrap and Visualforce, and a look at the code.

https://success.salesforce.com/Ev_MyAgenda#/session/a2q30000000gunfAAA

Wednesday 15th October 12:15 - 2:45 - Hands-on Workshop: Intermediate Development with Heroku and Force.com

A hands-on Elevate-style workshop in the devzone.  Don’t forget your laptop!

https://success.salesforce.com/Ev_MyAgenda#/session/a2q30000000iHsMAAU

Wednesday 15th October 10:30 - 11:30 - The Salesforce Developer Keynote

Okay, this isn’t one of my sessions - I’m still not important enough to make the stage for this one, although if Dave Carroll twists a knee on the steps up I’m ready to step in! The Developer Keynote is always the highlight of Dreamforce for me and this year will be no different, so make sure you don’t miss out and reserve your seat today.

https://success.salesforce.com/Ev_Sessions#/session/a2q30000000iVwcAAE

I’m also waiting to hear on a couple of other things (*cough* hackathon *cough*), so keep an eye out for updates to this post.

Saturday, 27 September 2014

London Salesforce Developers September Meetup

The September gathering of the London Salesforce Developers took place on September 24th at the Make Positive offices on the south side of the River Thames.  The talk this month was a continuation of the previous month’s integration theme on Multi Org Collaborative Architecture, by Richard Clark.

IMG 1468

I’m not going to give too much away about this talk, as it was a dry run for Dreamforce. If you aren’t going to Dreamforce you’ll be able to view the slide deck and probably a recording of the talk as most dev zone sessions are recorded.

What I will say is that there is some good, generic single versus multi-org content which anyone considering the two strategies will find useful, followed by a deeper dive into the challenges and solutions around cross-org collaboration.

As usual this was followed by a trip to a local hostelry for some further discussion and networking (and the usual delays on my train home, as my twitter followers know only too well!).

The inaugural meetup of the London Salesforce Admins takes place on Thursday October 2nd - I’m attending this so stay tuned for a write up after the event.

Saturday, 20 September 2014

Sorting Visualforce Tables with JavaScript

Overview

Since the introduction of List Sorting and SOQL offset, presenting tables of sorted data in Visualforce is straightforward.  Both of these require the data to be retrieved/sorted server side, so each time the user changes the sort order of the table a full round trip takes place, including a post containing the view state if the page contains a form. Sorting the table in the client is orders of magnitude faster, and using the JQuery tablesorter plugin, straightforward to implement.

Setup

To demonstrate this, I put together a simple custom controller that pulls 5 accounts from the Salesforce database and makes them available to the page as a list. There’s nothing particularly relevant to the topic of this post, so I won’t be digging into it.  You can view the controller at this gist.

Next, I created a Visualforce page and included the JavaScript resources.  Tablesorter can be downloaded from the home page, but I prefer to use a Content Delivery Network wherever possible. An excellent CDN for JavaScript is cdnjs.com, and this hosts all of the tablesorter resources. I’ve included the JavaScript and a theme to style the table, plus JQuery from the google CDN:

<apex:includeScript
  value="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" />>
<apex:includeScript
  value="//cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.17.8/js/jquery.tablesorter.min.js" />
<apex:stylesheet
  value="//cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.17.8/css/theme.blue.css" />

Then a simple datatable to iterate the accounts and output the details:

<apex:datatable value="{!accounts}" var="acc" id="accsTable" styleclass="tablesorter">
  <apex:column headerValue="Created">
    <apex:outputText value="{0, date, dd/MM/yyyy}">
      <apex:param value="{!acc.CreatedDate}" />
    </apex:outputText>
  </apex:column>
  <apex:column headerValue="Name">
    <apex:outputField value="{!acc.Name}" />
  </apex:column>
  <apex:column headerValue="Street">
    <apex:outputField value="{!acc.BillingStreet}" />
  </apex:column>
  <apex:column headerValue="City">
    <apex:outputField value="{!acc.BillingCity}" />
  </apex:column>
  <apex:column headerValue="State">
    <apex:outputField value="{!acc.BillingState}" />
  </apex:column>
  <apex:column headerValue="Postcode">
    <apex:outputField value="{!acc.BillingPostalCode}" />
  </apex:column>
</apex:datatable>

 Note that I’ve specified an id for the datatable and applied the tablesorter CSS class - the former allows the JavaScript to apply the sorting capability to a specific table and the latter allows the chosen theme to be applied.

 Finally, some JavaScript to turn the rendered table into a sortable table:

<script>
  $(document).ready(function()
  {
    $("[id$='accsTable']").tablesorter({theme: 'blue', dateFormat : "dd/mm/yyyy"});
  });
</script>

There are a number of configuration options for tablesorter - I’ve just used two here, to specify the theme I want to use and to change the date format to match that I have used when displaying my account created dates.  This allows tablesorter to identify the column as containing date values, and apply the appropriate sorting algorithm. The final point to note is how I’ve identified the table element - as explained in my $Components versus Selectors post, when Visualforce components are rendered the element id contains the full hierarchy of parent component identifiers, and as my data table can move around, I simply find the element that ends with my specified id, using the id$= selector.

Demo

Like my Slick Salesforce1 Messages with Alertify post, screenshots don’t really do this justice so I’ve recorded a short video showing the page in action:

You can also access a version of this page at my Force.com Demo Site

Gotchas

  • Tablesorter requires that the table contains <THEAD> and <TBODY> HTML elements. The Visualforce datatable renders these elements at present, but if Salesforce were to change this in the future the sorting capability wouldn’t be added. In this situation I’d need to convert this to a vanilla HTML table.
  • This will only sort the rows displayed on the page. If this page displayed a subset of rows in a collection, the sorted values wouldn’t reflect the full dataset. In this scenario I’d re-run the SOQL query with the appropriate ordering or sort the collection of data server side.

Resources

Friday, 12 September 2014

Slick Salesforce1 Messages with Alertify

[As I was checking my references and links etc for this post, Fabien updated the home page to indicate that he will no longer be maintaining alertify. I'll continue to use it as it does everything I need and (so far) I haven't encountered any problems with it. Thanks for everything you’ve done Fabien - its a great tool]

In a few of my recent posts around Salesforce1, I’ve made use of the alertify JavaScript library by Fabien Doiron to display animated success/failure/info messages to users. This is such a useful library I’ve decided its worthy of a post and example page all of its own.

While regular Visualforce page messages are a great way to communicate information to the user, when you move to Visualforce remoting in order to provide a more reactive user interface in the Salesforce1 mobile application, a different mechanism needs to be found.  Regular JavaScript alerts can be used, but these are somewhat old-fashioned and provide a less than app-like experience.

Alertify provides styling and animation via a small amount of JavaScript and CSS. The minified library weighs in at a svelte 8Kb, plus an additional 7-8Kb for the CSS, depending on which theme is chosen. I won’t go into full details of alertify, in terms of configuration/options etc, as there is plenty of information and live examples on the home page

In order to use alertify, download the zip from the home page and upload this as a static resource - I always include the version in the zip file name so that I can serve multiple versions from the same Salesforce instance:

Screen Shot 2014 09 12 at 08 31 06

In order to demonstrate alertify, I’ve put together a simple ‘Check In’ Visualforce page for use as a custom publisher action - this captures the user’s current location and any details they care to add, creates a custom Check In record and posts this to the user’s chatter feed.  As always when I build Salesforce1 publisher actions, I’m using the Bootstrap frameworkalertfy, which I’ve also uploaded as a static resource:

 

alertify provides a bootstrap theme, so the alerts will match the bootstrap standard colours for warning/info messages etc. To use alertify in my Visuaforce page, I simply include the JavaScript and CSS: 

 
<apex:stylesheet value="{!URLFOR($Resource.alertify,
      'alertify.js-0.3.11/themes/alertify.core.css')}"/>
<apex:stylesheet value="{!URLFOR($Resource.alertify,
      'alertify.js-0.3.11/themes/alertify.bootstrap.css')}"/>
<apex:includeScript value="{!URLFOR($Resource.alertify_0_3_11,
      'alertify.js-0.3.11/lib/alertify.min.js')}"/>

 I then make one small change to the styling to display the alerts half way down the page:

<style>
  .alertify-logs {
      top: 150px;
      right: 10px;
  }
</style>

Once the user has filled in the form and clicked the ‘Submit’ button, the Visualforce remote action is executed and the if successful, a success message is displayed to the user:

alertify.success('Check In Completed');

if, on the other hand, an the checkin fails, an error message is displayed

alertify.error('Checkin failed : ' + result);

similarly, if there is a remoting error:

alertify.error('Remoting error : ' + event.message);

Screenshots don’t really do alertify justice, so I’ve recorded a short video showing the check in in action:

I’ve added this demo functionality to the SF1Utils Github repository - click on on the following links to view the controller and page 

Sunday, 7 September 2014

Salesforce1 Notifications from Apex

As documented in the Salesforce Summer 14 Release Notes, there are a number of activities that send a Salesforce1 notification to a user (assuming they haven’t disabled any), but all of these require manual actions to be taken via chatter.  While playing around with some code to notify the owner of a Salesforce system that a new user had been created in their system, I wanted to be able to send a notification programmatically.

The least intrusive mechanism looked to be a post to my profile, with a mention to the user in question - “Keir Chatter” in my case.  My first attempt at this was to simply create a FeedPost instance and set the body of the post to ‘@[Keir Chatter] a new user has been created’. Unfortunately, this simply placed the raw text ‘@[Keir Chatter]’ into the feed and didn’t get picked up as a mention at all.  

After a little research, I realised that I needed to use the Chatter Connect API, specifically the version of the postFeedItem method that takes a ConnectApi.FeedItemInput as a parameter, as the documentation makes clear (the bold is mine): 


postFeedItem(String, ConnectApi.FeedType, String, ConnectApi.FeedItemInput, ConnectApi.BinaryInput)

Adds a feed item to the specified feed from the context user Use this method to post rich text, including @mentions and hashtags, and to attach a file to a feed item. You can also use this method to share a feed item and add a comment. 


Generating a FeedItemInput is a little more complex than simply adding a body property to a post - the mention and the message have to be constructed separately as segments and then attached.  As I wanted this to be reusable, I created a utility method that would take a user id and message and take care of all the heavy lifting (the utility method also excludes attempts to notify yourself - i.e. to post an @mention to yourself on your own chatter feed):

ConnectApi.MessageBodyInput messageInput = 
new ConnectApi.MessageBodyInput();
messageInput.messageSegments = 
new List<ConnectApi.MessageSegmentInput>(); // create and add the mention segment ConnectApi.MentionSegmentInput mentionSegment =
new ConnectApi.MentionSegmentInput(); mentionSegment.id = userId; messageInput.messageSegments.add(mentionSegment); // create and add the message body segment ConnectApi.TextSegmentInput textSegment; textSegment = new ConnectApi.TextSegmentInput(); textSegment.text = message; messageInput.messageSegments.add(textSegment); // create the FeedItemInput and add the messageInput to it ConnectApi.FeedItemInput input =
new ConnectApi.FeedItemInput();
input.body = messageInput;

// finally, post to the current user's feed
ConnectApi.ChatterFeeds.postFeedItem(null, 
ConnectApi.FeedType.News, 'me', input, null);

I then created a simple trigger to notify the user whose name begins with ‘keir.chatter’ when a new user is added to the system - note that this is a chatter free user - the user doesn’t need a full Salesforce license as the post isn’t associated with a Salesforce record:

trigger user_ai on User (after insert)
{
  User notifyUser=[select id from User where
                    username like 'keir.chatter%'];
  MentionUtils.NotifyUser(notifyUser.Id,
            ' a new user has been created - ' +
            trigger.new[0].username);
}

Note that the trigger is set up to handle a single record at a time, purely for the purposes of this post.  This does show up one weakness of this technique though, as there is no way to bulk post a number of messages at the time of writing (September 2014).  If I needed to handle multiple records in this case I’d just post a large message containing information about all of the users that had been added.  If I needed to notify a different user per record, I’d have to use some asynchronous processing (either @future or batch Apex) to avoid breaching governor limits.

Adding a new user to the system:

 

Screen Shot 2014 09 07 at 16 04 51

 

sends a notification to my chatter user:

 

Screen Shot 2014 09 07 at 16 54 16

 

accessing Salesforce1 shows the red bell indicating I’ve received a notification:

 

Screen Shot 2014 09 07 at 16 55 37

 

tapping the bell shows a summary of the post:

 

Screen Shot 2014 09 07 at 16 55 53

 

and finally, click on the notification takes me to the full post:

 

Screen Shot 2014 09 07 at 16 56 08

 

I’ve added this utility class and unit test to my SF1Utils github repository and unmanaged package that I created for an earlier post

Friday, 5 September 2014

Check File Size on Upload in Visualforce

When uploading a file via the Visualforce apex:inputFile standard component, if the file size exceeds the particular Salesforce limit (e.g. uploading an attachment greater than 25Mb in size), the first the user knows about this is after the file has been uploaded to the server and an attempt is made to insert it into the database.  In the event of a large file over a slow connection (exactly the type of connection I am using to write this post from the North Norfolk coast), this isn’t the greatest user experience.

A better way would be to check the size of the file can be handled on the server prior to starting the upload.  Up until a few years ago, this wasn’t possible using regular HTML and JavaScript, instead something like a flash plugin was required. The advent of HTML5 introduced the File API, which provides access to the file and its metadata directly in the browser.

To demonstrate this I have a simple page that uses the account standard controller, with an extension controller to insert the attachment.  The page displays the current set of attachments associated with the record and a button to allow a new attachment to be uploaded:

Screen Shot 2014 09 05 at 17 21 19

Clicking the upload button and selecting a file larger than 25Mb (a 120Mb+ movie in this case), cancels the form submission and pops up an alert explaining the problem to the user:

Screen Shot 2014 09 05 at 17 25 33

As I’m on a slow connection, I’ve also added a warning when the user chooses a file that is over 2Mb in size - in this case they have the option to continue, on the understanding it may take a while:

Screen Shot 2014 09 05 at 17 25 52

The file checking is carried out in JavaScript, from an onclick handler on the ‘Upload’ button:

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 = 25000000 > size;
            if(!goodSize)
            {
                alert(this.files[0].name +' is too large - please choose a file that is 25Mb or less');
            }
            else
            {
            	if (2000000 < size)
            	{
                    goodSize=confirm('The file size is ' + size +
                    	' bytes - this may take some time. Are you sure you wish to continue');
                }
            }

            return goodSize;
        }
    });
    return goodSize;
}

This function uses JQuery to iterate the file elements on the page (one in this case), and for each input, gets the first (and only) file chosen.  the call to getIEFileSize allows this page to work with Internet Explorer prior to version 10, although I haven’t tested this recently, as I only use Macs these days. For non-IE browsers, the size is determined by the filesize or size properties (filesize is deprecated now, but its always a good idea to allow for older browsers!).  

The rest of the function simply checks the size of the file and generates an alert or confirmation dialog box as required.  Finally, a true/false result is returned to the onclick handler - if false, the form submission will be cancelled.

You can access the full page here, and for the sake of completeness, the full controller here.

Sunday, 31 August 2014

London Salesforce Developers August Meetup

The August meetup of the London Salesforce Developers took place on Wednesday 27th, kindly sponsored by Make Positive.

This month our main organiser, Anup Jadhav, stepped away from the organisational side of the group and into the spotlight to present a talk on integration - building a messaging framework based on the Message Queue concept: 

Anup meetup

This is clearly a topic that a lot of people are interested in - the talk took in excess of 50 minutes and generated more questions than any talk I can remember. As this was a dry run of Anup’s Dreamforce talk, the slide deck won’t be available until after Dreamforce, although the good news is that isn’t that far away now.

If you are a Salesforce Developer in the London area and you aren’t attending these meetups, you should join us.  Its a great way to spend an evening - you’ll eat some pizza, drink some beer and learn something - what’s not to like.

If you are a Salesforce Admin in the London area, you should join the new London Salesforce Admins meetup group, organised by Matt Morris

In fact, if you are a Salesforce Admin or Developer in the London area, you should join both.  There’s plenty that everyone can learn from each other, and developers/admins will get closer at Dreamforce with the new Admin Zone in the heart of the Dev Zone at Moscone West. 

 

Saturday, 16 August 2014

Replace Visualforce Buttons in Salesforce1

On my Account record view page, I have a custom button :

Screen Shot 2014 08 16 at 16 31 17

which opens a Visualforce page to allow me to edit the Account and its related Contacts on a single page (based on my Edit Parent and Child Records with Visualforce - Part 1 blog post):

Screen Shot 2014 08 16 at 16 31 26

Unfortunately, the Salesforce1 application doesn’t render custom Visualforce buttons, so this is missing from the Account details view in the app:

Sf1 1

Now I can add the Visualforce page as a custom publisher action for the account object, allowing access from the publisher menu:

Screen Shot 2014 08 16 at 16 44 31

but this displays the page inside the publisher popup, meaning there’s less real-estate and the Cancel and Submit buttons that I don’t really want at the top of the page (note that the screenshot is from an iPad, on a phone the page isn’t really usable as it currently doesn’t use responsive design):

Screen Shot 2014 08 16 at 16 47 02

What I’d really like is the publisher action to launch the Visualforce page full screen, but unfortunately there’s no way to configure this.  What I can do, is have the publisher action launch an interim page, which then navigates to the Visualforce page via the Salesforce1 navigation JavaScript.  The interim page is pretty simple:

<apex:page standardController="Account">
  <h1>Please Wait</h1>
    Redirecting ....
  <script>
    if ( (typeof window.sforce != 'undefined') && (window.sforce!=null) ) {
      sforce.one.navigateToURL('/apex/AccountAndContactsEditV1?id={!Account.id}');
    }
    else {
      alert('Not in SF1 :(');
    }
 </script>
</apex:page>

Note that as always I’ve checked to see if I’m in the Salesforce1 application, as the navigateToURL method is only available when that is the case. Clicking on the ‘MultiEdit’ publisher action now briefly displays a Please Wait page:

Screen Shot 2014 08 16 at 16 53 42

followed by the MultiEdit page, without any unwanted borders or buttons:

Screen Shot 2014 08 16 at 16 55 02

As a bonus, the publisher action doesn’t impact the navigation, so clicking the back button on the top right takes me back to the record detail page of the account in question.  The downside to this is that it requires two round trips to the server - the first to retrieve the interim Visualforce page and the second to retrieve the target page. 

Sunday, 10 August 2014

Why You Have to Attend Dreamforce

Mqdefault

DF14 is just a couple of months away (at the time of writing [August] anyway - if you are reading this after October I’ve probably written a recap post on the event, so you should read that as well!).

If you’re still undecided (or you still haven’t managed to convince your boss), here’s a few reasons why the decision should be a no-brainer.

The Dev Zone

What can I say about the dev zone, aside from the fact that it gets better every year.  The opportunity for learning is incredible - there are loads of breakout sessions, multiple theatres running talks all day and free books (although these are very popular, so be prepared to queue). You’ll also bump into your friendly developer relations team and evangelists, ready and willing to shoot the breeze about all things Salesforce1.  Some people spend the whole event in the Dev Zone and more power to them - I have a bunch of partner sessions to attend when I’m at Dreamforce, so I only manage around 70% or so.

The Developer Keynote

This has been around for a couple of years now, and its always standing room only.  Its the place to find out about the latest features being launched for developers, and get a heads up on the direction for the next year or so.  Throw in a few demos from Dave Carroll, and its the perfect way to spend a couple of hours.

Networking

Anyone who’s anyone will be at Dreamforce, so its an excellent way to expand your network of connections. This also justifies your presence at any number of parties. If there are people you are keen to meet, following them on twitter and the Dreamforce application will give you a good idea of where they will be at a specific time, allowing you to casually bump into them and make that vital introduction.

Hands on Training

These sessions are always very popular, so if you fancy some instructor led app building with plenty of assistants to provide support, make sure to register as soon as the agenda builder goes live - they fill up in no time.

Its not that Expensive

For a limited time you can register for the bargain price of $899 at http://bit.ly/df14infblog  

Save Money Getting Certified

The weekend prior to Dreamforce there’s a bunch of certification-related training and cut-price exams available. If you aren’t certified then this can form the basis of your pitch to your boss - the savings are like money in the bank, and what better reward for passing a cert than attending the main event.

There’s more information and pricing at: http://www.salesforce.com/dreamforce/DF14/training.jsp

Give Back

There is always a volunteering event at Dreamforce - last year 2,700 bags were packed for Project Night Night.

My Session

Assuming I manage to keep on track, I’ll be giving a talk on Responsive Design with Visualforce and Twitter bootstrap, which I’m sure will be enormously popular and well attended.

Saturday, 2 August 2014

Book Review - Force.com Development Blueprints

2451EN

(Disclaimer: I didn’t purchase this book - I was sent an e-copy to review by Packt Publishing).

For the first time in six months, its book review time.  
 
Force.com Development Blueprints guides you through the development of number of complete applications on the Force.com (and other!) platforms:
  1. A Salesforce community, including the use of Site.com to create custom branded pages
    As I’ve not done a huge amount with Site.com I was keen to dig into this chapter, so it was handy that it was the first.  One of the things that surprised me is the amount of effort required in what is purported to be a simple drag and drop tool, not only the complexity of the tool itself but also the low-level CSS tweaks required. (This might explain why Site.com didn’t really take off as intended and is now being rolled into communities as a custom page builder).
     
  2. Develop an e-commerce application using Bootstrap, Ruby on Rails, Heroku and Visualforce
    I was surprised to jump straight into an application using Heroku - if you are working through the book in order it might make more sense to swap this with the third application, which is limited to the Force.com application itself.  Its a very thorough build out of the application, with a nice approach to building the basics first and then adding the slicker UI on top.

  3. Build a traditional CRM solution with a custom Apex workflow feature
    Another full-featured application, taking advantage of a lot of the clicks-not-code features of the platform. I’d like to have seen this as the first application, as this is very much about employee-facing applications - the sweet spot of Force.com I’ve always thought. 
     
  4. Building a custom reporting system using Visualforce
    An excellent real-world use case and example code that can be readily adapted to a number of scenarios. 

  5. Build an HTML5 mobile application, using Bootstrap, Angular, Node.js and Heroku
    The complexity ramps up several notches in this chapter, which is not for the faint hearted where JavaScript is concerned. Using Node.js for JavaScript on the server, and Angular as the front end MVW framework, there’s quite a learning curve if you are new to one or more of these technologies.  This is by no means wasted learning though, as JavaScript is eating the web and demand for mobile applications is very much increasing.  
     
  6. Building cloud connected applications
    Did I say application number 5 ramps up the complexity?  Chapter 6 takes it to the next level, building an application that connects to multiple external clouds via APIs and throws an Android application into the mix as well.  Set aside plenty of time for this one, as this number of external integrations means a pretty unforgiving development environment and promises plenty of opportunity to hone investigative and debugging skills.  It also bravely introduces the Android SDK, which readers of my musing on the BrightGen blog will remember was one of the more challenging aspects of the Elevate London Workshop. Once again, while this might (will!) be a challenging application to complete and understand, its very much the future of application development.  Applications built on Force.com are increasingly seen as part of the enterprise architecture and will be expected to integrate fully with the other architecture components.

As I’ve noted a couple of times above, I feel that the order of the applications could stand a little tweaking, so I would work through the applications in the following order:

3. Build a traditional CRM solution with a custom Apex workflow feature
4. Building a custom reporting system using Visualforce
1. A Salesforce community, including the use of Site.com to create custom branded pages
2. Develop an e-commerce application using Bootstrap, Ruby on Rails, Heroku and Visualforce
5. Build an HTML5 mobile application, using Bootstrap, Angular, Node.js and Heroic
6. Building cloud connected applications

This is a very well written book - each chapter flows well and the explanation of the purpose and high level overview of each application are clear and concise.  If I have one gripe its the amount of code, markup and repetitive instructions that appear (although the instructions do improve as the book goes on).  Often this comes across as filler, as the entirety of pages and controllers are presented, rather than just the key areas of functionality.  

This isn’t a book you’ll be able to skim through in a couple of evenings or a weekend. If you are coming over from the functional side of Force.com to development, you should expect the journey to take several months, returning to each chapter as your development experience increases - trying to rush through it when you aren’t that confident in the underlying technologies will likely lead to frustration when you are unable to get some of the more complex applications working.  If you are an experienced developer who is new to the Force.com platform you will go faster, but its still likely to be a number of weeks if you are making sure you understand all aspects of the applications.  It will be well worth the effort though.

You can find out more about the book, and purchase it, at:

Friday, 1 August 2014

Why I Don't Provide One to One Help

(This is a post that I’ve been considering for some time, but has been pushed to the front of my ideas stack based on the increasing levels of attitude I’m encountering)

Help me Bob Buzzard. You’re my Only Hope

I’m typically asked for one to one help several times a day, especially weekends!  Sometimes its on the Developer Force forums, where a poster asks for my email address so that they can contact my directly with some related questions, other times its direct emails with a lengthy code sample asking for me to write the test case. Lately its direct messages when I login to Facebook, asking me to get in touch when I’m free to answer some questions on web services, Visualforce, Apex in general, as there's an urgent customer requirement.

Regardless of the mechanism chosen, I don’t respond (in fact, this used to be in my signature on the original Lithium Developerforce Discussion boards, although it didn’t stop anyone). There is a very good reason for this:

I’m a Consultant

I’m a consultant by profession, for BrightGen, a UK-based Salesforce Platinum Cloud Alliance partner. When you are a consultant, you sell your time at a rate based on your experience and expertise.  As I’m a Certified Salesforce Technical Architect (along with all the other certifications), I’m charged out at the highest rate, so when a customer wants one to one assistance from me, they have to pay a not inconsiderable sum.  It would therefore be extremely unfair on both my customers and my company if I started handing out my time for free, simply because people contacted me directly through social media or personal channels.

Unfair on my customers, as they would be the only ones that had to pay for the privilege and unfair on my company as I’d be depriving them of potential revenue.  This is particularly unfair on those parties when someone is asking for help on their customer engagement - they are expecting me to fix their problems for free, so that they can complete the work that they are charging their customer for.  You wouldn’t charge to fix someone’s car, then take it to another garage and expect them to do the work for free as you are a "newcomer to car maintenance".

Its Nothing Personal

If you ask me for one to one help, I won’t respond.  Don’t take this as a personal slight - I don’t have a secret club of those that I do help, everyone is treated the same. If I accept your LinkedIn connection or Facebook friend request, that doesn’t mean I’m now your point of escalation for any and all Salesforce problems, your personal career counsellor, or your private support service.

I used to respond saying I only did this as a paid gig, but that started taking up too much of my time, so now I treat these requests as unsolicited contacts and ignore them. I’m sure that all these requests are super-urgent and its absolutely vital that the problem is fixed, but remember that’s a subjective view - there’s no urgency from my side and it makes no difference to me if the problem remains in place for years.  .  

Post it to the Forums

Post your request on the Developerforce forums and I’ll help if I can - even better, lots of other people will also see the post and will do their best to help as well. This way the knowledge is spread throughout the community as a whole. If you post a question and then ask for direct contact details, what you are effectively saying is that you’d like to tap into the community knowledge, but keep the solution to yourself, which is pretty un-community minded.

I Know its a Minority

This post isn’t an attempt to drum up more business - I’m really not that interested in single day engagements to debug code or write unit tests these days.  Its also not a cry for validation. I know that the vast majority of people that I interact with in the community don’t behave like this, so there’s no need for any “I appreciate you, Bob” style comments!

If nothing else, this post gives me a URL to point people at when I’m not replying and they don’t understand why.