Cross-platform Compatibility for Visualforce Pages
June 10, 2017 - Updated for the Summer 2018 Release
Objective:
With more employees using the Salesforce1 App and the shift of many organizations to Lightning Experience, it has become increasingly more important to develop Visualforce pages that work for users on Salesforce Classic, Lightning Experience, and Salesforce1. This is especially true when developing packages for the AppExchange because there is no way to know how your audience will be using the package.
Challenges:
Most solutions to solve this problem require maintaining a separate Visualforce page that is included in subsequent Visualforce pages. This includes server-side logic so that the “rendered” attribute can be used for Apex elements. If you’re like us, you avoid using native Apex elements when possible.
This approach also necessitates the use of many extra elements because each of three platforms has a unique URL scheme. For a simple link to a record detail page, you would need three elements – two of which would not be rendered. You will also need to type the individual URL scheme for each element. This can be very time consuming on complex Visualforce pages.
Finally, there are certain tricks that work on Salesforce Classic but not on Salesforce1 and Lightning Experience. For example, adding URL parameters that pre-populate fields when creating a new record will only work for Salesforce Classic.
Solution:
We decided to create reusable codebase to handle this on the client side using jQuery. We use the $User.UITheme Global Visualforce Variable to determine the platform on which the user is accessing the page.
Once the platform is identified, we dynamically bind the “href” attribute to links and buttons. We also use the class attributes to hide/reveal certain functionality based on the platform. For example, we hide any links and buttons that use URL hacking to pre-populate fields on a record creation page.
This code can be maintained in a separate JavaScript static resource and referenced from all Visualforce pages. Any changes to the URL schemes will only require a change to the JavaScript file.
This solution is also available as an open-source, unmanaged package. Get more details here.
We developed a managed package for CapiT - Available now on the AppExchange
April 17, 2017
Objective:
Our friends at CapiT help their clients increase sales with an NLP-based approach to managing customer relationships. They hired us to build them a managed package for Salesforce customers to download from the AppExchange.
The app needs to show how the customers’ salespeople are performing compared to their colleagues within the company and industry. Sales managers and account teams need to be alerted automatically via Chatter when certain updates are made to the Event or Opportunity.
It needs to be functional on Salesforce Classic, Lightning Experience, and the Salesforce1 mobile app. The mobile pages must be fast because, without a positive user experience, adoption will suffer.
Challenges:
To gather the details necessary, an object needs to be created that is a child of Events. Unfortunately, at this time, Salesforce does not allow children of Events or Tasks.
Salesforce Classic, Lightning Experience, and Salesforce1 have different URL schemes. This means that a simple link to a record detail page has three different values depending on which platform the user is on.
Reloading a Visualforce page to retrieve new data is slow, and the reload takes away from the user experience. We are also using filtering and sorting functionality which would create even more reloads.
Solution:
To facilitate the collection of data, we created an object with a text field for the Event ID. This is not truly a child of the Event and can cause problems with orphan records. To eliminate these issues, we created validation rules and triggers that help keep the data clean. Triggers are also used to post to Chatter on behalf of the user.
We also need to host the industry averages on an external server to be retrieved by the dashboard. Complex logic calculates the averages for the entire company and for each account. The dashboard graphically shows you how each account is progressing in comparison to the other accounts and to the industry as a whole.
To maximize the performance of the pages, we used JavaScript Remoting after the initial loading of the page. When a user decides to drill-down on an Account, the Account data is retrieved using AJAX instead of a page reload. Because we are retrieving all relevant data with one SOQL query, we can do our filtering and sorting on the client side with jQuery. Finally, we used jQuery again to dynamically bind the href attributes of the links/buttons on the page and to dynamically hide/reveal certain functionalities depending on the platform being used.
Find out more here.
Send multipart/form-data http request from Salesforce
February 19, 2016
Objective:
Automatically send status updates to Twitter from the Salesforce.com platform.
Challenges:
After writing Apex methods necessary for OAuth 1.0 authentication, sending a Tweet is not terribly difficult. If you want to send a Tweet with an attached image, it starts to get complicated. It is a two-step process using the Twitter API version 1.1. The first step requires the use of a multipart/form-data http request from Apex. The challenge lies in finding documentation for this online.
The solution was discovered after significant trial and error. It required research into the w3.org specifications on multipart/form-data requests. I also tried to adapt a solution that was written in Apex, but the code was over-complicated with conversions back and forth from string to blob. It is my aim to show you a well-formed request so that you can easily recreate it in Apex.
Solution:
Encode your image using the Apex Base-64 encoding method and create a string for the body of the request. The well-formed request body will look something like this:
------------------------------741e90d99eff
Content-Disposition: form-data; name="media_data";
Content-Type: application/octet-stream;
Content-Transfer-Encoding: base64
/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAMCAgMCAgMDAwM//Z
------------------------------741e90d99eff--
The Apex code to create the string is below. Please note that some of the values are passed in from other methods.
public static MediaResponse postMediaCallout(string verb, string endpoint, string sAuth, string contentType, string image){
String boundary = '----------------------------741e90d99eff';
String header = '--'+boundary+'\nContent-Disposition: form-data; name="media_data"; \nContent-Type: application/octet-stream; \nContent-Transfer-Encoding: base64';
String footer = '--'+boundary+'--';
header = header+'\r\n\r\n';
footer = '\r\n' + footer;
string body = image;
body = header+body+footer;
HttpRequest req = new HttpRequest();
req.setMethod(verb);
req.setEndpoint(endpoint);
req.setHeader('Authorization', sAuth);
req.setHeader('Content-Type','multipart/form-data; boundary='+boundary);
req.setTimeout(120000);
req.setBody(body);
req.setHeader('Content-Length',String.valueof(req.getBody().length()));
Http http = new Http();
HTTPResponse res = http.send(req);
system.debug(res.getStatusCode() + '-------------' + res.getStatus() + '-------------' + res.getBody());
MediaResponse response = (MediaResponse)JSON.deserialize(res.getBody(), MediaResponse.class);
return response;
}
Salesforce.com Visualforce Bulk Data Entry
February 5, 2016
Objective:
We were helping a non-profit streamline their business processes. They needed to enter multiple donation checks at once and automatically generate PDF acknowledgment letters.
One of the re-occurring themes in most business is that users are spending much of their day navigating from page to page for data entry. With Visualforce pages, we can create custom forms to insert or edit multiple records at once. These projects are the easiest to justify because the time-savings pay for the project in just a few weeks.
Challenges:
When entering donations, the user must be sure that the account and contact already exist. This will require a search feature or autocomplete widget. Both of these options tax the page performance because the dataset can grow to be quite large is some organizations.
Our client also wanted checkboxes next to certain dropdown lists. This way if all checks in certain batch are associated to a particular campaign, they only need to select the campaign on the first row. That functionality made for more complex javascript logic when adding a row when the box is checked.
Solution:
The Visualforce page starts with a table containing only one row. To make the data entry process even easier, we used a few jQueryUI widgets. The account field uses an autocomplete widget to let the user search for existing accounts right in the form. If the account is not found, a modal dialog can be called to insert a new account on the fly. On the payment date field, a date-picker widget is used to prompt a calendar popup. At the end of each row, is a simple javascript button to add another row if needed. When the user is finished entering the data, a javascript button remotely calls the save method from the controller. At any time, a user may generate acknowledgment letters for any contacts who did not already receive a letter for their donation.
Salesforce.com Simple Tweet Portal
December 21, 2015
Objective:
Create an app that allows social media managers to post daily tweets in multiple categories (eg. quotes, articles, promotions.) The app must be mobile-ready, allow for bulk data entry, and data uploads from Excel.
Challenges:
Strict logic is neccesary to make sure the correct tweets are brought up in the queue. Also, the user must be able to store tweets for multiple accounts.
Finally, the finished product is to be a managed package for distribution on the Salesforce.com AppExchange.
Solution:
For this project, we used an approach know as test-driven develpoment. To get started we wrote the test classes that will test our insert, update, and get methods. These were fairly straight forward.
The insert and udate methods use the @Remote annotation, because we will call them on the client-side. In addition, we added a test method that inserts 250 tweets.
Although this functionality is not affected by our app, we need to be sure that any future enhancements or customizations won't intefere with bulk/batch inserts.
The user interface is a single page app built on the jQuery mobile platform. The "Tweet" button brings up the Twitter web-intent with the body of the tweet filled out with the parameters in the URL.
At the same time, javaScript remoting is being used to update the record so that the user will know that this particular tweet was sent today.
When the "Quick Tweet" page is viewed, there is one tweet for each category within each account that is next in the queue based on the date the tweets were last sent. Tweets can be sent very quickly from the Salesforce1 mobile application.
This managed package is currently under security review by Salesforce.com. Once approved, it will be available free of charge. For our regular consulting fee, customization can be made to meet the needs of your orginazation such as calling your link-shortening service or authomatically entering tweets into the database based on other records.
For more information about the functionality and setup of this app, please visit www.TimeToExpand.com/SociaCrewSimple.
Salesforce.com Time Tracking App
October 12, 2015
Objective:
Allow employees to track the amount of time they spend on a particular task, case, and job function.
Challenges:
The portal needs to include all functionality on one page to drive adoption. Additionally, the information must be easily retrievable for reporting purposes. The user must be able to track their time on a task and they may work on the task many times on different days. Unfortunately, at the time, we could not create a one task - to - many time log records.
Solution:
We designed and built a custom Visualforce portal page using jQuery UI widgets. A particular user can clock-in to a task and mark it complete from this page. They can then quickly clock-in to the next task or clock-out for lunch/end of day. The user can drag and drop tasks within the sortable list to change the priority of the task. Dialog widgets were used for rapid creation or modification of tasks without navigating away from the main portal. Security settings were used to only allow the tasks and priority lists to be read/edited by the user or his/her managers. Using the Salesforce1 platform, we were able to make the portal available on mobile devices and the sales team is now able to easily use it on the road.
A custom object was created to store the micro data for each clock-in/clock-out event. Each micro-object is created and updated by APEX trigger or custom Visualforce. Another APEX trigger is used to re-order the priorities of each task when a task is marked as completed. Using custom fields, we are able to track the amount of time spent on a task, job function, and case number(s) for each employee. Another Visualforce page was created to export custom reports based on the filter logic chosen by the user.
For this use-case we also added email-to-case functionality. Any email sent to the internal service email address creates a case and is assigned based on the time of day.
Social Media Automation Mobile Application
September 29, 2015
Objective:
Create a mobile app that will allow a social media professional to post to Twitter with minimal effort.
Challenges:
The marketing plan for the particular website includes daily inspirational quotes and semi-weekly links to articles. The app must track the last time a particular item was Tweeted so that it is not repeated until all items have been Tweeted.
Also, the budget for this particular project was only $350 which excluded robust solutions such as oAuth verification, API calls, and SQL data management.
Solution:
We started with the Cordova 5.1.1 platform in Visual Studio 2015 for a quick solution that will work across multiple devices.
Using jQuery Mobile, we parse the XML data in two separate page containers. A Tweet button is created that allows one-touch Social Media engagement. After the Tweet is sent, the user may touch another button to record that the last Tweet was sent today. When the app is loaded again, the user Tweets the item(s) with the oldest "Last Tweeted" date.
The social media manager has since told us that they have an influx of new followers every week now that the posts are consistent in timing and content.