Salesforce Wallpaper for iPad (by request)

August 13, 2010 · Filed Under Companies, salesforce.com · 2 Comments 

Because he asked so nicely, here's a cloudy iPad wallpaper just for JP Seabury (and anyone else who wants it). Enjoy!

iPad Salesforce Wallpaper

iPad Wallpaper (1024x768 at 132ppi)

(Click on the image to download the full-sized version.)

New Blackberry and iPhone Wallpaper

August 1, 2010 · Filed Under salesforce.com · 7 Comments 

Last year, I published a wallpaper image for Blackberry, at a resolution of 320×240 (how quaint). Screen resolution has grown since then, as has salesforce.com and Salesforce CRM.

This is a new wallpaper background using the latest branding, at 480×360.

Blackberry Wallpaper 480x360px

For those of you using retinal displays, here’s one for you:

iPhone Wallpaper 640x960px

I expect to see you ALL using these at Dreamforce 2010!

(Thanks to Jamie Grenney at salesforce.com for the original image post years ago.)

Chatter and the CLM

June 11, 2010 · Filed Under New Features, Tips and Tricks, salesforce.com · 3 Comments 

I love Chatter. I love the way that it is a game-changer. I think that Chatter is a powerful tool. But as Peter Parker's Uncle Ben said, "With great power comes great responsibility," and with the possibility of great rewards from Chatter comes great risks.

First I'll list my conclusions, just so there's no misunderstanding. Then I'll explain some of the possible pitfalls that Chatter presents.

  1. Chatter is amazing. It lets users share information in ways never seen before.
  2. Chatter should be enabled in an org as an all-or-nothing switch - as it is now. Allowing it to be disabled for some users goes against its core purpose.
  3. As a single-opt-in system, the risk of Chatter-Spam is huge, but responsibility falls to each user to self-police, and probably to each company to educate its users in appropriate chatting.

Let's compare Chatter, Facebook, and Twitter in terms of what is necessary for a conversation between two people to appear in my activity stream.

Facebook

Facebook is a double-double opt-in system. To see a wall post from one person to another in my stream, I must be friends with both of them, and each friendship-connection is a double opt-in, meaning that one person requests and the other approves.

Twitter

Twitter is a double-single opt-in system. To see one person's reply to another, I must be following both of them. But unless one of them decides to block me, the default is that I will see the entire exchange in my twitter feed.

Chatter

Chatter is a single-single opt-in system. This means that if I post something to anyone in the org, everyone following that person will see. Here's an example: Let's assume that every user at salesforce.com follows Marc Benioff. One user posts a photo of his son's graduation to Marc. Everyone following Marc will see it.

Sounds innocuous, right? Probably. But if a user keeps posting silly things to Marc, and enough people see it, other users could become annoyed.

Take it down a level, and imagine that everyone on a sales team follows each other. A junior AE keeps sending silly stuff to the strongest seller in the group, and all the sales people have to see it. This could clog their streams.

This is called a CLM: A Career-Limiting Move. As great power requires great responsibility, we must ask who should shoulder this responsibility.

  1. The company: Training for users and a quick eye to bring inappropriate chatters into line.
  2. Individuals: Just be careful. This is the Facebook public-posting dilemma. Don't forget that you have no control with whom you are connected, so anyone who wants to follow you will see anything you post, and anyone who follows someone you post to will see it as well.

The solution is NOT to turn off Chatter for certain people - Chatter is about the free-flow of data across an org, organized into forms that make it useful information. By putting information at the fingertips of every user, productivity will be increased.

Blocking people is also not a good solution.

Chatter Groups (Safe Harbor Statement!) will help, but will attenuate, not completely remove, the chances that this will happen.

Salesforce CRM should be a "sticky" app - that is, it should provide services in one place so that users see it as their central point of information. The best way to ensure the free-flow of information is to prevent blocking of certain users.

I Found a Bug – And Salesforce Support Was Great

June 10, 2010 · Filed Under salesforce.com · 6 Comments 

A week ago, I installed Scott Hemmeter’s amazing app, Geopointe. Everything was going smoothly until I decided to manage the licenses I had assigned to the app.

I successfully managed the licenses and tried to click the “Back to Previous Page” link, but something broke:

Salesforce CRM's standard "Bad URL" error

This should never happen for a Salesforce-generated URL

Here’s a video of the problem in action: Managing Licenses Previous Page Link Is Broken.

This is a story about the super-responsiveness of the salesforce.com support team. (I’m not including peoples’ names, but for any employees, it is Case 03580410.

I submitted my case May 24, and the first response was received May 25. The first person, JW, sent an email asking me to clear my cache and change my browser. I cleared the cache, used multiple browsers, and even tried with different packages in my org. All had the same problem.

May 27, JW tried to call me, but I was unavailable. (Good follow-up.) He also sent an email, and I promised to get back to him. I replied the next day and told him that the problem occurred even on different computers, with different browsers, and different packages. He quickly replied and asked to login to the org to try to recreate the issue.

The next step was for us both to login to the org while on the phone. He verified that the problem was happening, and we noticed something interesting:

Clicking “View Installed Packages” gives a URL like …/0A3?setupid… and clicking Manage Licenses gives a long URL including ?allPackageId=033600000004Pjn. The broken link was https://na7.salesforce.com/033600000004Pjn. See anything there?

The “Back to Previous Page” link should have taken me back to the /0A3 page (exactly as the browser Back button did), but instead tried to show me the inner workings of a package, which Salesforce clearly cannot display.

JW suggested that this was a problem with the app, as the link was on a page managing licenses for two managed apps by the same publisher. I quickly explained that I had developed managed applications before and that I knew that this was a feature created by salesforce.com, and therefore salesforce.com was responsible for the broken link. He said that he understood and that he would escalate the case to Tier 2.

Quick recap so far: Salesforce Support has responded quickly, so they get good points for that, but I’ve not been available to talk. The support rep has tried everything he and I can think of, and we have found a problem in a link that directs to the wrong URL. I’m up to Tier 2.

At this point, I don’t have any more communication until June 8. I’m not fussed, as I know it’s a real issue, but I have a good workaround (use the Back button).

June 8, GE sends me an email saying that he reproduced the error and escalated it to Tier 3 for further investigation. I thanked him and said that it felt bittersweet because I had found a real problem and it’s no fun when my beloved Salesforce CRM has a bug. He validated my feelings. (Warm fuzzies all around.)

The next day, GE sends an email. This is the text:

An issue tracking number has been attached to your case, and is awaiting prioritization by R&D. I will receive updates on this, but the timeline for rollout may not be known for some time. Just wanted to let you know that it has been queued up for a fix, and I will let you know more when I know. Feel free to check in any time, and also bear in mind that there is a very good workaround for this, being the browser back button J We’re just required to mention workarounds, even though I know you know this. Let me know if you need anything else.

Music to my ears! An issue tracking number! R&D looking into it! I had found a real bug and it would be fixed!

The point of all this is to say that for all the complaints (many justified) that people have about salesforce.com support (why do they automatically want login access when the problem sometimes has nothing to do with anything in an org???) there are times that they really shine.

Thank you, JW and GE. At least I know a fix is coming.

Sophisticated DateTime “Formula Fields” with Apex and Field-Level Security

What do you do when you want to calculate a formula-like field but a regular formula won't work?

Salesforce CRM's formulas handle dates very well. If you want to enter a date value and have formula fields display, for instance, mydate__c + 21 days, that's simple. Just use mydate__c + 21.

Side note: If you try going the long way around and use DATE( YEAR( mydate__c ), MONTH( mydate__c ), DAY( mydate__c ) + 21 ) and mydate__c = 09/17/2010, Salesforce returns #Error! because there's no date 09/38/2010. Similarly, adding three months to a date like 1/31/2010 will also give an error. More about this in a future post.

DateTime fields are like Date fields, but they include... wait for it... a time component (and can be created in the running user's local time zone or in GMT).

Here's a use-case for a DateTime formula field:

A photography studio schedules photo shoots, and different packages include different durations. Similarly, we could use a hair salon which offers different services, each with a different duration, a dentist... you get the idea.

Requirements:

  1. Enter a DateTime for an appointment start time (starttime__c)
  2. Enter a duration (though in a production system, I'd include a value on the Product2 sObject, we'll just enter a value here) (minutes__c)
  3. Display a read-only DateTime field with the end time (endtime__c)
  4. The end time must be read-only to all users, like any formula field

Here's what won't work:

  • A formula field won't work because there are no MINUTE(), HOUR(), SECOND() formula functions
  • Workflow won't work because it depends on formulas to fill new values for date/datetime fields

That leaves Apex. First, the configuration:

  1. Create DateTime field starttime__c
  2. Create DateTime field endtime__c
  3. Set endtime__c field-level security to Read-Only for all profiles
  4. Create Number (18,0) field minutes__c
  5. Create a trigger on the sobject

Here's the trigger:

trigger timeTrigger on TestObject__c (before insert, before update) {
    for (TestObject__c t : Trigger.New){
    	if(t.StartTime__c != null && t.minutes__c != null){
        datetime myDateT = t.StartTime__c;
        double d = t.minutes__c;
        Integer shootmins = d.intValue();
        if(mydateT != null && shootmins != null)
        	t.EndTime__c = myDateT.addminutes(integer.valueof(shootmins));
       	}
    }
} 

Regular readers will note that I do usually split triggers into a trigger and a class, but I've not done so here purely for the sake of brevity.

Here's the test code:

public without sharing class shootTimesTriggerTest {

    private static testMethod void ShootCalculateEndTime_PositiveTestCases() {
        TestObject__c to;
        TestObject__c l;    
        test.starttest();
        l = new TestObject__c (name = 'test');
        datetime myDateTime = datetime.newInstance(2008, 12, 1, 12, 30, 2);
        l.StartTime__c = myDateTime;
        l.minutes__c = 90;
        insert l;
        to = [SELECT id, EndTime__c FROM TestObject__c WHERE id = :l.id];
        datetime newDateTime = datetime.newInstance(2008, 12, 1, 14, 0, 2);
        system.assertequals(to.EndTime__c, newDateTime);
        l.minutes__c = 45;
        update l;        
        to = [SELECT id, EndTime__c FROM TestObject__c WHERE id = :l.id];
        newDateTime = datetime.newInstance(2008, 12, 1, 13, 15, 2);
        system.assertequals(to.EndTime__c, newDateTime);
        test.stoptest();
    }

    private static testMethod void OppCalculateEndTime_NegativeTestCases() {
        test.starttest();
        TestObject__c l = new TestObject__c (name = 'test');
        l.minutes__c = null;
        insert l;
        system.assertequals(l.EndTime__c, null);
        test.stoptest();
    } 
}

A few points about how this works:

  • Triggers run in System mode, so they don't respect field-level security. Thus, we can set a field to read-only for all profiles, and the EndTime__c field will still be updated.
  • The test code runs in System mode as well, avoiding any potential problems if the field were set to invisible to a profile and we used System.RunAs() to test for various profiles.
  • Although I'm not a fan of using SOQL queries this often, I used these in the interest of saving time. Keep in mind that if you had quite a few queries in your regular code, adding these two might put you over the limit, so use queries sparingly!
  • This is the only way I know of to add minutes to a DateTime.

Did I miss anything? Please let me know in the comments.

Next Page »