Subscribe

Appirio RSS Feed
Subscribe with Bloglines

Add to Google
Subscribe in NewsGator Online

Community

Appirio Technology Blog

Tuesday, September 23, 2008

Page breaks in Visualforce PDF templates

Kyle
Roche


The Visualforce page component defines a renderAs attribute that supports certain content converters. This is extremely useful when automatically printing forms, receipts, reports, etc. Often, we're asked to create nicely formatted forms that span multiple pages. If you leave the control of page breaks to the browser unexpected things can happen. This is an easy solution to solve with some basic CSS. You can use the page-break style properties to control where the browser will insert a page break. The Force.com PDF content converter will carry that over to the PDF.

Here's the basic code to demonstrate how this works. Create a new Visualforce Page called MultiPagePDF. Add the following code to the page:

<apex:page renderas="pdf">
<div style="page-break-after:always;">
This is page one
</div>
<div style="page-break-after:always;">
This should be rendered as page two
</div>
And, finally... page three
</apex:page>

9-14-2008 4-24-54 PM This should yield something like this illustration when rendered. Some natural extensions to this posting would be to dynamically insert these into the page. Inserting <div> tags and binding the style to an APEX property could be one way to accomplish this. You would pass back a blank style in some cases and return a string with the value of "page-break-after:always" for <div> sections where a break is needed.

As a quick side note, you can get a bit more advanced with the Page formatting via CSS. The following snippet shows you have to switch the page layout to landscape and add page numbers to your Visualforce page. This was found in the Case History Timeline example.

@page {
/* Landscape orientation */
size:landscape;

/* Put page numbers in the top right corner of each
page in the pdf document. */
@top-right {
content: "Page " counter(page);
}
}

Wednesday, September 17, 2008

Access Custom Fields on Standard Objects in Force.com IDE

Kyle Freeman

I recently created a new Force.com Project in Eclipse, but was surprised to find that there was not an option to include Standard Objects in the project manifest. Upon first glance it appears that only Custom Objects can be included.

9-14-2008 2-35-21 PM

This force.com thread by JonP discusses a workaround summarized here.

First, it is worthwhile to note that you cannot include standard fields on standard objects. However, you can include CUSTOM fields on standard objects. From your existing project in Eclipse, open the package.xml file located at src->unpackaged->package.xml Locate the Custom Object section and add a new member for each standard object you would like included in your project.

This method will include all custom fields on the standard object in your project.
For example, I was able to use the following snippet to include all custom fields on the Account, Contact, and Case objects, as well as all fields from all custom objects. Simply add the code to the package.xml file and then refresh the project from the server.

<types>

<members>*</members>

<members>Account</members>

<members>Case</members>

<members>Contact</members>

<name>CustomObject</name>

</types>

Care should be taken when pushing to production, however as including a standard object will also cause field-level security settings to be pushed to profiles in the salesforce.com production environment. Should your production field-level security settings be out of sync with your sandbox, you could run into some issues.

Utilizing this method can prove invaluable if you are needing to migrate a large number of custom fields and do not want to recreate these manually in production.

Monday, September 15, 2008

Complementing Visualforce - Girafa Thumbnail Service

Kyle
Roche


There are so many technologies out there to complement your Salesforce.com projects. In a previous posting I showed you how to leverage Google's Geocoding API from within your APEX classes. In this example, let's take a look at something more useful in the UI Layer.

It's pretty typical in projects with heavy Visualforce development to create a custom search form. Recently, I was working on a project where we created a custom search form for the Account object. Instead of showing the Account's logo image, we were thinking it would be a cool enhancement to display a thumbnail to the Account's website. You could use services like Amazon's Alexa Site Thumbnail Service (as shown in the APEX Language Reference). However, most of these aren't free. After some Googling, I stumbled on the Girafa Thumbnail Service, which offers a free service to those who use less than 2000 images requests per day (200 request per day at the time of this writing. Please check girafa for updated daily limits).

Let's get started. Create a Visualforce page called GirafaThumbnail and a controller called GirafaThumbnailController. In your controller add the following APEX Property.

