Writing Flash Android Clients for Salesforce.com

August 4th, 2010

Now that Adobe Flash Builder for Force.com has been released we are starting to see some of the fruits from this joint venture. Salesforce.com released a beta version of the Force.com Explorer on Adobe Air a couple of days ago and I caught an interesting tweet from Jeff Grosse about a new video on the Salesforce Channel. James Ward developed a Flash app with the tool that pulls down accounts and contacts from Salesforce.com and allows you to update records. This might be the preferred method of development for Android devices since:

  1. Android now runs Flash 10.1
  2. Android does not support Axis Web services natively (i.e., no Partner API love)
  3. There is no secure REST API for Force.com

So now you can write Android apps with Adobe Flash Builder for Force.com that uses the same Web services API that you are familiar with.

The video is pretty slick and the source code is available if you would like to start hacking up your own implementation.

VN:F [1.9.3_1094]
Rating: 0.0/10 (0 votes cast)
VN:F [1.9.3_1094]
Rating: 0 (from 0 votes)

Categories: Android, Salesforce

1 Comment

Appirio Marketing has too much Free Time

August 3rd, 2010


At Appirio we routinely hold customer-exclusive webinars to talk about features, trends and the latest technologies in cloud computing. This week I’m hosting a webinar to some of our existing Salesforce.com customers on the new features released in Summer ’10.

This is how the marketing team “effectively” markets the event.


BTW since someone asked… jeffdonthemic is in homage to “Mike D” from the Beastie Boys. I picked it up in the late 90′s and it’s an easy login that’s always available. I tend to fight with the other Jeff Douglas for logins.

VN:F [1.9.3_1094]
Rating: 9.5/10 (2 votes cast)
VN:F [1.9.3_1094]
Rating: +1 (from 1 vote)

Categories: Appirio, Salesforce

4 Comments

Google Chrome Extension: Salesforce.com ID Converter

August 1st, 2010

The Saleforce UI displays 15 digit, case-sensitive IDs for records. However, for Windows machines you will need to use a case-safe version of the ID for data migration, API calls and such. It’s not hard to generate these 18 digits IDs but it does take some time. I’ve been working on a lot of Google Gadgets and Chrome extensions lately so I thought I would throw together a Chrome extension (sorry Firefox users!) to quickly generated an 18 digit Salesforce.com ID. Thanks to Ron Hess for the conversion script!

Building Chrome extensions is fairly simple with a little bit of HTML and Javascript. The Google Code site has some sample “Hello World” code as well as an extension video to get you going. I’ve already started thinking about my next extension with a bunch of Force.com utilities that should make development life easier. I’ll be glad to share.

You can download the extension here.

Please let me know if you have any issues, bugs or feedback.

Chrome Extension

VN:F [1.9.3_1094]
Rating: 10.0/10 (1 vote cast)
VN:F [1.9.3_1094]
Rating: 0 (from 0 votes)

Categories: Google, Salesforce

13 Comments

Force.com Explorer (beta) for Adobe Air Available

July 29th, 2010

A copy of the new Force.com Developer News arrived in my inbox this morning with some interesting news. An Adobe Air (beta) of the Force.com Explorer has been released for people to begin playing with. If you missed the newsletter, you can catch Jon Mountjoy's blog post with the announcement. Jon writes more eloquently than me due to his enchanting accent. You should give the app a whirl as it’s off to a good start. You can download it here.

I was quite excited to see how the Adobe Flash Builder for Force.com was coming along since I developed a small app with the toolkit last January. I took a peek at the docs and they’ve made some really nice progress. However, the main reason I immediately downloaded the Force.com Explorer was to test out the functionality in hopes of a better tool. If you are a Mac user than you are blessed with the options of running Simon Fell’s SoqlXplorer in addition to the Force.com IDE for Eclipse. Neither one is quite the perfect tool. SoqlXplorer doesn’t let you drill into objects to build complex relationship queries like Eclipse but the Eclipse IDE doesn’t allow you to copy the resulting data from queries into your clipboard. I typically live in the SoqlXplorer all day. (Side note: not being able to copy results in the Eclipse IDE is frustrating but Job Plax, the Force.com IDE PM, told me that this feature just missed the last release so hopefully it will be in a future release.)

