Tuesday, 24 September 2013

Highlight Empty Fields

A question that came up this week was how to highlight to a user that fields in a form don't have a value, but without stopping the form submission or annoying them with popups/confirmation dialogs. Essentially flagging up 'nice to have' fields that are empty, but leaving the required fields with the standard Salesforce decoration and the fields that nobody really cares about alone.

When the form is initially rendered, this is easy enough to achieve through conditional styling, but the problem with this is that it can't change the background when the user populates the field - instead, a form postback is required and even if rerendering is used to minimise the page updates, a full round trip every time a field is changed is a pretty hefty tax on the user.

This seemed like a good fit for jQuery, so I fired up the Force.com IDE and created a simple lead entry page that highlights a selection of empty fields:

Screen Shot 2013 09 24 at 19 26 14

In order to easily identify the nice to have fields, I gave them each an id that started with 'flagEmpty':

<apex:inputField id="flagEmptyFName" value="{!Lead.FirstName}" />
<apex:inputField id="flagEmptyEmail" value="{!Lead.Email}" />

Next, I wrote the function to apply the necessary style class.  This takes all or part of an id, finds any elements containing the id and for each match, checks if the field has value.  If it does, the 'fieldpopulated' class is applied, otherwise the 'fieldempty' class is applied.  When the appropriate class is applied, the other class is removed:

function flagEmpty(theId)
{
  $("[id*='" + theId + "']").each(function(index, ele) {
		if($(ele).val().length > 0)
		{
			$(ele).removeClass('fieldempty');
			$(ele).addClass('fieldpopulated');
		}
		else
		{
			$(ele).removeClass('fieldpopulated');
			$(ele).addClass('fieldempty');
		}
	});
}

 When the page is initially loaded, the id fragment 'flagEmpty' is passed to the function, which finds all of the elements I've marked in this fashion and highlights the background:

flagEmpty('flagEmpty');

Finally, an onchange handler is added to each element with an id containing 'flagEmpty'. This handler extracts the id of the element and executes the 'flagEmpty()' method, passing the id as the parameter:

$("[id*='flagEmpty']").change( function(event) {
	flagEmpty($(event.target).attr('id'));
});

The fields marked as 'flagEmpty' are originally rendered with a yellow background:

Screen Shot 2013 09 24 at 19 29 22

but after filling in a field and moving focus, the onchange handler fires and changes the background to white:

Screen Shot 2013 09 24 at 19 31 26

The Visualforce page is available at this gist

 

Monday, 2 September 2013

MVP Summit 2013

Mvps

This week I attended the 2013 MVP summit in San Francisco.  As my employers, BrightGen, were kind enough to allow me to take three days out to travel and attend, I've written this up on the company Facebook page at: https://www.facebook.com/BrightGen - while you are there, please take a moment to like the page!

 

Sunday, 11 August 2013

Swipe Navigation

I've written a few blog posts in the past around using JQuery Mobile to give a native (well, iOS) feel to web applications. In each of these cases, I've used buttons in the header or footer to navigate around the site and open panels.  These days, users expect to be able to swipe to navigate, even when running web applications through the built-in device browser.

Luckily, JQuery Mobile has a solution to this - if the user touches the screen and moves their finger left or right more than a certain number of pixels while maintaing contact, a swipeleft or swipe right event will be generated. These events can then be handled in custom JavaScript functions to navigate the user to another page.

To demonstrate this, I've created a sample application containing three pages that each displays a picture from my Customer Company Tour Developer Theatre session in May 2013. The header contains the buttons to open a panel and navigate between pages, as shown below:

Page1Page2

This is a single page application, where a single physical HTML page contains a number of logical application pages coded as stacked <div> elements with a data-role of page. The markup for the first page is as follows:

<div data-role="page" id="page1">
  <div data-role="panel" id="aboutpanel" data-theme="b">
    <h3>About</h3>
    <p>Welcome to the Bob Buzzard swipe demo web application.</p>
    <a href="#page1" data-role="button" data-rel="close">Close</a>
  </div> <!-- /panel -->
      
  <div data-role="header" class="header-large">
    <h3>Swipe Demo</h3>
	<a href="#aboutpanel" data-role="button" data-icon="info" data-iconpos="notext">About</a>
    <a href="#page2" class="ui-btn-right" data-icon="arrow-r" data-iconpos="notext">Page 2</a>
  </div><!-- /header -->
    	
  <div data-role="content" style="text-align:center">
    <h1>Getting Ready</h1>
    <div style="margin:auto"><apex:image styleclass="image" value="{!URLFOR($Resource.CCT, 'CCT1.jpg')}"/></div>
    <p><caption>A quick chat with Nick Tran before taking the stage</caption></p>
  </div> <!-- /content -->
    	
</div> <!-- /page -->

To allow swipe navigation from the logical page with the id of page1 to the logical page with the id of page2, I need to handle the swipeleft event when it takes place in page1. The JavaScript for this is straightforward, thanks to the event handling capabilities of JQuery:

$j('#page1').on( "swipeleft", function() {
    $j.mobile.changePage('#page2');
});

An added benefit is that the swipe behaviour works on the desktop too, as the following video attempts to show.  the first part shows navigation using the buttons, while in the second part I simply click, hold and drag the mouse/trackpad left or right.

If you'd like to try this application yourself, its available on my dev org site at the following short link:

http://bobbuzz.me.uk/18lXNSx

or you can scan the following QR code on your mobile device:

Swipe qrcode

The Visualforce page containing the application is available at this gist - note that this page relies on a static resource containing the images, so if you clone it you'll need to replace this with images of your own.

 

Saturday, 20 July 2013

Publisher Actions - Not Just Creating Records

The Summer 13 Salesforce release introduced Publisher Actions - these allow additional chatter actions to be added to the publisher, over and above the standard Post, File, Link and Poll actions.   Some setup is required before you can use Publisher Actions - this is detailed in the Salesforce help here.

There are four types of Publisher Actions - Create and Custom for a specific object, and Global Create and Global Custom.  The Create actions allow a new record to be created directly from the chatter feed.  Create for a specific object allows a new record to be created that is related to the object the feed appears on - for example, to create a new Opportunity from an Account chatter feed where the Opportunity account id is set to the id of the Account the feed appears on. Global Create allows a new record to be created from a feed, but with no relationship to any existing object.  One very useful aspect of the Create actions is that you can specify default values for fields, which goes some way to removing the need for URL-hacking.

The Custom actions allow a Visualforce page to be displayed in a chatter feed - the specific object variant of this means that the standard controller must be used, as the id of the record the chatter feed appears on will be passed to the page, while the Global variant uses a custom controller. An example of a Custom action is to display the location of an account on a map - while you can also achieve this via an embedded Visualforce page on the standard record view, making it a Publisher Action means it is displayed on demand, so those users that aren't interested in that information don't have to wait for the map to render.

Custom actions can also create information. In this post I'll demonstrate how to create a Publisher action to post summary information to a feed.

Here at BrightGen, we have a Service Management offering for Salesforce. Rather than waiting until the end of the month to find out how we are doing for a particular customer, its useful to push information out on a regular basis. In this post I'll demonstrate how to create a Custom action that posts a snapshot of case information to an account record.

The snapshot will contain details of cases created this month, cases closed this month and the current number of open cases for the account.

As this is an object specific action, the page uses the account standard controller and the additional functionality is provided by a controller extension. This executes a number of queries to get the case information required.  The snippet below determines the number of cases closed this month:

Date startDate=System.today().toStartOfMonth();
Date endDate=System.today().addMonths(1).toStartOfMonth();
List<AggregateResult> ars=[select COUNT(Id) closedCount
		 	   from Case
		 	   where Status='Closed'
		 	   and AccountId=:accId
			   and ClosedDate>=:startDate
		   	   and ClosedDate<:endDate];

Integer result=0;
if (ars.size()>0)
{
	AggregateResult ar=ars[0];
	result=(Integer) ar.get('closedCount');
}

Once all the information is gathered, this is posted to the feed programmatically:

FeedItem item=new FeedItem();
item.ParentId=accId;
item.body='Service snapshot\n' +
          'As of ' + System.now().format('dd/MM/yyyy')  +
          '\nNew Cases : ' + getNewCases() +
          '\nClosed Cases : ' + getClosedCases() +
          '\nOpen Cases : ' + getOpenCases();
		
insert item;
		
ApexPages.addMessage(new
ApexPages.Message(ApexPages.Severity.INFO, 'Snapshot Posted'));
posted=true;

Note the final line of this snippet, which sets the boolean property posted to true - this property is used in to automatically refresh the page, otherwise the post does not get displayed to the user.

The page simply displays the details and provides a button for the user to post the details to the feed:

  <apex:pageBlock title="Service Snapshot">
    <apex:pageBlockButtons location="bottom">
      <apex:commandButton value="Post to Feed" action="{!post}" />
    </apex:pageBlockButtons>
    <apex:pageBlockSection columns="1">
      <apex:pageBlockSectionItem >
        <apex:outputLabel value="Closed Cases" />
        <apex:outputText value="{!closedCases}" />
      </apex:pageBlockSectionItem>
      <apex:pageBlockSectionItem >
        <apex:outputLabel value="New Cases" />
        <apex:outputText value="{!newCases}" />
      </apex:pageBlockSectionItem>
      <apex:pageBlockSectionItem >
        <apex:outputLabel value="Open Cases" />
        <apex:outputText value="{!openCases}" />
      </apex:pageBlockSectionItem>
    </apex:pageBlockSection>
  </apex:pageBlock>

The automatic refresh (which is based on the technique described in this blog post) is provided by the following, conditionally rendered, JavaScript:

  <apex:outputPanel rendered="{!posted}">
   <script>
      window.top.location='/{!Account.id}';
   </script>
 </apex:outputPanel>

The code for the controller extension can be found in this gist, and the Visualforce page in this gist. You'll need to save these into your Salesforce edition to complete the next steps.

Next, create a publisher action for this page - navigate to Setup -> Customize -> Accounts -> Buttons, Links, and Actions and click the New Action button.

Scroll down to the Chatter Feed section and fill out the details as shown in the screenshot below and click the Save button.

Screen Shot 2013 07 20 at 13 14 52

(note that this assumes you have saved the Visualforce page with the name ChatterAccountSnapshot - if that is not the case, pick the name that you saved the page under from the Visualforce Page pick list).

To add the Publisher Action to the chatter feed, edit the page layout for the Account record.  Scroll down to the Chatter Feed section - if you see the following text, click the 'overidde the global layout' to make the actions editable for that layout:

Screen Shot 2013 07 20 at 13 21 34

The Chatter Feed section will then contain the following standard actions:

Screen Shot 2013 07 20 at 13 23 19

Next, select the Actions entry from the menu at the top left of the page layout editor:

Screen Shot 2013 07 20 at 13 24 50

Drag the Snapshot entry from the right hand side to the Chatter Feed section - note that this can be placed anywhere in the list of actions:

Screen Shot 2013 07 20 at 13 25 40

and finally save the page layout.

 

Now lets take a look at the Publisher Action in action:

Navigating to an account view page shows the new Snapshot action in the Chatter Publisher. Clicking the Snapshot button displays the Visualforce page with the case detais:

Screen Shot 2013 07 20 at 13 27 01

Clicking the Post to Feed button posts the information to the chatter feed and then refreshes the page to display the post to the user:

Screen Shot 2013 07 20 at 13 29 17

 

Saturday, 29 June 2013

Meetups and More

Meetup with Apex Product Managers

On 20th June the London Salesforce Developers welcomed Apex Product Managers Andrew Waite and Josh Kaplan to our Meetup at SkillsMatter. There was a real buzz of anticipation for this event and it didn't disappoint.  After a short run through of some of the Apex specific enhancements for the Summer 13 release, and a sneak peek at a pre-release version of the chatter mobile app, we then entered a no-holds barred Q&A session.  One issue with this sort of informal session is that it can descend into a forum to air grievances and bad experiences - although we flirted with this, in general we managed to maintain the positive vibe that we all had at the start of the meeting. Something I always enjoy when I have a chance to speak to the product managers is finding out which enhancements that I view as simple and can't understand why they aren't complete already are actually incredibly hard to do and require a complete overhaul of a section of the platform. 