public string GirafaUrl
{
get
{
if (GirafaUrl == null)
{
string algorithmName = 'MD5';
string signatureKey = 'your signature key here';
string clientId = 'your client id here';
string siteUrl = 'http://www.appirio.com/';
string inputString = signatureKey + siteUrl;
Blob myDigest = Crypto.generateDigest(algorithmName, Blob.valueOf(inputString));
string myDigestString = EncodingUtil.ConvertToHex(myDigest);
integer myStartingPosition = myDigestString.length() - 16;
integer myEndingPosition = myDigestString.length();
string mySubString = myDigestString.substring(myStartingPosition, myEndingPosition);
string myUrl = 'http://scst.srv.girafa.com/srv/i?i=' + clientId + '&s=' + mySubString + '&r=' + siteUrl;
return myUrl;
}
return GirafaUrl;
}
set;
}

Let's break this down by first understanding how girafa thumbnail service works. There are a few basic steps to generating the URL for your thumbnail. Girafa's thumbnail images are embedded using the <img> tag. The call is formatted as follows:

http://scst.srv.girafa.com/srv/i?i=<client ID>&s=<signature>&r=<site URL>

client ID is your Girafa Client ID. It is supplied when you register your account. The site URL is the URL of the website for which you would like to generate a Thumbnail. In our example http://www.appirio.com

The signature authenticates your request and is generates using the following steps:

1) Concatenate your secrete key (chosen when you create your account) and the Site URL

2) Calculate the MD5 Checksum of the concatenated string.

3) The signature is the 16 least significant hexadecimal digits of the MD5 checksum.

In our method we set the variables we'll need to generate the Image call then we use the Crypto class' generateDigest() method to calculate the MD5 checksum of our concatenated string. Some basic string manipulation returns the 16 character substring and we put our URL together to return to our caller.

In your Visualforce page add an image tag to see the results. Bind the src attribute of the <img> to our APEX Property. <img src="{!GirafaUrl}"></img>. You should see something along the lines of the following illustration.

9-14-2008 5-52-59 PM

Sunday, September 14, 2008

Escaping Quotes in Merge Fields

Glenn Weinstein

S-controls, button on-click Javascript, and Visualforce pages can contain merge fields, which SFDC replaces with their record values prior to rendering. But this is a straight replacement, without any opportunity to escape characters. This leads to messy results when a merge field contains a double-quote character.

For example, consider using this merge field in on-click Javascript:

project.Name = "{!Opportunity.Name}";

That would work great, but consider what happens if the opportunity is named Acme "World Peace" Project (with the double quotes). SFDC will render the code as:

project.Name = "Acme "World Peace" Project";

Okay, now you have a huge mess. This will break the Javascript. A very clever workaround for this was created by an Appirio colleague of mine, Linda Evans. Here it is:

Step 1: Wrap the value in a hidden textarea.
Step 2: Retrieve the value by traversing the DOM.

So in our example above, first, you'd drop this HTML element into your page:

<textarea id="opportunityName" style="display:none">{!Opportunity.Name}</textarea>

Then, inside a <script> element, you'd retrieve the value:

project.Name = document.getElementById("opportunityName").value;

I told you this was clever - thanks Linda!

One final note, this won't work (directly) in a button on-click Javascript, since you can't put HTML elements in them (the entire body is considered a <script>). So you'll have to move your code into an s-control, and then tie your button instead to the s-control.

Wednesday, August 27, 2008

Defaulting your mailto: links to Google Apps in Firefox 3

Tim Parker

If you're using Google Apps and Firefox and would like to default email links to open in Google Apps follow the steps below.

First, make sure you're using Firefox 3. Open Firefox, and enter about:config into the address bar. This will bring up a warning message, click on the "I'll be careful, I promise!" button.

Capture

First, we need to edit the option for web pages to register themselves as protocol handlers, so enter gecko.handler in the filter bar and select the option highlighted below.

gecko.handlerService.allowRegisterFromDifferenceHost

Capture2

Make sure this option is set to True (You can do so by double clicking that entry in the browser). This allows mailto: links to forward to web based email providers such as Google Apps. If you wanted to set webcal: links to a web based calendar like Google Calendar you would need this setting enabled as well.

Now that we've enabled this option we need to register Google Apps as a handler. To do this, we need to execute a single line of JavaScript. In the address bar, copy and paste the following:

javascript:window.navigator.registerProtocolHandler('mailto','https://mail.google.com/a/yourdomain.com/mail/?extsrc=mailto&url=%s','Google Apps')

Note that you will need to replace yourdomain.com with your actual domain name. Firefox will prompt you to add an application. Click "Add Application."

Capture3 Finally, Navigate to Tools / Options / Applications and set Google Apps as your default for mailto.

8-27-2008 9-57-15 AM

Wednesday, August 13, 2008

