Monday, September 25, 2017

[Salesforce / Chrome Extension] Happy birthday to the ORGanizer: 1 year on the store!

Checking my diary agenda I could not believe: ORGanizer's first go live was exactly 365 days ago!


I wanted to share my love and expertise for Salesforce in one single Chrome Extension to be free for all, and after 1 year I receive cheers from my Salesforce pals because ORGanizer helps them successing in their daily tasks!


Here are some quick numbers:

  • 20 releases
  • users from 127 countries
  • 4000 active users ca.
  • 28 new features (7 suggested by you)
  • 47 enhancements (17 suggested by you)
  • 32 bug fix (9 suggested by you)
  • 81500 login actions in the last month
  • 222 daily logins in the last month

The analytics (recently introduced) say that the numbers are rapidly increasing and the adoption rate is getting higher and higher.

The most important number is the active users:


And indicates the number of users that day after day keep getting the ORGanizer on their Google Chrome browsers...and it is incresing day by day, and I cannot be happier!

The ORGanizer is more than the extension itself.

Trying to make it the most amazing Chrome Extension ever, there an ecosystem of side projects and systems to help me achieving this aim.

ORGanizer site

This is the central information repository for the extension.

FAQ page

A complete extension guide updated at every release.


Video guides

Support

Active support site where you can report a bug or suggest a new feature.

This is linked to my Salesforce CRM org where I store all the stuff.
It also uses an Heroku app to send the email used to confirm your identity.

Next Release page

This page contains all features and bugfix in development or developed, that will be release in the next release.
Also this feature is related to my personal ORGanizer CRM org.

Change Log

A list of all features delivered in all releases.

Donations and Swag Store

Donations link and a link to the Swag Store to get some cool ORGanizer swag to allow me keeping the extension free for all.

Live Messages

Live messages to get live data to users about the extension, like unexpected bugs or general info.
I use ORGanizer like you do, so I wanted to put in place a feature to communicate with my ORGanusers.

Online reviews
I received amazing reviews from Salesforce community leaders:

Who made it?
I'm the only person behind the ORGanizer but my dear friend Davide D'Annibale is helping me with all the graphics (I litteraly have not taste in graphics :) ) to get it the most professional appearance it can have!

Amazing feedbacks
What's the best way to thank all supporters? Let's show some of the best tweets I got from the web!







Tuesday, September 19, 2017

[Salesforce / Lightning] Trigger automatic Lightning Quick Action popup close

Few days ago my #AwesomeAdmin buddy Tom Blamire asked me:

"Do you know if I can automatically close a Quick Action popup window after triggering an action that does not require a UI (like downloading something from a Visualforce page)?"

The flow was simple as he asked me: click on a quick action on a standard layout page (with Lightning Experience enabled), trigger something (that can be a call to a Visualforce page or simply a backend elaboration from Apex) and close automatically the popup.

This is the solution: in this case the setTimeout() call was necessary because you need to get a bit "asynchronous", but if you make an Apex controller callback, you don't need it.

Thanks to Salesforce Lightning framework it is easy as it seem:



Friday, September 8, 2017

[Salesforce] Mason Frank Salesforce Salary Survey: you play the game!

It’s that time of year again. The annual Salesforce Salary Survey is now open!

Mason Frank, global Salesforce recruiters, have begun collecting responses for the their annual Salesforce salary survey!

The survey is formed through collecting insights from people working with Salesforce all over the world and forms to create a crucial resource for the Salesforce industry.

How much should you be earning in your job?

How do your benefits and perks compare?

Which factors can advance your earning potential?

What is your Salesforce working culture really like?

If you work with Salesforce, helps make the survey as accurate and representative as possible by contributing your answers.

Take the survey and you'll also be entered for a chance to win either a Nintendo Switch or Apple Watch 2!


The survey should take you no longer than 15 minutes and will close on the 29th September 2017.

Mason Frank will be launching the results at Dreamforce 2017 and your own copy of the report will be sent directly to you.

Thank you for your input and good luck!

Monday, August 7, 2017

[Salesforce / Apex] Cleanse your data with super powers: Execute Anonymous Batch

Warning: use this only if you have super powers and you know exactly what you are going to do!

Developers are always lazy people, they prefer (love) writing scripts to handle data cleansing.

I rarely use Data Loader or massive CSV updates using other tools if I can, I always prefer to write my own scripts to update fields, if I'm asked to and, ofcourse, if it is possibile.

The only problem with this approach is that sometimes the logic you implemented in your script cannot be run for more than a couple of objects a time, requiring you to run your script on and on...and this is not good for lazy people.

Talking with my friend and colleague Tonone, he asked me if I had already did something like:

  • A way to execute an Apex script in batch style (hell yeah, this is an Apex batch class)
  • He didn't want to deploy in production every new script (well, this can't be a normal batch class)
  • He wanted to provide the algorithm dynamically (still nope in batch)

