Monday, December 19, 2016

Thumbs Up/Down ("Solution Helpfulness Rating") Feature for Knowledge with Visualforce/Apex

This is a feature that came out without too much fanfare and was supposedly meant to address an idea.  The only place that mentioned how to use it in Salesforce with details was in Summer '15 Release Notes, when it came out as beta.  Then there's an article about using it with Community templates, in which it's referred as "Article Voting".  None of them gives any clue on how it can be used in a Sites based Public Knowledge Base, which happens to be the use case I need to handle. 

After some back and forth with Salesforce Support, they pointed me to this Developer Forum thread that talks about Vote object for Idea.  I did some simple code to verify, and I could indeed set parentId to __ka objects and programmatically manipulate up/down vote state.  That, was not mentioned at all in the official doc for the Vote object (as of Dec 2016, ver. 38.0).

With almost all the pieces in place, one hurdle remains for to a public KB Up/Down vote solution in VF/Apex, if the KB is used in an unauthenticated context.  Vote object doesn't allow vote on the same parentId more than once by the same user.  If the users are unauthenticated - basically always Public Site Guest User - then votes can't really be recorded for more than once.  That's hardly useful.  I have no workaround so far, other than building a custom solution without using Vote.  That would suck if you'd really want internal and external votes are counted and reported together.

Tuesday, April 5, 2016

Use Apex @InvocableMethod with Process Builder

It's one great way to marry the versatility of Apex to implement complex logic and the easy administration of Processes.  Say you want to auto-unfollow old Contracts a period of time after they're terminated for those who manage a lot of contracts.  It's pretty straightforward to do the unfollowing in Apex, then use Process to invoke the action and leave it to the administrators to control how long the wait period should be.  Bob Buzzard has a good piece on the general pattern, and info about @InvocableMethod can be found here.

The only thing I found a bit non-intuitive is how parameter is passed to the Invocable Method, which only takes List of arguments.  Process Builder, on the other hand, doesn't have an obvious way to create an array/list variable.  It turns out that all you need is to set a single value of the correct type (of the List element).  For instance if the method expects a list of IDs, just set a single ID, which is called reference in Process Builder.

"scids" is the argument expected by the Apex method.

    @InvocableMethod(label='Chatter unsub for Service Contracts' description='Unfollow terminated/cancelled service contracts for PMM team')
    public static List UnfollowSCs(List scIds) {

Monday, February 29, 2016

Apex code example for Kayako signature

Was looking at Kayako's API documentation - don't ask me why - and noticed there's no Apex example for their API request signature scheme (why would they?), so this might be useful for people who's in the same situation as I'm.

public class KayakoAPI {

    public static string getSignature(string secretKey) {
        string signature;

        if (secretKey!=null && secretKey.length()>0) {
            Blob salt = crypto.generateAesKey(128);  //can use other ways for random string generation

            // keyed hash
            Blob hmacsha256 = crypto.generateMac('HmacSHA256', salt, Blob.valueOf(secretKey));

            // Base64 and URL encoding
            signature = EncodingUtil.urlEncode(EncodingUtil.base64Encode(hmacsha256), 'UTF-8');

        return signature;

Wednesday, November 11, 2015

Salesforce AJAX Toolkit update call fails *silently* with field access issue

Some simple button code with Salesforce Ajax Toolkit (connection.js) to send a lead to queue (and related assignment logic in Apex):

var updateRecord = new Array();
var myquery = "SELECT Id, CompleteOwner__c FROM Lead WHERE Id = '{!Lead.Id}' limit 1";

var result = sforce.connection.query(myquery);
var records = result.getArray("records");

if (records[0] && (records[0].CompleteOwner__c === undefined || records[0].CompleteOwner__c === null)) {
    var update_lead = records[0];

        update_lead.OwnerId = "{!$Setup.CompleteQId__c.Id__c}"; /* To complete Q */

        var callback = {onSuccess: updated, onFailure: failed}
        sforce.connection.update(updateRecord, callback);
The trouble is, the update call returned successfully, but update didn't happen at all - I couldn't even capture the API call with a debug log.

It turns out the trouble is the CompleteOwner__c field.  If that's taken out, the code works fine.  The user has no write permission to the field, but, the code isn't updating it either.  Apparently merely having that field member in the record data structure prevents the update from taking place, without any failure message.  Changing the highlighted part to this, which uses an new object for update without that field member, fixed the problem.
var update_lead = new sforce.SObject("Lead"); //can't use query result for update = "{!Lead.Id}";

update_lead.OwnerId = "{!$Setup.CompleteQId__c.Id__c}"; /* To complete Q */
 The frustrating part is the silent failure.  Why not just fail the call when there's a non-writeable (by permission) field? 

Tuesday, June 16, 2015

Biometrics Software is not Working with PGP Desktop

That's the title of a Symantec KB article. It's true.  I got the biometrics software from Broadcom, which is supplied by Dell for their fingerprint reader on Latitude E6230.  Following the instructions here after a system rebuild due to a bad BSOD, I was able to get the biometrics set up and working (seemingly), or so I thought.  Then it broke down.  What happened was, when a fingerprint was scanned correctly, the fingerprint icon just disappeared from the login screen without letting me in.  It's almost hilarious.  So, having a full-disk encryption for now prevents me from using fingerprint as authentication. 

What's worth mentioning is that, before I had the fingerprint setup working, then had full-disk encryption retrofitted in, the fingerprint setup stayed working.  This time around encryption is in first, and they could no longer work together.

Monday, June 15, 2015

Recovering workspaces: Eclipse vs Sublime Text

My PC's OS had to be reinstalled recently, so I had to get my IDE in order before starting to work again on  Eclipse has always been pretty easy - both my binary and workspaces were covered by Box Sync, so it took almost no effort.  Once everything was sync'd back, I could simply run Eclipse and point it to the right workspace.

MavensMate on Sublime wasn't nearly as easy.  First I had to reinstall Sublime (3) + MavensMate (4.0.5), which wasn't too bad.  After that I edited MM's setting to use the correct workspace, which got all my projects back.  However, none of them actually worked anymore.  The context menus were just gone.  Not wanting to spend time on figuring out what's wrong, I just went ahead recreate the projects.  No a huge deal, but kinda annoying.

Friday, January 16, 2015

Enable Visualforce pages for Salesforce1

It's a fairly simple process.  Barring any unsupported stuff on the VF, it should just work.  However I've run in a timeout issue on more than one occasions - clicking on Save ended with a timeout page and the change was never saved.  Had no idea why and didn't have the patience to open a case with Salesforce, so I took the developer route.

Here's how you can work around it if you can use developer tools (or any Metadata API enabled tool for that matter).  The enablement switch is implemented as this availableInTouch field on the ApexPage object.  All is needed is to flip it to true.  For instance if you have Eclipse, you'll notice the field as a member in the page-meta.xml file.

 <?xml version="1.0" encoding="UTF-8"?>  
 <ApexPage xmlns="">  

Quick code formatting provided by Source Code Formatter

Once flipped, save it (remember to Save to Server, as Eclipse doesn't build it to server for meta files) and you're all set.  Similarly this can be done with Ant as well.

Please note the field is only available API 27 or later, so make sure the tool connects using supported API versions.