FIrst look at Dynamic APEX

Kyle
Roche


I opened a case in my Dev org a few days ago to request that Dynamic APEX be enabled. I took my first look at it today. The first step required to take a look at Dynamic APEX is to open a case in your development org. It took about 48 hours to get the setting enabled. The rest of this post and the follow up posts assume that step has been completed.

Start by creating a new Visualforce page. I'm using a page called /apex/dynamicApex. I created a custom controller called dynamicApexController by changing the <apex:page> component as follows:

<apex:page controller="dynamicApexController">

Let's start with the controller. Switch your editor to the controller view and add the following APEX Property. As in my previous posts, I'm using APEX Properties in place of the old getters / setters. For more information on APEX Properties see the Summer 08 Developer's Guide.

public List<Account> DynamicAccountList
{
get
{
if (DynamicAccountList == null)
{
string myDynamicQuery = 'select id,name from Account limit 10';
DynamicAccountList = Database.Query(myDynamicQuery);
}
return DynamicAccountList;
}
set;
}

Now, this is obviously a basic example. I'll extend this using some more complicated situations in the coming posts. Since we have Dynamic APEX enabled we can now use Dynamic SOQL, SOQL and DML. We're creating a string to hold our Dynamic SOQL query. We can then pass the string to be evaluated at runtime to the Database.Query() method. The possibilities for customization are endless.

To display the results on the Visualforce Page add a quick dataTable component.

<apex:dataTable value="{!DynamicAccountList}" var="acct">
<apex:column value="{!acct.id}"></apex:column>
<apex:column value="{!acct.name}"></apex:column>
</apex:dataTable>

We'll look at some examples of dynamic queries built on user input in the following posts in this series.

Saturday, August 9, 2008

Google Geocoding from Visualforce

Kyle
Roche


Mashups are becoming a common part of most implementations. Replacing legacy applications are sometimes phased into adoption by creating a mashup in Salesforce.com of the current application(s) and slowly replacing components as they are reconstructed using native Salesforce. Google Maps Mashups are among the most popular. However, we found most of the examples were either using the AJAX toolkit or were hard coding the Lattitude / Longitude coordinates. In this example, we'll demonstrate how to use Google's Geocoding API to geocode an Account address from with your Visualforce controller. The key difference is that this example geocodes the address using server side scripting.

Start off by creating a new APEX Class called GoogleGeocodeExtension. This will be our controller extension. Remember, controller extensions have constructors that take an argument of controller for which they are extending. In this example, we'll be extending the standard controller for the Account object. Make sure your class looks like the following.

public class GoogleGeocodeExtension {
private final Account acct;

public GoogleGeocodeExtension (ApexPages.StandardController stdController) {
this.acct = (Account)stdController.getRecord();
}
}

Google's Geocoding API can be accessed via server side scripting. You can choose different output formats like XML, CSV, JSON (default). In our case, we'll keep things simple and return the results in CSV format. Add the following property to your APEX class.

public string[] Coordinates
{
get
{
if (Coordinates == null)
{
Account myAccount = [select name,billingstreet,billingcity,billingstate,billingpostalcode from Account where id=:acct.id];
String url = 'http://maps.google.com/maps/geo?';
url += 'q=' + EncodingUtil.urlEncode(myAccount.BillingStreet,'UTF-8') + ',' + EncodingUtil.urlEncode(myAccount.BillingCity,'UTF-8') + ',' + myAccount.BillingState;
url += '&output=csv&key=yourgooglemapkeyhere';

Http h = new Http();
HttpRequest req = new HttpRequest();

req.setHeader('Content-type', 'application/x-www-form-urlencoded');
req.setHeader('Content-length', '0');
req.setEndpoint(url);
req.setMethod('POST');

HttpResponse res = h.send(req);
String responseBody = res.getBody();
Coordinates = responseBody.split(',',0);
}
return Coordinates;
}
set;
}

This APEX property queries the billing address for the Account record and passes it to the Google Geocoding API. Because spaces and other special characters can appear in addresses and city names we need to use the urlEncode() method to properly format these strings.

We chose to use the CSV format on our response. So, we simply need to split the string by the comma delimiter so we can access each field individually. To keep things simple you can add the following two properties to your controller extension.

public string CoordinateLat { get { return Coordinates[2]; } }
public string CoordinateLong { get { return Coordinates[3]; } }

Like any other APEX property in a controller or extension you can access these in your Visualforce page using the standard binding syntax {!CoordinateLat}.