The Silver Lining

Lessons & Learnings from a salesforce certified technical architect.

Flickr + jQuery + Salesforce = Awesome^5 [Part 2]

with 4 comments

Okay software developing enthusiasts, I’m back from Paris (you didn’t know I was gone did ya?), I’m a year older and culturally, I’m richer (well I’d like to think so at least). It’s time to complete our two-part series on integrating disparate systems using  the most-excellent combination of web services, jQuery and the Force.com platform. In part 1 we learnt how to connect to a third-party endpoint (Flickr in our case), and consume their SOAP-based web services. Now we’re going to jazz it all up with our spiffy jQuery gallery carousel. Let’s have another look at where we want to be at the end of this all,

GalleryView 2.0 Integration
First up we’re going to need to do a few things, let’s make a nice little list,

  • We sign up for a Flickr Account(If you have a Yahoo account I think you just need to sign in).
  • We ask Flickr real nice like for an API key.
  • We create a Remote Site setting for the API. This can be done by clicking through Setup > Security Controls (Under Administration Setup) > Remote Site Settings. Create a new record called ‘Flickr’ with URL of http://api.flickr.com.
  • You might want to grab a copy of the code covered in this, and the previous post. You can get it all from here.

Tools in hand we begin our journey.. hi-ho hi-ho it’s off to work we go. First up, we need to have a class type that encompasses the concept of an image. We then have the option of extending this class for any specific requirements we might have. This base class might look something like this,


/** An image object to be used by the GalleryView component **/

public with sharing virtual class GalleryImage {

	public String id{get;set;}
    public String title{get;set;}
    public String owner{get;set;}
    public String path{get;set;}
public virtual String getOriginalUrl(){

    	return null;
    }
public virtual String getDisplayUrl(){

    	return null;
    }
}

Now we can use a list of this type within our component,


<apex:component >
	<apex:attribute name="images" type="GalleryImage[]" description="A list of images that you want to display" required="true"/>

We also need to include the jQuery libraries and plugins needed for the carousel,


    <apex:stylesheet value="{!URLFOR($Resource.galleryview,'jquery-galleryview-2.0/galleryview.css')}"/>

    <apex:includeScript value="{!$Resource.jquery}"/>

    <apex:includeScript value="{!URLFOR($Resource.galleryview,'jquery-galleryview-2.0/jquery.easing.1.3.js')}"/>

    <apex:includeScript value="{!URLFOR($Resource.galleryview,'jquery-galleryview-2.0/jquery.galleryview-2.0.js')}"/>

    <apex:includeScript value="{!URLFOR($Resource.galleryview,'jquery-galleryview-2.0/jquery.timers-1.1.2.js')}"/>

And we need to initialise the GalleryView library, as well as attach it to an HTML element, in this case a list with an ID of ‘gallery’,


    <script>
	    $(document).ready(function(){
	        $('#gallery').galleryView({

	            panel_width: {!panel_width},
	            panel_height: {!panel_height},
	            frame_width: {!frame_width},
	            frame_height: {!frame_height},
	            transition_speed: 350,
     		 	easing: 'easeInOutQuad',

                show_captions: {!show_captions},
                pause_on_hover: true,
                nav_theme: 'light',
                overlay_color: 'white',
                overlay_opacity: 0.5
	        });
	    });
	</script>

That done, the rest of the component is pretty straight forward. All we need is an unordered list, with list elements being the wrappers for the images, and any accompanied text.

	<!-- The unordered list of images and info that get's rendered as the carousel. -->
	<ul id="gallery">

	   <apex:repeat value="{!images}" var="image">
	       <li>
	       	<span class="panel-overlay">
	       		<h2>{!image.title}

	       		Photo by <span style="font-weight:bold">{!image.owner}</span>.  View full-size photo <a href="{!image.originalUrl}" target="_blank">here</a>.
	        </span>
	        <img src="{!image.displayUrl}" alt="{!image.title}" title="{!image.title}"/>

	       </li>
	   </apex:repeat>
	</ul>

And faster than you can say ‘Jiminy-Cricket!’, we’re done. All we need to do now is embed the component within a page and pass it a list of GalleryImage objects. For our example we’re going to use the Flickr web services we wrapped in the first post of this series, and we’re going to modify the Photo class from the Flickr project,

    public class Photo extends GalleryImage{
        public String id{get;set;}
        public String secret{get;set;}
        public String server{get;set;}
public Map<String,String> urls{get;set;}

        public Photo(){
            urls = new Map<String,String>();

        }
public String getSquareUrl(){

            return urls.get('url_sq');
        }
public String getTinyUrl(){

            return urls.get('url_t');
        }
public String getSmallUrl(){

            return urls.get('url_s');
        }
public String getMedUrl(){

            return urls.get('url_m');
        }
public String getLargeUrl(){

            return urls.get('url_l');
        }
public override String getOriginalUrl(){

            return urls.get('url_o');
        }
/** Overridden GalleryImage methods **/

        public override String getDisplayUrl(){

        	return getMedUrl();

        }
    }

