Friday, August 30, 2013

A Riddle, Wrapped in a Mystery; Inside an Enigma

Do you ever see inconsistent results when running unit tests in a sandbox versus running them in production?  Or, inconsistent results when running unit tests between sandboxes?  Well, the black-box that is the Salesforce unit test, just became blacker and boxier to me.

I was trying to deploy a patch to production and was going through our normal build path that passes through our full sandbox.  When attempting to deploy to the full sandbox, our automated build process failed on a test method that we had not changed in any way.

The error: ... System.TypeException: Invalid date/time: 05/05/2010 00:00 AM stack...

A colleague and I inspected the method and the class and determined that they were identical in our sandbox and production.  When we ran the individual test in our full sandbox, it failed, and when we ran it in our production environment, it passed.

What the?!

The error pointed to this fragment of code in the test: datetime.parse('05/05/2010' + ' 00:00 AM');

Not sure why it was 00:00 AM so I updated it to 12:00 AM and was able to proceed with the deployment.

I was unsettled by experience because we had been able to deploy to this sandbox two weeks earlier and had not had this test method fail.  So, we opened a case with Salesforce and asked their "premier" support to explain it.  Our first support rep reproduced the results but gave us a canned response that it wasn't a good practice to use 00:00 AM.  Fine, we said, but tell us why it passes production tests but not tests in our full sandbox and what exactly changed in our sandbox?  We went on and on like this for a couple days, escalated the case, and are now doing the same dance with another "premier" support rep.

For those of you who read this, please try this at home and let me know if you can replicate this in your sandboxes:

Create a test object called TestDate__c.  Add one custom field called SomeDate (type = date/time).

Create an apex class (api version 27, for consistency sake) with the following code:

global with sharing class TestingDate
{
    public static testMethod void testMyObj()
    {
        TESTDate__c myobj = new TESTDate__c();
        myObj.SomeDate__c = datetime.parse('05/05/2010' + ' 00:00 AM');
        insert myobj;
    }
}


Run your test.  The results we got were as follows:

NA14 (developer org): Pass
CS15: Fail
CS12: Pass

If nothing changed on CS15, how can this be?  Still waiting for an answer...

Wednesday, August 28, 2013

Winter 14 Highlights

Winter 14 previews are being applied to sandboxes over the next week or so.  I've gone through the release notes and pulled out some notable items.  Before getting to these items, a couple general reactions to the release notes:

  • Salesforce is getting massive.  I mean, between the Sales & Service features, Chatter updates, API changes, and all of the new ".com" products like Data.com, Work.com, Desk.com, and Social.com, it's a wonder that anyone is able to keep anything straight, including Salesforce employees.
  • Chatter keeps getting the lion's share of enhancements.  Since 2010, it seems that every quarterly release is stuffed with Chatter updates with a few core CRM improvements thrown in to keep the masses happy.  I've been doing Salesforce.com work since 2008 and only know of a few clients who use Chatter regularly.  I understand the academic appeal of Chatter but given the realities of email in business, can you really believe that integrating a canvas app into a Chatter stream is more valuable than providing a true M:M relationship between Accounts and Contacts or providing a better and more flexible Salesforce/Outlook/Exchange plug-in?  

Without further delay, here are some notable features coming our way in middle October with Winter 14:

User Object Sharing
I was surprised to find out that this object was not previously governed by sharing.  There are some interesting use-cases where you'd potentially need to hide/share user details.  Curious to see how this works if the org-wide default is set to Private with Chatter-enabled orgs.  Also notable, Apex managed sharing is not supported for this object.

Approval Emails with Comments
This is one of those "duh" features that has taken a backseat to Chatter.  Finally, you can add the approver's comments to an approval email notification without having to build some trigger or vf-based email.

Embed a report within a standard detail layout
I would also categorize this under "duh".  Finally, remove the link to the report and just put the report in the layout.  Much better, though it seems there are some limits, such as, only 2 charts per layout.
Sandbox Updates
Configuration-only sandboxes are being renamed to "Developer Pro" and will have storage limits bumped up from 500 MB to 1 GB.  Developer sandboxes will continue to be called "Developer" but will also get a bump in storage from 10 MB to 200 MB.

Developer Console
I do most of my development in Eclipse but if I'm away from my laptop, I use the developer console as it is a vast improvement over the standard code editor.  A couple highlights here:

Have you ever had a complex process to debug?  Well, you probably ran into a log limit, right?  Just before discovering the source of your headaches, you see text like "Log limit reached"... ugh!  You can now override log levels for a specific trigger or class.  So you could turn logging down by default and then turn it up on the class or trigger where you suspect the problem resides.  Sweet!

One new feature that caught my eye was this debug tool in the console.  It looks like it will graphically display the order of execution.  Very nice!


Visualforce Updates
VF has some HTML 5 updates that should prove useful.  One that stood out to me was the new <apex:input> tag.  You can specify the type by using a "type" attribute.  The browser, using HTML 5 standards, should render the input based on the type.  One use case where this should help is with input dates.  If you needed to provide a date input, without binding it to a Salesforce object's field, you had to do some hokey workaround to get the date picker to display.  Now, you should be able to just do something like <apex:input type="date">.

There is also support for a "list" attribute to add to the input tag.  It looks like this should provide visualforce with some autocomplete capability that you could previously do with some jquery magic.  For picklists, we'd typically generate a list in the controller, then bind the list to VF through selectlist/selectoption tags.  I guess we'd use the input tag and list attribute in cases where we'd need the autocomplete on and where we wouldn't need some of the selectlist/selectoption features like displaying but disabling values.

Apex Changes
The maximum number of code statements has changed from being a flat number to CPU time-based.  Previously this was 200k, which is suprisingly easy to hit, when you have some nested loops.  The new limit is based on whether you are synchronous or asynchronous (batch Apex).  They've advertised this as removal of a limit, but in reality, they've just changed how the limit is computed.  Seems like it'll be harder for a developer to anticipate CPU utilization.

The new Database.getUpdated() method allows you to pass in the object, and the start and end date/time and will return to you a list of updated records.  That should be useful!

There is a new BusinessHours static method that allows you to pass in a date/time and an id to a Business Hours object to see if something is within the business hours and to fetch when the next available business hours start.  That should be handy.

Other
Environment hub?!  I need to see screen shots to determine the actual utility, but it potentially sounds useful. Particularly if you are an admin for multiple orgs with multiple instances.

***

When the changes are actually applied to my personal sandbox, I'll do some mockups of some of the new VF stuff.  Stay tuned.


Friday, August 23, 2013

Salesforce Sandbox Instances


A while back, I was on a client site that was affected by a sandbox outage that lasted over 3 days. Unfortunately, both of our full copy sandboxes were on this instance and a good number of our developer/configuration sandboxes were as well.  Needless to say, we were greatly affected by the outage and when we asked about migrating some of our sandboxes to another instance to reduce the risk for our team, we were told that it was not possible.  Today, again, there is another major outage, this time on CS15, and again, most of our development, testing, and full copy sandboxes are affected. It seems crazy to me that customers who seek the cloud for protection against this type of outage do not have the ability to specify that they want their sandboxes distributed across multiple instances.  If we have a break/fix situation, we are in a difficult situation given that our development path is dependent on CS15 being operational.  Customers should be allowed to spread their developer sandboxes across instances to reduce the impact of another outage.

Vote for the idea here.

Tuesday, August 13, 2013

Required InputTextArea

I was asked to make an inputtextarea required on a visualforce page and thought, "ok, no problem, 2 minutes!".  In reality, this was kind of a nightmare to implement.  My vf page looked something like this:

<apex:pageblock id="pageBlock">
<apex: pagblocksection id="pageBlockSec">
<apex:pageblocksectionitem>
<apex:outputlabel value="Big Field">
<apex:inputtextarea value="{!myObject__c.Big_Field__c}" required = true id="bigfield"}"
...

So the first issue is that the iconic redline next to the required field does not display for text areas.  To fix this, you have to wrap the tag with an outputpanel like this:

<apex:pageblock id="pageBlock">
<apex: pagblocksection id="pageBlockSec">
<apex:pageblocksectionitem>
<apex:outputlabel value="Big Field">
<apex:outputPanel styleClass="requiredInput" layout="block">
<apex:outputPanel styleClass="requiredBlock" layout="block"/>
<apex:inputtextarea value="{!myObject__c.Big_Field__c}" required = true id="bigfield"}"
</apex:outputPanel>
....


When unit testing, the error that is displayed is something like:

pageBlock:pageBlockSec:j_id38:bigfield: Validation Error: Value is required.

This is not a message a user would understand, even with the text area marked with a red line.  Some of the initial searches turned up crazy solutions like using jquery to clean up the message, or rewriting the validation to occur within the controller.  So, it took a while but the solution was buried in this thread.

To remove the garbage text in the error, you have to provide the textarea a label attribute.  Your final markup will look something like this:

<apex:pageblock id="pageBlock">
<apex: pagblocksection id="pageBlockSec">
<apex:pageblocksectionitem>
<apex:outputlabel value="Big Field">
<apex:outputPanel styleClass="requiredInput" layout="block">
<apex:outputPanel styleClass="requiredBlock" layout="block"/>
<apex:inputtextarea value="{!myObject__c.Big_Field__c}" required = true id="bigfield" label = "Big Field"}"
</apex:outputPanel>
....

Friday, August 9, 2013

Auto save with TinyMCE and Salesforce

I'm on a project where our users have large numbers of long text areas and are often working on old-ish computers.  One common complaint was that they'd spend a good amount of time typing something only to find that they'd lost it when they clicked Save.  We explored some options with some features within HTML 5, like local storage, but settled on trying auto-save.  Auto save is a common feature in most online services like blogger or gmail, so this seemed like a good model to follow.  Additionally, it seemed less browser dependent, which is also good.

The system is composed of a Visualforce page using TinyMCE as the rich text editor.  TinyMCE has a (somewhat buggy) function that will tell you if an editor instance "is dirty".  We set a timer to check this attribute every few seconds.  If the attribute returns true, we can execute a controller method via javascript remoting.

Putting it all together:

A visual indicator (to show the user that the auto save ran):

<div id="saving" class="minitext" style="color:#666; font-style:italic; display: none">AutoSaving...</div>

The timer:
var $j = jQuery.noConflict();  
// First we tell this to run when the page is loaded
$j(document).ready(function()
{
  $j(function()
  {
    setInterval("auto_save('{!$Component.theform.thepageblock.thepbs.somefield}')",5000);
  });

});