The Force.com Explorer is a breeze to install if you already have Adobe Air and you can be up and running within seconds. It has a nice clean UI which looks a lot like the .NET Apex Explorer. It has most of the functionality that you find in the other SOQL query tool plus some:

  1. Filter objects by name
  2. Drill down into objects and relationships to create complex queries. Very similar in functionality to the Eclipse IDE
  3. Return results with child object that you can click on to drill into
  4. Copy the text from cells in the query results
  5. View metadata about the objects
  6. Query, query all and batch size options
  7. Right click on an object to view it of its metadata and save it as a PDF
  8. It remembers the fields you selected in previous objects to easily rebuild SOQL queries

Some issues that I found right away (BTW… email any issues or requests to developerforce@salesforce.com):

  1. I was not able to log into any sandbox orgs
  2. I had a problem printing the object report but could download it as a PDF with no problems.
  3. It crashed a couple of times while trying to create complex queries
  4. It was not able to construct queries with parent-to-child relationships and if I did manually enter them the results came back null.

View Child Records Inline

force-explorer4.png

View Metadata about an Object

VN:F [1.9.3_1094]
Rating: 9.0/10 (1 vote cast)
VN:F [1.9.3_1094]
Rating: +1 (from 1 vote)

Categories: Salesforce

5 Comments

Using the Salesforce.com OAuth Playground

July 28th, 2010

My article on developer.force.com, Using OAuth to Authorize External Applications, shows how to develop external Java applications that use OAuth to access your Saleforce.com data. But what if you want to do the opposite? What if you need to write an application on the Force.com platform that uses your data from Google, Twitter, LinkedIn or TripIt using OAuth? Luckily Jesper Jorgenson at Salesforce.com posted an open source project called sfdc-oauth-playground which is a generic consumer implementation of OAuth as a Force.com App.

The main purpose of this beta project is to show you how to write OAuth signed requests in Apex. There is a managed packaged you can install into your org but if you really want to dig into the guts of OAuth (and who doesn’t??) you’ll need to download and install the source code from the project. The managed package doesn’t afford you to opportunity to modify code or view granular debug statements.

Jesper doesn’t provide much documentation for the project so I’ve put together a short video showing how to get started using Google Accounts and Blogger. Unfortunately different providers implement OAuth to their own liking so you sometimes have to make modifications to the requests being sent over the wire. I ran into a number of issues so hopefully this will assist you in getting up and running with OAuth.

 

In case you don’t have time for the video, here are a couple of screenshots outlining the process using the OAuth Consumer Playground application from the managed package.

Create a new OAuth Service (e.g., Blogger) with various URLs for accessing services

oauth-service-unauthorized.png

 

Authorize Salesforce access to Blogger

oauth-service-preauth.png

 

Grant access to Blogger

oauth-service-blogger.png

 

Authorization confirmation

oauth-service-auth.png

 

OAuth Service access tokens

oauth-service-authorized.png

 

Testing the service

oauth-service-test.png

VN:F [1.9.3_1094]
Rating: 0.0/10 (0 votes cast)
VN:F [1.9.3_1094]
Rating: 0 (from 0 votes)

Categories: Apex, Salesforce

No Comments

Using OAuth to Authorize External Applications

July 23rd, 2010

My article on OAuth went up yesterday on developer.force.com (thanks Jon!). It seems like OAuth is gaining adoption so I hope the article comes in handy for people developing applications for Salesforce.com. I’m currently working on another article for the Force.com Web Services Connector (WSC).


“Force.com lets you build external web applications that access data on the Force.com platform using the Oauth 1.0a protocol. OAuth is an open protocol that allows a website to access resources of another website without having to expose a user’s credentials. Instead of supplying a username and password, OAuth allows users to hand out security tokens to specific sites for access to specific resources for a defined duration.

In this article we’ll explain what OAuth is and why you should use it. We’ll also configure a Remote Access Application in Force.com, and develop an application on Google App Engine that uses a Remote Access Application to authorize access to Force.com to display account and contact records. Finally, we’ll look at some tips, tricks, available libraries and best practices to get you up and running in no time.”

Read the entires article at developer.force.com

All of the code for the application is available at the Google Code project: sfdc-oauth-demo


VN:F [1.9.3_1094]
Rating: 1.0/10 (1 vote cast)
VN:F [1.9.3_1094]
Rating: 0 (from 0 votes)

Categories: Salesforce

2 Comments

Email a Document with Salesforce.com

July 22nd, 2010

After my last post, Create and Email a PDF with Salesforce.com, I received a few comments whether it was possible to do the same with Document stored in Salesforce.com. Could you choose a Document and then send it via email as an attachment? The short answer is yes! However, I tried to do it from a Force.com Sites page but was not able to get it to work. The document is not marked for internal use and it is marked as an externally available image. I also made sure that the public settings for my Site included read access to documents but still the query for the document returns no results. No time to look at it in depth right now so if anyone has an idea, please send it my way.

DocumentEmailer Visualforce Page

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<apex:page controller="DocumentEmailController">
  <apex:sectionHeader title="Document Example" subtitle="Email a Document" 
    description="Example of how to email a Document."/>
 
  <apex:form >
    <apex:pageMessages />
    <apex:pageBlock title="Document Input">
 
      <apex:pageBlockButtons >
        <apex:commandButton action="{!sendDoc}" value="Send Document"/>
      </apex:pageBlockButtons>
 
      <apex:pageBlockSection >
 
        <apex:pageBlockSectionItem >
            <apex:outputLabel value="Email to send to" for="email"/>
          <apex:inputText value="{!email}" id="email"/>
        </apex:pageBlockSectionItem>
 
        <apex:pageBlockSectionItem >
            <apex:outputLabel value="Document" for="document"/>
            <apex:selectList value="{!documentId}" id="document" size="1">
                 <apex:selectOptions value="{!documents}"/>
            </apex:selectList>
        </apex:pageBlockSectionItem>
 
      </apex:pageBlockSection>
 
    </apex:pageBlock>
  </apex:form>
 
</apex:page>

DocumentEmailController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public with sharing class DocumentEmailController {
 
  public ID documentId {get;set;}
  public String email {get;set;}
 
  public List<SelectOption> documents {
    get {
      if (documents == null) {
        documents = new List<SelectOption>();
        documents.add(new SelectOption('01570000001NZDn','Cup of Coffee? - DOC'));
        documents.add(new SelectOption('01570000001NZDi','Workflow Cheatsheet -  PDF'));
      }
      return documents;
    }
    set;
  }
 
  public PageReference sendDoc() {
 
    Document doc = [select id, name, body, contenttype, developername, type 
      from Document where id = :documentId];
 
    Messaging.EmailFileAttachment attach = new Messaging.EmailFileAttachment();
    attach.setContentType(doc.contentType);
    attach.setFileName(doc.developerName+'.'+doc.type);
    attach.setInline(false);
    attach.Body = doc.Body;
 
    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
    mail.setUseSignature(false);
    mail.setToAddresses(new String[] { email });
    mail.setSubject('Document Email Demo');
    mail.setHtmlBody('Here is the email you requested: '+doc.name);
    mail.setFileAttachments(new Messaging.EmailFileAttachment[] { attach }); 
 
    // Send the email
    Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
 
    ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.INFO, 'Email with Document sent to '+email));
 
    return null;
 
  }
 
}

Test Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@isTest
private class Test_DocumentEmailer {
 
  static Document document;
 
  static {
 
    document = new Document();
    document.Body = Blob.valueOf('Some Text');
    document.ContentType = 'application/pdf';
    document.DeveloperName = 'my_document';
    document.IsPublic = true;
    document.Name = 'My Document';
    document.FolderId = [select id from folder where name = 'My Test Docs'].id;
    insert document;
 
  }
 
  static testMethod void testDocumentEmailer() {
 
    PageReference pref = Page.DocumentEmailer;        
    DocumentEmailController con = new DocumentEmailController();    
 
    Test.startTest();
 
    System.assertEquals(2,con.documents.size());
 
    // populate the field with values
    con.documentId = document.id;
    con.email = 'test@noemail.com';
    // submit the request
    pref = con.sendDoc();
 
    Test.stopTest(); 
 
  }
}


VN:F [1.9.3_1094]
Rating: 9.0/10 (1 vote cast)
VN:F [1.9.3_1094]
Rating: +1 (from 1 vote)

Categories: Apex, Code Sample, Salesforce, Visualforce

5 Comments

Create and Email a PDF with Salesforce.com

July 16th, 2010

This is a continuation of my post a couple of days ago, Attach a PDF to a Record in Salesforce, and shows how to dynamically generate a PDF and attach it to an email. The code is fairly similar and has the same issue with testing the PageReference getContent() method.

You can run this demo at my developer site.

PdfEmailer Visualforce Page

The Visualforce page simply allows the user to enter their email address and select a sample Account from the picklist. This is Account that is passed to the PdfGeneratorTemplate Visualforce page to generate the PDF.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<apex:page controller="PdfEmailController">
  <apex:sectionHeader title="PDF Example" subtitle="Email a PDF" 
    description="Example of how to email a dynamically generated PDF."/>
 
  <apex:form >
    <apex:pageMessages />
    <apex:pageBlock title="PDF Input">
 
      <apex:pageBlockButtons >
        <apex:commandButton action="{!sendPdf}" value="Send PDF"/>
      </apex:pageBlockButtons>
 
      <apex:pageBlockSection >
 
        <apex:pageBlockSectionItem >
            <apex:outputLabel value="Email to send to" for="email"/>
          <apex:inputText value="{!email}" id="email"/>
        </apex:pageBlockSectionItem>
 
        <apex:pageBlockSectionItem >
            <apex:outputLabel value="Account" for="account"/>
            <apex:selectList value="{!accountId}" id="account" size="1">
                 <apex:selectOptions value="{!accounts}"/>
            </apex:selectList>
        </apex:pageBlockSectionItem>
 
      </apex:pageBlockSection>
 
    </apex:pageBlock>
  </apex:form>
 
</apex:page>

PdfEmailerController

The Controller passes the Account ID that the user entered as a parameter for the Visualforce page being generated. It let creates the attachment from the PDF content. The Body of the attachment uses the Blob returned from the PageReference’s getContent method. You could also use the getContentAsPDF method which always returns the page as a PDF, regardless of the component’s renderAs attribute. However, this method always seems to throw an error in the test class. See the PageReference documentation for more info. The method then constructs the SimpleEmailMessage object and then sends it on its way to the recipient’s inbox.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public with sharing class PdfEmailController {
 
  public ID accountId {get;set;}
  public String email {get;set;}
 
  public List<SelectOption> accounts {
    get {
      if (accounts == null) {
        accounts = new List<SelectOption>();
        accounts.add(new SelectOption('0017000000LgRMb','United Oil & Gas Corp.'));
        accounts.add(new SelectOption('0017000000LgRMV','Burlington Textiles Corp of America'));
      }
      return accounts;
    }
    set;
  }
 
  public PageReference sendPdf() {
 
    PageReference pdf = Page.PdfGeneratorTemplate;
    // add parent id to the parameters for standardcontroller
    pdf.getParameters().put('id',accountId);
 
    // the contents of the attachment from the pdf
    Blob body;
 
    try {
 
      // returns the output of the page as a PDF
      body = pdf.getContent();
 
    // need to pass unit test -- current bug  
    } catch (VisualforceException e) {
      body = Blob.valueOf('Some Text');
    }
 
    Messaging.EmailFileAttachment attach = new Messaging.EmailFileAttachment();
    attach.setContentType('application/pdf');
    attach.setFileName('testPdf.pdf');
    attach.setInline(false);
    attach.Body = body;
 
    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
    mail.setUseSignature(false);
    mail.setToAddresses(new String[] { email });
    mail.setSubject('PDF Email Demo');
    mail.setHtmlBody('Here is the email you requested! Check the attachment!');
    mail.setFileAttachments(new Messaging.EmailFileAttachment[] { attach }); 
 
    // Send the email
    Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
 
    ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.INFO, 'Email with PDF sent to '+email));
 
    return null;
 
  }
 
}

PdfGeneratorTemplate Visualforce Page

This is the Visualforce page that is generated in the Controller. It simply uses the StandardController and displays the Account name for the ID passed to it.

1
2
3
4
<apex:page standardController="Account" renderAs="pdf">
  <h1>Congratulations!!</h1>
  <p>You created a PDF for {!account.name}</p>
</apex:page>

Test Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@isTest
private class Test_PdfEmailController {
 
  static Account account;
 
  static {
 
    account = new Account();
    account.Name = 'Test Account';
    insert account;
 
  }
 
  static testMethod void testPdfEmailer() {
 
    PageReference pref = Page.PdfEmailer;
    pref.getParameters().put('id',account.id);
    Test.setCurrentPage(pref);
 
    PdfEmailController con = new PdfEmailController();    
 
    Test.startTest();
 
    System.assertEquals(2,con.accounts.size());
 
    // populate the field with values
    con.accountId = account.id;
    con.email = 'test@noemail.com';
    // submit the record
    pref = con.sendPdf();
 
    Test.stopTest(); 
 
  }
}

VN:F [1.9.3_1094]
Rating: 0.0/10 (0 votes cast)
VN:F [1.9.3_1094]
Rating: 0 (from 0 votes)

Categories: Apex, Code Sample, Salesforce, Visualforce

5 Comments

Attach a PDF to a Record in Salesforce

July 14th, 2010

Salesforce.com makes it extremely easy to generate PDF documents on the fly by simply using the renderAs=”pdf” attribute for the <apex:page> component. It’s also a snap to attach these PDFs to records as Attachments. Below is a small Visualforce page and Controller that generates a PDF and saves it to an Account.

Note: there is a small issue when it comes to testing the Controller. Salesforce currently throws an error (Salesforce.com Error “Unable to retrieve object”) when getContent or getContentAsPDF is called from a test method. There’s an Idea to make this work properly. I encourage everyone to vote for this.

PdfGenerator Visualforce Page

The Visualforce page allows the users to enter the ID of the Account to attach the PDF to as well as the name of the PDF. You could just have easily used a Controller Extension instead of entering the ID for the Account but I went this route for simplicity.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<apex:page controller="PdfGeneratorController">
  <apex:sectionHeader title="PDF Example" subtitle="Attach a PDF" 
    description="Example of how to attach a PDF to a record."/>
 
  <apex:form >
    <apex:pageBlock title="PDF Input">
 
      <apex:pageBlockButtons >
        <apex:commandButton action="{!savePdf}" value="Save PDF"/>
      </apex:pageBlockButtons>
      <apex:pageMessages />
 
      <apex:pageBlockSection >
 
        <apex:pageBlockSectionItem >
            <apex:outputLabel value="File Name" for="pdfName"/>
          <apex:inputText value="{!pdfName}" id="pdfName"/>
        </apex:pageBlockSectionItem>
 
        <apex:pageBlockSectionItem >
            <apex:outputLabel value="Account ID" for="id"/>
          <apex:inputText value="{!parentId}" id="id"/>
        </apex:pageBlockSectionItem>
 
      </apex:pageBlockSection>
 
    </apex:pageBlock>
  </apex:form>
 
</apex:page>

PdfGeneratorController Custom Controller

