суббота, 26 ноября 2016 г.

Flexible Salesforce Site.UrlRewriter implementation

I have recently developed a flexible and useful (I believe) implementation of UrlRewriter for Force.com sites.

Simply define routes as a pattern-target pairs. Each route can be defined from pattern String and target PageReference. Pattern can consist of regular parts as well as of named parameters starting with colon. Parameters would be passed as GET parameters of target Page Reference.

Usage example:
global class SiteUrlRewriter implements Site.UrlRewriter {

    List<Route> routeList = new List<Route> {
        new Route('/account', Page.SiteAccountsList), // --> /SiteAccountsList
        new Route('/account/:accountId', Page.SiteAccountView), // --> /SiteAccountView?accountId=0016C000003eH88
        new Route('/account/:accountId/edit', Page.SiteAccountEdit), // --> /SiteAccountEdit?accountId=0016C000003eH88
        new Route('/account/:accountId/contacts/:regionId', Page.SiteAccountContactsList) // --> /SiteAccountContactsList?accountId=0016C000003eH88&regionId=US

    // ...


In this case the following rewriting will occur:

/account => /SiteAccountsList

/account?directParameter=test => /SiteAccountsList?directParameter=test (direct parameters are preserved as well)

/account/0016C000003eH88 => /SiteAccountView?accountId=0016C000003eH88

/account/0016C000003eH88/edit => /SiteAccountEdit?accountId=0016C000003eH88

/account/0016C000003eH88/contacts/US => /SiteAccountContactsList?accountId=0016C000003eH88&regionId=US

Get code or install in one click from the GitHub repo. Issues and pull requests are welcome!

понедельник, 21 ноября 2016 г.

The easiest way to have iFrames inside your Salesforce Lightning Community

As you know, Salesforce Lightning communities don't support Web Tabs / iFrames natively.

I developed a Lightning Component for Salesforce Communities (see on GitHub) to embed iFrames easily:

It gives administrator an ability to configure iFrames in a few clicks:

There still are some restrictions:

  • Framed page should be HTTPS only due to Salesforce Lightning limitations;
  • Page inside iFrame should support framing (no X-FRAME-OPTIONS DENY or SAMEORIGIN header).
Install in one click from my GitHub here:

четверг, 15 октября 2015 г.

Introducing concat – pretty Apex string builder for Salesforce/Force.com

Concatenating strings in Apex with "+" operator  or String.join() can be full of pain. Unwanted spaces and commas. Empty or null strings. Ugly cascades of checks. Familiar?

Concat provides a solution!

It's key features are:

  • Null- and empty string safe
  • Clear chaining syntax

String result = 
concat.strings('Apex', 'can', 'be').add('less').add('').

Pretty easy, huh?

среда, 14 октября 2015 г.

Force.com Fundamentals 30 Questions Quiz to Prepare Salesforce Certification

Salesforce Certifications are widely recognized qualifications that prove to employers or customers that you are a professional in a specific Salesforce area.

Checking you knowledge of platform basics is necessary before diving into preparation to specific certification. I've created a quiz of 30 questions covering this need. Force.com Fundamentals test checks your knowledge of native functionality of the Force.com platform, including designing and creating Force.com objects, modifying the appearance of the standard Force.com interface, the Force.com security scheme, reporting on your data, using workflows and more. This quiz can be useful for Salesforce developers and administrators starting certification process.

Questions are based on Force.com Platform Fundamentials book. Reading this book is highly recommended before starting the quiz.

Good luck!

вторник, 13 октября 2015 г.

Solution to display Salesforce user profile photos on external Force.com sites without authentication

Displaying Salesforce user profile images on external sites is a wide known problem. Access to User.SmallPhotoURL or User.LargePhotoURL needs authentication and that's the trouble. There are various solutions provided by different Salesforce developers, for example:
  • authorizing via OAuth by attaching "?oauth=[your-token]" to photo url - seriously?.. send valid access token to unauthorized client?
  • saving user photos as custom objects attachments - pretty tricky to implement and really ugly solution.
But there is a simple and 100%-working solution (thanks to Denis from Synebo)!

public String getUserPhotoURL(String UserId) {
    ConnectApi.Photo ph =  ConnectApi.ChatterUsers.getPhoto(null, UserId);
    return ph.fullEmailPhotoUrl;
 Notice, that resulting URL expires 30 days after it was obtained. It doesn't matter if you are using it on dynamic pages (since you are requesting a new URL every time when page is reloaded), but keep it in mind when using URL in some static stuff (emails, static pages, etc).

вторник, 6 октября 2015 г.

TriggerTransaction Apex class for cleaner Salesforce triggers

My newly written Salesforce Apex TriggerTransaction class allows you to accumulate changes made to DB in trigger code to minimize DML requests number. Tastes better with kevinohara80/sfdc-trigger-framework.


You need my xforce utility class to use TriggerTransaction.

Usage example

This example uses kevinohara80/sfdc-trigger-framework.



Create new Apex Trigger named AccountTrigger.apxt with the following code:

trigger AccountTrigger on Account (before insert,
                                   before update,
                                   before delete, 
                                   after insert,
                                   after update,
                                   after delete,
                                   after undelete) {

      new AccountTriggerHandler().run();



Trigger handler class

Create new Apex Class named AccountTriggerHandler.apxc with the following code:

public class AccountTriggerHandler extends TriggerHandler {

    public override void afterInsert() {
        TriggerTransaction transact = new TriggerTransaction 
                new List<String> {'Account', 'Contact', 'Opportunity'});
        for(Account account_i : (List<Account>) Trigger.new) {
            transact.ups('Contact', new Contact(AccountId = account_i.id,
                                                        +' Main Contact'));
        List<Contact> shadowList = transact.shadow([select id, lastName
                                                    from Contact limit 100]);
        X.debug('Shadow contact list', shadowList);