After the event Andrew and Josh joined us for our traditional post-meetup drinks, where we were able to continue pitching our ideas, as well as shooting the breeze about Apex, Force.com and Salesforce in particular.

The session was recorded by the good folks at SkillsMatter and is available to watch here.

If you aren't already a member of the Salesforce London Developers Meetup group, you can join via the meetup site.

Second Introduction to Force.com Workshop

After the success of the first Introduction to Force.com workshop in April, we ran another one of these on 25th June, led by myself with hosting and assistance from John Stevenson (@jr0cket), one of the Salesforce Developer Evangelists here in the UK.

The format was broadly the same as before - get a group of people keen to learn about Force.com together at Salesforce offices and go through the Force.com workbook as a group, with hopefully useful tips and advice from the hosts as the evening progresses.  April Nassi, Developer Program Manager at Salesforce, sent us through a pack of hardcopy workbooks and cheat sheets which were a useful addition - not only does it give the attendees something to take away, it meant that those who were struggling a little could catch up at their own pace by following the instructions in the book when we took a break at the end of each chapter. April also sent through some schwag so some of the attendees went away with new Force.com wear.

We'll be running more of these workshops in the future, so keep an eye on the meetup group for the next one - if you'd like to help out or host one, let us know.

 

Thursday, 30 May 2013

Book Reviewers Wanted

NewImage

Earlier this year I acted as a Technical Reviewer for the CRM Admin Cookbook.  This means its a little tricky for me to review in this blog, as I obviously ensured everything was perfect and there was no way to improve it :)

Packt Publishing are offering free copies of Salesforce CRM Admin Cookbook : http://www.packtpub.com/salesforce-crm-admin-cookbook/book in exchange for a review either on your blog or on the title’s Amazon page.

The book covers the following areas:

  • Building home page components and creating custom links to provide additional functionality and improve the Home Tab layout
  • Advanced user interface techniques to improve the look and feel of Salesforce CRM with the presentation of graphical elements
  • Exposing hacks and hidden features to enhance and override native aspects of Salesforce CRM
  • Automatic data capture and improving data quality in Salesforce CRM
  • Implementing an approval process to control the way approvals are managed for records in Salesforce CRM
  • Increasing productivity using tools and features to provide advanced administration
  • Configuring and installing Salesforce for Microsoft Outlook email integration
  • Integrating Salesforce CRM with external online tools to provide enhanced functionality and extend the power of Salesforce CRM 

If you’re a Salesforce CRM user or interested in getting to grips with it, this is a good way to bag yourself a free guide (current retail price £28).

Free review copies are available until Monday 5th June 2013

If you’re interested, email Harleen Kaur Bagga at: harleenb@packtpub.com

 

Sunday, 26 May 2013

Mobile Web Apps with HTML5 and JQuery Mobile

At the Salesforce Customer Company Tour in London on 2nd May I presented a session in the Developer Theatre on Building Mobile Web Apps with HTML5 and JQuery Mobile, using a Todo list application as the example.

Yes, We've Got a Video

The session wasn't recorded, but since then I've recorded a replay of it which is available on youtube:

Get the Source

I've made this application available as an unmanaged package at: https://login.salesforce.com/packaging/installPackage.apexp?p0=04ti0000000VGqW

The main contents of this package are:

  • Custom Todo app
  • Custom objects for Group and Task, with tabs
  • Visualforce TodoJQM page and tab, also called TodoJQM
  • A few static resources for JQuery Mobile
  • Visualforce components for the custom CSS and JavaScript for the Todo pages - obviously in a  production system these would be in static resources and the JavaScript would be minified.  However, in this case I've put them into custom components so that they can be viewed without downloading and easily changed.

Feel free to post any questions you might have in the comments section below.