The Controller passes the Account ID that the user entered as a parameter for the Visualforce page being generated. It then creates a new Attachment object and sets some attributes. It sets the ParentId to the value of the Account ID that the user entered so that the PDF is attached to that record. The Body of the attachment uses the Blob returned from the PageReference’s getContent method. You could also use the getContentAsPDF method which always returns the page as a PDF, regardless of the <apex:page> component’s renderAs attribute. However, this method always seems to throw an error in the test class. See the PageReference documentation for more info. The method then redirects the user to the Account page so they can view the PDF attachment.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public with sharing class PdfGeneratorController {
 
  public ID parentId {get;set;}
  public String pdfName {get;set;}
 
  public PageReference savePdf() {
 
    PageReference pdf = Page.PdfGeneratorTemplate;
    // add parent id to the parameters for standardcontroller
    pdf.getParameters().put('id',parentId);
 
    // create the new attachment
    Attachment attach = new Attachment();
 
    // the contents of the attachment from the pdf
    Blob body;
 
    try {
 
        // returns the output of the page as a PDF
    	body = pdf.getContent();
 
    // need to pass unit test -- current bug	
    } catch (VisualforceException e) {
    	body = Blob.valueOf('Some Text');
    }
 
    attach.Body = body;
    // add the user entered name
    attach.Name = pdfName;
    attach.IsPrivate = false;
    // attach the pdf to the account
    attach.ParentId = parentId;
    insert attach;
 
    // send the user to the account to view results
    return new PageReference('/'+parentId);
 
  }
 
}

PdfGeneratorTemplate Visualforce Page

This is the Visualforce page that is generated in the Controller. It simply uses the StandardController and displays the Account name for the ID passed to it.

1
2
3
4
<apex:page standardController="Account" renderAs="pdf">
  <h1>Congratulations!!</h1>
  <p>You created a PDF for {!account.name}</p>
</apex:page>

Test Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@isTest
private class Test_PdfGeneratorController {
 
  static Account account;
 
  static {
 
    account = new Account();
    account.Name = 'Test Account';
    insert account;
 
  }
 
  static testMethod void testPdfGenerator() {
 
    PageReference pref = Page.PdfGenerator;
    pref.getParameters().put('id',account.id);
    Test.setCurrentPage(pref);
 
    PdfGeneratorController con = new PdfGeneratorController();    
 
    Test.startTest();
 
    // populate the field with values
    con.parentId = account.id;
    con.pdfName = 'My Test PDF';
 
    // submit the record
    pref = con.savePdf();
 
    // assert that they were sent to the correct page
    System.assertEquals(pref.getUrl(),'/'+account.id);
 
    // assert that an attachment exists for the record
    System.assertEquals(1,[select count() from attachment where parentId = :account.id]);
 
    Test.stopTest(); 
 
  }
}

VN:F [1.9.3_1094]
Rating: 5.3/10 (3 votes cast)
VN:F [1.9.3_1094]
Rating: +3 (from 3 votes)

Categories: Apex, Code Sample, Salesforce, Visualforce

6 Comments

Building a Dynamic Search Page in Visualforce

July 13th, 2010

I brushed this code off and thought it might be useful to someone as a starting point for a dynamic search page. It has some cool functionality including passing search criteria via Javascript to the controller, search as you type, sorting of results by clicking on the column header plus much more. Hope you find it useful.

You can run this demo at my developer site.

Some of the interesting features of the Visualforce page and Apex controller include:

  • Instead of the typical overhead of using getters/setters in the controller to maintain the variables for the search form, I used Dave Carroll’s blog as an example to pass the variables via Javascript.
  • Since I’m using Javascript to pass my parameters, I can get fancy and make the search form more dynamic by running the search as the user types or selects an option in the picklist. No submit button is required.
  • Users can click on the column headers to toggle the direction of the search results by using a CommandLink component and passing the column name.
  • Build the picklist using Dynamic Apex’s Describe functionality so that it’s maintenance free.
  • Perform the search against multiple fields including multi-select picklists using dynamic SOQL and preventing SOQL injection attacks.

ContactSearchController

Take a look at the Apex code below. The search interaction is split up between two separate but integral parts. When the search is fired from the Visualforce page via Javascript, the SOQL is constructed in the runSearch() method and is then passed to the runQuery() method to execute. The SOQL string is persisted so that when the user clicks on the table column the same SOQL can be issued again in the toggleSort() method but ordered by a different field name and sort direction.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
public with sharing class ContactSearchController {
 
  // the soql without the order and limit
  private String soql {get;set;}
  // the collection of contacts to display
  public List<Contact> contacts {get;set;}
 
  // the current sort direction. defaults to asc
  public String sortDir {
    get  { if (sortDir == null) {  sortDir = 'asc'; } return sortDir;  }
    set;
  }
 
  // the current field to sort by. defaults to last name
  public String sortField {
    get  { if (sortField == null) {sortField = 'lastName'; } return sortField;  }
    set;
  }
 
  // format the soql for display on the visualforce page
  public String debugSoql {
    get { return soql + ' order by ' + sortField + ' ' + sortDir + ' limit 20'; }
    set;
  }
 
  // init the controller and display some sample data when the page loads
  public ContactSearchController() {
    soql = 'select firstname, lastname, account.name, interested_technologies__c from contact where account.name != null';
    runQuery();
  }
 
  // toggles the sorting of query from asc<-->desc
  public void toggleSort() {
    // simply toggle the direction
    sortDir = sortDir.equals('asc') ? 'desc' : 'asc';
    // run the query again
    runQuery();
  }
 
  // runs the actual query
  public void runQuery() {
 
    try {
      contacts = Database.query(soql + ' order by ' + sortField + ' ' + sortDir + ' limit 20');
    } catch (Exception e) {
      ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Ooops!'));
    }
 
  }
 
  // runs the search with parameters passed via Javascript
  public PageReference runSearch() {
 
    String firstName = Apexpages.currentPage().getParameters().get('firstname');
    String lastName = Apexpages.currentPage().getParameters().get('lastname');
    String accountName = Apexpages.currentPage().getParameters().get('accountName');
    String technology = Apexpages.currentPage().getParameters().get('technology');
 
    soql = 'select firstname, lastname, account.name, interested_technologies__c from contact where account.name != null';
    if (!firstName.equals(''))
      soql += ' and firstname LIKE \''+String.escapeSingleQuotes(firstName)+'%\'';
    if (!lastName.equals(''))
      soql += ' and lastname LIKE \''+String.escapeSingleQuotes(lastName)+'%\'';
    if (!accountName.equals(''))
      soql += ' and account.name LIKE \''+String.escapeSingleQuotes(accountName)+'%\'';  
    if (!technology.equals(''))
      soql += ' and interested_technologies__c includes (\''+technology+'\')';
 
    // run the query again
    runQuery();
 
    return null;
  }
 
  // use apex describe to build the picklist values
  public List<String> technologies {
    get {
      if (technologies == null) {
 
        technologies = new List<String>();
        Schema.DescribeFieldResult field = Contact.interested_technologies__c.getDescribe();
 
        for (Schema.PicklistEntry f : field.getPicklistValues())
          technologies.add(f.getLabel());
 
      }
      return technologies;          
    }
    set;
  }
 
}

CustomerSearch Visualforce Page

The Visualforce page has three sections: 1) a search form, 2) a results blockTable and 3) a debug panel displaying the SOQL that was executed.

The search form has a number of fields that fire the Javascript search using the onkeyup and onchange events. Instead of each form field passing the current values to actionFunction’s Javascript method, for ease of use, each form field calls the doSearch() function that gathers up the values and submits them. When the actionFunction renders it creates a Javascript function that POSTs the values to the controller in the same manner as CommandLink or CommandButton.