What we’ve done here is extend the GalleryImage class when declaring the Flickr.Photo class. It could be argued that we should not be tampering with our library, but I would counter-argue that we’ve lost nothing by doing this, and have gained a smidgen of functionality. [In truth however, I would never do this sort of thing in Java class design, but there are fundamental Apex language restrictions (in this case passing parameters to object methods from a VisualForce page) that I've had to work around.]

And that, cool cats and hot dogs, is just about it. Let’s quickly throw together our demo controller,

public with sharing class GalleryviewDemoController {

private String wallpaperPsId = '72157605600725406';

    public String keywords{get;set;}
public Flickr fc{get;set;}

    public List<Flickr.Photo> photos{get;set;}
public GalleryviewDemoController(){

        fc = new Flickr();
keywords = ApexPages.currentPage().getParameters().get('keywords');

        if(keywords==null)
        	photos = fc.getPhotosByPhotoSet(wallpaperPsId);

        else
        	photos =  fc.photoSearch(keywords);
    }
public PageReference search(){

    	PageReference pr = System.Page.GalleryviewDemo;

    	pr.getParameters().put('keywords',keywords);
    	pr.setRedirect(true);
    	return pr;
    }
}

and demo page,

<apex:page controller="GalleryviewDemoController" showHeader="false" standardStylesheets="false">
<apex:form >

		      outputPanel layout="block" style="padding:20px">

		      	<!-- This is a nice little hack to get commandbuttons and commandlinks to submit forms when enter is pressed. -->

		      	<!-- It is only required on some Force.com servers. -->
		          <script>
		              function noenter(ev) {

		                  if (window.event  window.event.keyCode == 13 || ev.which == 13)
		                      { var ele=document.getElementById('{!$Component.searchbtn}');
		                        ele.click();
		                        return false;
		                      } else {
		                          return true; }
		              }
		          </script>
		          inputText value="{!keywords}"onkeypress="return noenter(event);"/>

               commandButton value="Search" action="{!search}" id="searchbtn"/>

           outputPanel>

       </apex:form>
outputPanel layout="block" id="results" style="padding-left:20px">

outputPanel rendered="{!NOT(ISNULL(photos))}">

GalleryView images="{!photos}" />

           </apex:outputPanel>
<apex:outputPanel rendered="{!ISNULL(photos)}">

<h2 style="color: white;">Oopsy Daisy. Flickr has timed out. Please try again.</h2>

           </apex:outputPanel>
       </apex:outputPanel>
</apex:page>

After all that hard work we’d definitely like to see our awesome^5 gallery in use, so we publish a public URL and let people start playing (I’ve noticed that a significant number of searches on the Flickr search demo used keywords relating to unclothed female body parts. Boys will be boys I guess).

Wandering through this post we’ve (hopefully)learnt how to abstract away the underlying technicalities of information we own (and man do we own A LOT of information these days) and present the end-user with a fun and sleek way to interact with said information. I’d like to postulate that this is the direction software development will take for the next few years.. goodbye information age, hello user-experience age (UXA for short. It’ll catch on, you’ll see).

About these ads

4 Responses

Subscribe to comments with RSS.

  1. Like your work. Thanks for sharing!

    Mike Leach

    November 14, 2009 at 9:25 pm

  2. Wes, just got a chance to really take a look at this and it is REALLY cool. Great work. I don’t think I’ve seen anyone using Flickr and jQuery together like this. I want to start working with more JavaScript with Visualforce and see what’s possible. Would love to do some GWT stuff but need to find the time.

    Jeff Douglas

    December 2, 2009 at 11:52 am

    • Why thank you:) I plan on digging heavily into GWT in the next few months.. I’ve played with GAE somewhat, and at the moment I’m helping someone get into ‘Cloud Computing’ (another kitty bites the dust) using the platform.. exciting stuff.

      Wes

      December 2, 2009 at 12:38 pm

  3. [...] will cover point 1 in this post, and then bring it all together in a second post which will cover point 2. It’s gonna be a rocky ride, but I’m sure all you [...]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 2,198 other followers

%d bloggers like this: