Wednesday, October 31, 2012

Invincible 'Dashboard' using Visualforce

One of our clients wanted to build a 'dashboard' to display in their lobby, to show some key metrics about how the business was doing for all employees to see without having to go run reports.  Here are the requirements:
  • Update at least every 10 minutes
  • Secure from the outside world (so competitors can't find and watch it)
  • Don't need to babysit it
  • Work across multiple sites
  • Have custom branding
We decided to build it as a custom Visualforce page and put it in a public facing site. We then limited the access setting for the sites profile to only allow certain IP ranges that the client owned.  

The Visualforce nature lets us easily grab the information direct from their database and build it with custom branding.  Building into a public site takes out all the headache and security issue that go along with authentication, and makes it so that the unattended PC it is running on is not a security issue.  We can keep out people that shouldn't be looking at it by restricting the IP range to the sites that are going to use it. Finally, it is pretty easy to update it as often as needed by using an <apex:actionPoller />.

This let us fulfill all the requirements except for the babysitting requirementIf the computer lost internet for a few minutes, or if the Salesforce servers went down for maintenance overnight, the page would throw an error, and the actionPoller would stop runnng. Then someone would have to find the computer and hit 'refresh' to get it to start up again. 

To get around this, we built a second Visualforce page and used it to wrap the page with the actionPoller on it.  We then just use javascript to refresh the frame that the real page lives in.  The result is a page that is virtually indestructible.  Because the full page is not re-rendering, it will self-heal, even if the wrapped page goes down or the internet gets disconnected for a bit.
<apex:page showHeader="false" sidebar="false" cache="false">
<script>
     setInterval ( "refreshFrame()", 600000 );// Self-heal every 10 min

     function refreshFrame ( )
     {
          // (refresh the dashboard)
          window.DashboardFrame.location = "/apex/DigitalDashboard";
     }

</script>
<iframe src="/apex/DigitalDashboard" name="DashboardFrame" id="DashboardFrame" scrolling="no" height="768" width="1024"/>
</apex:page>
To summarize, here are the steps to build your own 'dashboard':
  • Build a Visualforce page that displays whatever data you want. 
  • Build the wrapper page above, replacing 'DigitalDashboard' with the page name you just built
  • Create a Site, and add both pages to the site
  • In your site, modify the 'Public Access Settings' and add the IP ranges that you want to view the site.  If you want it totally public, just skip this step.
  • On the computer you want to show the dashboard on, browse to the site URL, and hit F11 to put it in fullscreen.  
  • Walk away!  The dashboard should run forever.

Considerations:

  • I realize it seems odd to wrap a refreshing page in another page that also refreshes.  The key difference is that one uses an AJAX actionPoller, which does not 'flicker' when re-loading, and the other uses a frame refresh, which might flicker when re-loading.  If you don't mind flickering, you don't need to use an actionPoller at all. 
  • A Salesforce Site has limited bandwidth.  It is easy to check by just going to the Site details in Setup; limits and usage are listed at the bottom of the page.  If this page is going to run 24/7, which is the point, you should turn the refresh rate down to make sure you don't blow over the limits.  




Monday, December 19, 2011

Mike the Customer



The other day we got together with Marketing, remodeling magazines, some scissors, and some colorful markers and created our customer. It was a fun way to end the week.

The goal of the project was to create who we thought our customer was.
  • What are his interests?
  • What is important to him in business?
  • What is important to him in his personal life?
"Mike" is now apart of the improveit! 360 team. When we are developing new products or features we will turn to "Mike" to see if it is something he would be interested in.

Friday, January 21, 2011

Dreamforce for Dummies

I realize this is a technical blog, but I have special talents when it comes to reviewing (imagine an analyst being good at writing reviews.... shocker!!). So, here's my recap / review of Dreamforce for any of you fellow techies who are considering attending in the future.

This year was my first year attending Dreamforce. I'd heard a lot about it from co-workers and acquaintances who had attended in years past. I expected a large conference where, everyone attends sessions during the day, has a few hours of downtime in the evening, then possibly a dinner party or networking event. Reality did not meet my expectations. This was not a bad thing.

Transportation from the Airport: There are several ways to get from the airport to your hotel (and vice versa). One is to take a shuttle / taxi (I recommend this). The other option is to take the BART. Bart is a rickety (very loud) old train that takes you back into the 1970's through creepy tunnels and bad looking neighborhoods. If your hotel is near market street, it does have a stop that is relatively close. It is not worth a dollar more that the $7.50 it costs to take it one way.

Hotels: Book early and try to stay around 4th and Market. There are several hotels in this area, and it is in very close proximity to the Moscone center. We stayed at the Mosser. This is a very unique hotel, that offers some standard hotel rooms, and other hostel type rooms with shared restrooms. Economical, not fancy, got the job done. Keep in mind you will literally only be at the hotel to sleep.

The Conference: While "cult-ish", with uniformed Salesforce people and their Harry Potter scarves swarming the streets and conference buildings with walkie talkies and clip boards, this is to be expected at any conference. Clouds are taken to a new extreme. A generation that has no idea what a bean bag chair is will soon be educated upon attending a Dreamforce conference.

The keynotes were lots of marketing, but definitely worth attending. There were announcements galore on new features, live demos, and lots of 'people getting excited getting you excited' type stuff. I attended about 3 sessions that I would call great. Otherwise, the sessions were not overly impressive. Some are poorly named and described, so it's not until half way through the session you realize it has nothing to do with your job or function. There are ad hoc sessions that are put together as new features are announced. I highly recommend attending these, as the SME's are doing the presentations and all the information is new. In addition, stay after the sessions and ask questions - you will assuredly get more out of the sessions if you do this.

Also, there is a huge developer zone, where you can basically take any development questions and ask a Salesforce developer how to solve it. This is hugely beneficial. They also have a plethora of valuable development resources, so pack a light bag. You will be bringing lots of valuable freebies home. Some of them are heavy (force.com fundamentals book, etc).

Food: If you require more food that a 5 year old child, bring snacks. There were no snacks or beverages available between sessions. I ended up going to a Walgreens and stocking up on Diet Coke, Tea, and Honey Roasted peanuts.

Nightlife: Be prepared to not sleep. We had partner events galore, and when not attending those, we were meeting our Salesforce reps out. The relationship building and networking aspect of the conference is phenomenal - your biggest benefit from the conference will be to take advantage of this.

In conclusion, Dreamforce for me was sort of like the technical version of the social event of the year. The most I got out of the conference was from talking to people - speaking with presenters after the sessions, asking questions, networking, and meeting the individuals we do business with from a distance the other 361 days of the year.

The one thing I will most definitely do differently when attending Dreamforce next year is to wear comfortable shoes.

Friday, November 19, 2010

Troubleshooting Lead & License Creation Issues in the LMA

We ran into an issue a few months back where licenses and leads were not being created in our LMA (License Management Application) for new trials. Without licenses, we have no way to activate a trial in the event that a trial user subscribes. We tried several workarounds before finally figuring out the issue - which was frustratingly obvious.

First, we attempted reinstalling our package over the trial, in hopes that installing the package would cause the lead and license to be created. Unfortunately, this didn't work. We now know it didn't work becuase it was an issue in our LMA.

Next, We attempted manually creating licenses, since we knew the Org IDs of the trials that had no licenses. Unfortunately, Salesforce doesn’t seem to make the association in the background. I think it may have something to do with the Package License ID, which is automatically populated on a license that’s created from creating a trial. Our manually created licenses were missing this value.
Finally, our technical evalgenlist (salesforce support couldn't identify the issue) was able to walk us through a few critical checks in the Org that our LMA was installed in that did eventually lead us to finding the issue and resolving it. I couldn’t find this information anywhere online, hence the reason for this blog post.
So, if you’re having issues with licenses and leads being created in your LMA, try the following:
1.       Make sure your package is registered and listed properly on AppExchange
  • The package version of your trial you are creating is listed
  • In the leads tab on your app exchange listing, the org that contains the LMA is designated under the “Send Leads to” field.
2.       In your org where your LMA is installed, check the following:
  • There are not required fields on the Lead object that aren’t being populated. If there are, this could be causing the creation of the lead to fail in the background and prevent the license from being created.
  • There are no custom assignment rules executing when the lead is being created that are assigning that lead to an inactive user.
  • The Lead Manager on the package that the trial is for is an active user. This turned out to be our issue .

Friday, September 24, 2010

Using the Old Report Builder

The new report builder is flashy and has increased functionality, but there are times when using the old one is just quicker for some functions.

When the new report builder showed up you had the option to use either method to build or edit a report. Then one day my ability to use the old one just went away. Here's what happened:

There is a new permission on profiles called 'Report Builder' (under General User Permissions). When this is checked, you can only access the new report builder. To access the old one, you have to uncheck it. Here's the trick: System Administrators automatically have all permissions checked and you can't change that. So if you need access to the old way of doing things, you won't be able to do it with a user who is an admin.

Support suggested cloning the admin profile and then unchecking the option, but I have not performed that exercise.

Monday, September 20, 2010

Storing a Website URL for use in Custom Email Templates

In Salesforce setup, most users are familiar with the “Organization” object, which stores basic organizational information such as address, time zone, and phone numbers. Well it’s my understanding whoever created this object was designing applications for companies stuck in 1980 who don’t have websites. The URL for an organization’s website is a critical piece of organizational information that most companies want to have available on emails and other system templates.
Unfortunately, custom fields cannot be added to the organization object, making it impossible to do what would seem most intuitive - create a URL field to store a company website on the Organization object. A workaround we’ve used to circumvent this limitation is to do the following:
Step 1: Create a Custom Setting to Store the Company Website URL
Simply create a new custom setting. Keep the default setting type of hierarchy and set the visibility to Public. Once the setting has been created, add a new custom field to the setting. It should be created as type “URL” (it will look something like the image below when you’re done).

Don’t forget to save an actual website URL in the field once you’ve saved it.
Step 2: Add a Custom Field to the User Object to Reference the Custom Setting
Next, simply go to the user object and add a custom field of type “formula”. In the simple formula area, reference the custom setting that was created in step 1. Your reference formula should be in the format of:
$Setup.CustomSettingAPIName.CustomSettingFieldAPIName
            Save the custom field when you’re finished.
            Step 3: Reference the User Website Field in a Template
Now, referencing the company website URL in an email template is easy! Since each user in the system now has the website stored in a custom field, the website URL can be accessed through any system user. Luckily, a field type called “Sending User” is available for use on any template. You can now place a website URL in a template by using the following merge field:

Implementing Quicksort to sort a list of SelectOptions

A common task when building Visualforce pages is displaying a dropdown list that is populated by a list of SelectOptions, and making sure the values in the dropdown are ordered alphabetically. The most efficient ways to sort in Force.com are to either ask the SoQL database to do it for you by using ORDER BY, or by using the List object's .sort() function. Unfortunately .sort() only supports primitive datatypes, which SelectOption is not. And if the list didn’t come from a simple SoQL query that you can tweak the ORDER BY statement on, or if you just want to save a query, then it's time to bite the bullet and sort the list yourself.

Sorting a list is one of the most fundamental problems in computer science, and there are many ways to do it. We chose to use one of the most popular algorithms, Quicksort, because it doesn’t require a lot of memory, has high performance, and is easy to implement. To us, that means our sorting won't use up too many script statements and we wont' need to spend 4 days writing and testing the sorting code.

Below is the implementation of Quicksort that we use to sort out SelectOption lists. Note that this implementation does not do a true in-place sort, so the memory usage is a bit higher than it could be, but it has served our purposes very well.

You should be able to simply drop this in a class and use it as is!

    //  This is a simple quicksort algorithm to sort a SelectOption list (dropdown) 
    // by label alphabetically.
    global static List<SelectOption> SortOptionList(List<SelectOption> ListToSort)
    {
        if(ListToSort == null || ListToSort.size() <= 1)
            return ListToSort;
            
        List<SelectOption> Less = new List<SelectOption>();
        List<SelectOption> Greater = new List<SelectOption>();
        integer pivot = ListToSort.size() / 2;
          
        // save the pivot and remove it from the list
        SelectOption pivotValue = ListToSort[pivot];
        ListToSort.remove(pivot);
        
        for(SelectOption x : ListToSort)
        {
            if(x.getLabel() <= pivotValue.getLabel())
                Less.add(x);
            else if(x.getLabel() > pivotValue.getLabel()) Greater.add(x);   
        }
        List<SelectOption> returnList = new List<SelectOption> ();
        returnList.addAll(SortOptionList(Less));
        returnList.add(pivotValue);
        returnList.addAll(SortOptionList(Greater));
        return returnList; 
    }