The search results BlockTable is rerendered when the search is submitted to the controller so that the results display properly. Users can click on the column headers to reorder the results of the query. This uses a CommandLink to pass the sort field to the controller and toggle the sort direction.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
<apex:page controller="ContactSearchController" sidebar="false">
 
  <apex:form >
  <apex:pageMessages id="errors" />
 
  <apex:pageBlock title="Find Me A Customer!" mode="edit">
 
  <table width="100%" border="0">
  <tr>  
    <td width="200" valign="top">
 
      <apex:pageBlock title="Parameters" mode="edit" id="criteria">
 
      <script type="text/javascript">
      function doSearch() {
        searchServer(
          document.getElementById("firstName").value,
          document.getElementById("lastName").value,
          document.getElementById("accountName").value,
          document.getElementById("technology").options[document.getElementById("technology").selectedIndex].value
          );
      }
      </script> 
 
      <apex:actionFunction name="searchServer" action="{!runSearch}" rerender="results,debug,errors">
          <apex:param name="firstName" value="" />
          <apex:param name="lastName" value="" />
          <apex:param name="accountName" value="" />
          <apex:param name="technology" value="" />
      </apex:actionFunction>
 
      <table cellpadding="2" cellspacing="2">
      <tr>
        <td style="font-weight:bold;">First Name<br/>
        <input type="text" id="firstName" onkeyup="doSearch();"/>
        </td>
      </tr>
      <tr>
        <td style="font-weight:bold;">Last Name<br/>
        <input type="text" id="lastName" onkeyup="doSearch();"/>
        </td>
      </tr>
      <tr>
        <td style="font-weight:bold;">Account<br/>
        <input type="text" id="accountName" onkeyup="doSearch();"/>
        </td>
      </tr>
      <tr>
        <td style="font-weight:bold;">Interested Technologies<br/>
          <select id="technology" onchange="doSearch();">
            <option value=""></option>
            <apex:repeat value="{!technologies}" var="tech">
              <option value="{!tech}">{!tech}</option>
            </apex:repeat>
          </select>
        </td>
      </tr>
      </table>
 
      </apex:pageBlock>
 
    </td>
    <td valign="top">
 
    <apex:pageBlock mode="edit" id="results">
 
        <apex:pageBlockTable value="{!contacts}" var="contact">
 
            <apex:column >
                <apex:facet name="header">
                    <apex:commandLink value="First Name" action="{!toggleSort}" rerender="results,debug">
                        <apex:param name="sortField" value="firstName" assignTo="{!sortField}"/>
                    </apex:commandLink>
                </apex:facet>
                <apex:outputField value="{!contact.firstName}"/>
            </apex:column>
 
            <apex:column >
                <apex:facet name="header">
                    <apex:commandLink value="Last Name" action="{!toggleSort}" rerender="results,debug">
                        <apex:param name="sortField" value="lastName" assignTo="{!sortField}"/>
                    </apex:commandLink>
                </apex:facet>
                <apex:outputField value="{!contact.lastName}"/>
            </apex:column>
 
            <apex:column >
                <apex:facet name="header">
                    <apex:commandLink value="Account" action="{!toggleSort}" rerender="results,debug">
                        <apex:param name="sortField" value="account.name" assignTo="{!sortField}"/>
                    </apex:commandLink>
                </apex:facet>
                <apex:outputField value="{!contact.account.name}"/>
            </apex:column>
 
            <apex:column >
                <apex:facet name="header">
                    <apex:commandLink value="Technologies" action="{!toggleSort}" rerender="results,debug">
                        <apex:param name="sortField" value="interested_technologies__c" assignTo="{!sortField}"/>
                    </apex:commandLink>
                </apex:facet>
                <apex:outputField value="{!contact.Interested_Technologies__c}"/>
            </apex:column>
 
        </apex:pageBlockTable>
 
    </apex:pageBlock>
 
    </td>
  </tr>
  </table>
 
  <apex:pageBlock title="Debug - SOQL" id="debug">
      <apex:outputText value="{!debugSoql}" />           
  </apex:pageBlock>    
 
  </apex:pageBlock>
 
  </apex:form>
 
</apex:page>
VN:F [1.9.3_1094]
Rating: 8.8/10 (9 votes cast)
VN:F [1.9.3_1094]
Rating: +7 (from 7 votes)

Categories: Apex, Code Sample, Salesforce, Visualforce

14 Comments

Feed

http://blog.jeffdouglas.com /

WordPress Appliance - Powered by TurnKey Linux