Just after telling him that he cannot execute an anonymous batch class from the console (or in the ORGanizer ;) ) in Execute Anonymous mode, I tought why don't put together a Batch class and an Execute Anonymous call?


To the idea behind this is to create a batch class that executes at each iteration a script provided that is executed in "anonymous" style across all the batch's scopes objects.

If you want to jump to the code, check out this Github repo.

If you want watch the tool in actiom jump to the Let's play section.

Getting a Session ID


The only way to invoke Salesforce APIs from a batch job is to make a login call from the script to obtain a Session ID or (easier) to create a named credentials logged with OAuth 2.0.

To get to this create a new Connected App (from Setup > Apps > Connected App):


Save the Consumer Key and Consumer Secret for next step, while the Callback URL will be filled with a fake url (e.g. http://fake.com).

Now create an Authorization Provider ( Setup > Auth. Provider):


Use your org's full address for the Authorize and Token Endpoint URLs (whether you have My Domain activated or not) and place Consumer Key and Consumer Secret of the previous step.

After saveing you have the callback URL needed to convalidate the OAuth 2.0 dance:


Take the Callback URL and put it in the same field of the Connected App.

Finally let's create the Named Credential (from Setup > Named Credentials):


After saving the credential you are requested to login with your user (you need admin access or at least an API enabled user to call execute anonymous feature).

The APi name of the credential must match the one provided in the ExecuteAnonymousBatch class:

public class ExecuteAnonymousBatch implements Database.Batchable<SObject>, Database.AllowsCallouts, Database.Stateful {
    private static final String API_VERSION = '40.0';
    private static final String NAMED_CREDENTIAL = 'EXECUTE_ANONYMOUS';

Now you can make API call from asyncrhrous jobs.

Let's play


Now you can use the tool in the following way:

String script = 'List acList = [Select Id, Name From Account Where Id IN :ID_LIST];' 
    +'for(Account acc : acList){'
    +'   acc.BillingCity = \'Gnite City\';'
    +'}'
    +'update acList;';

String query = 'Select Id From Account Where BillingCity != \'Gnite City\'';

Boolean sendEmailOnFinish = true;

ExecuteAnonymousBatch batch = new ExecuteAnonymousBatch(query, script, sendEmailOnFinish);
Database.executeBatch(batch, 200);

Where:
  • query: is the Batch's query
  • script: is the script you want to execute per batch execution
  • sendEmailOnFinish: send or not an email to the executing user in the finish method

The only thing you have to take care is to write a script that is compilable and that uses the ID_LIST variable that is injected in your script and that is of type List<ID> and that contains the IDs of all objects in the execution scope.

You can launch the script right from your tab with the Quick Console of the ORGanizer:


Results


At the end you'll receive a marvellous email with query, script and elaboration erros (if any):

Subject: 
 [Execute Anonymous Batch] Elaboration completed: with 3 errors.
 
Body:
 Query:
         Select Id From Account limit 40
 Execute anonymous code:
         List<Account> acList = [Select Id, Name From Account Where Id IN :ID_LIST];
         for(Account acc : acList){   acc.BillingCity = 'Gnite City';} 
         update acList;
 Errors:
         '00124000005FoHPAA0': System.DmlException: Update failed. First exception on row 0 with id 00124000005FoHPAA0; first error: FIELD_CUSTOM_VALIDATION_EXCEPTION, Account cannot be updated: [] -- AnonymousBlock: line 2, column 1
         '0012400000ZBRAGAA5': System.DmlException: Update failed. First exception on row 0 with id 0012400000ZBRAGAA5; first error: FIELD_CUSTOM_VALIDATION_EXCEPTION, Account cannot be updated: [] -- AnonymousBlock: line 2, column 1
         '0012400000ZBRJNAA5': System.DmlException: Update failed. First exception on row 0 with id 0012400000ZBRJNAA5; first error: FIELD_CUSTOM_VALIDATION_EXCEPTION, Account cannot be updated: [] -- AnonymousBlock: line 2, column 1


 Sent by: [email protected]

Behind the scenes


There is not trick behind this tool, it's just putting knowledge over knowledge.

The only thing worth to note is that I've used the Execute Anonymous action from the Metadata API. Why not the REST API?

Only because the SOAP version give you the debug log in response if you add the proper headers, while the REST API version needs you to query the log.

To ease script writing you can put your scripts in a File, Document or Static resource, load it by hand and pass it to your script execution.

Document scriptDoc = [Select Body From Document Where Name = 'My_Cleanse_Script'];

String query = 'Select Id From Account';

Boolean sendEmailOnFinish = true;

ExecuteAnonymousBatch batch = new ExecuteAnonymousBatch(query, scriptDoc.Body.toString(), sendEmailOnFinish);
Database.executeBatch(batch, 200);

That's it!