The auto_save function referenced in the timer:
function auto_save(inputs)
{
  // First we check if any changes have been made to the editor window
  if(tinyMCE.getInstanceById(inputs).isDirty())
  {
    $j('#saving').show();
    var content = tinyMCE.get(inputs);
    var notDirty = tinyMCE.get(inputs);
    saveTestField('Some_Field__c', content.getContent());

    notDirty.isNotDirty = true;
   }
else
  {
    $j('#saving').hide();
    return false;
  }


The saveTestField function:
function saveField(fieldName, fieldValue) 
{
    Visualforce.remoting.Manager.invokeAction('{!$RemoteAction.AutoSave.updateFieldValue}', recId, fieldName, fieldValue, 
        function(result, event){
            if (event.status) {
            } else if (event.type === 'exception') {
                console.log(event.message);
            }
        }, 
        {escape: true}
    );


The Apex method:
@RemoteAction
public static Boolean updateFieldValue(Id recordId, String fieldName, Object fieldValue)
{
String sObjectName = recordId.getSObjectType().getDescribe().getName();
String queryString = 'Select Id, ' + String.escapeSingleQuotes(fieldName) + ' From ' + String.escapeSingleQuotes(sObjectName) + ' Where Id = \'' + String.escapeSingleQuotes(recordId) + '\'';
Sobject record = Database.query(queryString);

record.put(fieldName, fieldValue);
update record;

return true;

}


Observations:

  • UI validations are completely ignored.  If you have a required field in your UI, the field will still commit, without error.
  • The auto save only commits the field passed to the controller.  No other field is saved.

Issues

  • One issue that I observed while assembling this was that the isDirty attribute wasn't always reliable.  It turns out that you have to tell TinyMCE to save at some point for the attribute to reset itself.  You can do it by adding this line: tinyMCE.get(inputs).save();
  • Another issue: this recipe works well for existing records (records with Id).  If you're inserting a new record, you'll need to modify accordingly.

Credits
This was assembled with some helpful tips and tricks from other folks sharing their solutions.  Thank you for sharing!
https://github.com/pbattisson/Visualforce-Autosave/
http://webnv.net/2008/02/10/autosaving-with-jquery-and-tinymce/

Opportunity Contact Role "Trigger"

Among the many limitations of the standard OpportunityContactRole object in Salesforce is the inability to put a trigger on it.  The use case I wanted to solve was to aggregate and copy the contact information related to certain roles to the parent opportunity.  Some options I considered:

1. Create a batch job to query OCR nightly and update the parent opportunity
2. Create a trigger on Opportunity updates to fetch the OCR data
3. A combination of 1 and 2
4. Replace the standard OCR with a custom OCR and create the necessary trigger

I finally came across this blog post, and with some tinkering, found that it worked for our use-case. The idea is to hide a inline visualforce page within the standard layout and use that vf page's action property to run some code.  The author chose an asynchronous opportunity update whereas I went with a synchronous update.  The action property on the page tag is generally used for redirects, but could certainly be used for defaulting data as well.

Some issues I came across:

1. Since visualforce is served up from a different domain, I was seeing my page redirected by a servlet to a "page not found".  I was able to stop this by returning a non null page reference.
2. The js console in chrome logs some refusal errors due to the different domains.  This went away with the resolution to #1.
3. I was unable to refresh the parent page from the inline vf page after the update, so the user has to manually refresh the opportunity to see the effect.  This is still the case but our users were ok with it.
4. The hidden vf page's controller is trying to execute a DML.  This is usually followed by a page refresh.  The problem is that the page refresh is occurring within the inline VF, which,when it renders, again executes the controller's action.  This can cause some serious looping, which is why it is imperative for the action method to check whether an update is required or not.


Thursday, August 1, 2013

Visualforce Page with ContentType in MultiByte Language

I'm working on a project where there are several multibyte languages, like Chinese and Arabic, that are supported.  One issue that we discovered in a new feature is related to exporting some content as a Word file.  The issue was in specifying the filename.  As you probably know, the syntax for something like this is:


<apex:page Controller="yourController" contenttype="application/msword#yourfilename" .. />

This works great but if you are substituting yourfilename with something from the controller, one symptom you could see is the file generated with the name of your VF page.  So, let's say you were doing something like this:

<apex:page Controller="yourController" contenttype="application/msword#{!someVar}" .. />

If {!someVar} is a value that is in Chinese or Arabic, your file name will probably look like "YourVFPage.doc" instead of "{!someVar}.doc".

There wasn't much in the Salesforce support community so I've come up with a workaround:

1. I added a charset identifier to the contenttype attribute like so:

<apex:page Controller="yourController" contenttype="application/msword#{!someVar};charset=utf-8" .. />

When you try to view your file, you get a little closer - your filename will likely look like: "------.doc".

2. The following thread, gave me the idea for the fix for ---- characters.  In the controller, we just encode the someVar value like so:

  String someVar = EncodingUtil.urlEncode(myString, 'UTF-8'); 
  return someVar;

When you try to view your file, you'll see the multibyte value in the filename.  You may have to do some substitution to remove any other characters, but this should get you closer to a user acceptable solution.