When Salesforce is life!

Category: Post Page 20 of 27

[Salesforce] The Sobject Crusade: ActionLinkTemplate

Back to the Sobject Crusade list.

Source: ActionLinkTemplate

Action Links has been presented when discussing the ActionLinkGroupTemplate object (see this object for a complete guide on configuring and using Action Links): they are a way to execute custom actions on button click on Chatter feed posts.

By templating an Action Link you define a template action which can be “instantiated”.

The Action Link is defined by:

  • LinkType: this is the kind of action that you want to expose, and it can be:
    • Api: direct call to an API endpoint (with given method, parameters, headsrs)
    • Api Async: like the previous point but the server response is asyncrhonous: this way the serve have to send a response to /connect/action-links/[actionLinkId] endpoint to set the SuccessfulStatus o FailedStatus of the operation
    • Download: triggers a download of a file
    • Ui: redirects to the given URL
  • ActionUrl: endpoint of the action. This could have bindings values, e.g. “https://myapi.com/id={!Bindings.customValue}&userId={!userId}”
  • Headers: headers request values (only for Api and ApiAsync), binding supported. Every header value should be in the form (one per line) “Header-key: value”
  • Method: HTTP method for the request, with the following supported behavior:
    • HttpDelete: HTTP 204 on success, response body is empty
    • HttpGet: HTTP 200 on success
    • HttpHead: HTTP 200 on success, response body is empty
    • HttpPatch: HTTP 200 on success or HTTP 204 if response body is empty
    • HttpPost: HTTP 201 on success or HTTP 204 if response body is empty
    • HttpPut: HTTP 200 on success or HTTP 204 if response body is empty

    “Ui” and “Download” types support only “HttpGet” value.

  • RequestBody: bpdy of the request (only for Api and ApiAsync), binding supported
  • LabelKey: standard set of labels (None if none apply, then use the Label field)
  • Label: custom label associated to the action (Pending, Success and Failed applied to the action action)
  • Position: position of the action in the defined group (0 is the first)
  • IsConfirmationRequired: users is prompted with a confirmation alert box upon click on the action
  • IsGroupDefault: default group action
  • UserVisibility: states who can see the action, that is Creator, Everyone, EveryoneButCreator, Manager (manager of the creator on the Action Link), CustomUser or CustomExcludedUser (using the UserAlias field to link to a specific user)
  • UserAlias: user alias to set the visibility of the action. Supports binding
  • LinkType:
  • LinkType:

You can query this object:

Select Id, ActionLinkGroupTemplate.DeveloperName, LinkType, ActionUrl, Headers, IsConfirmationRequired, Label, Method, RequestBody From ActionLinkTemplate

And get:

[Salesforce] The Sobject Crusade: ActionLinkGroupTemplate

Back to the Sobject Crusade list.

Source: ActionLinkGroupTemplate

Action Links are actions you can do in chatter posts (e.g. API call, document download, UI redirect) by clicking a button on the related chatter post.

Action Links are grouped into Action Link Group, so you can choose which action you want to play:

You may think that these actions comes to configuration and some sort of layout, but it is part configuration and part Chatter API call.

Action Links are grouped into Action Link Groups but this is not the object that will be configured.

In fact we talk about Templates (Action Link Group Template and Action Link Template): we define template actions rather than the actual actions we’ll be executing.

The template defines the actions you want to apply to a Chatter feed: when you want to apply the group of actions to a feed, we’ll make an API call to get the “instance” of the actions to be linked to the feed.

Let’s start creating a Group Template (the object we are inspecting here) clicking on Setup > Create > Action Link Templates:

You can define:

  • Name and DeveloperName
  • Category (position of the action buttons in the feed)
  • Number of execution allowed
  • Hours untile the action expires

Once a group is published it cannot be modified anymore.

Let’s query the object:

select id, DeveloperName, IsPublished, MasterLabel  from ActionLinkGroupTemplate

Let’s define the actions:

With the following main fields (more details here):

  • Type of Action (API, API Async, UI, Download)
  • Endpoint of the action (in this example a simple request bin)
  • HTTP Method
  • HTTP Request Body
  • HTTP headers
  • Position among other buttons
  • Label (you can choose between some presets and a custom label)
  • Visibility

Consider that URL, request body and headers can have standard and custom binding values (you can for example pass the current User ID or a custom binding value).

These are the actions defined:

To use them we need a valid Session ID, a call to the /connect/action-link-group-definitions/ API endpoint and a call to Chatter API to send a feed.

We’ll be doing things manually (we could use Workbench, but we like to get our hands dirty!).

First we get a valid OAuth Session ID.

Click on Setuo > Create > Apps click on New button in the Connected Apps section; we only need to setup the middle section:

to get access to Chatter API.

This is what you get:

This application will be online in 10 minutes at most.

Let’s make a REST call to get a valid token using the OAuth Password Flow (details here ):

POST https://login.salesforce.com/services/oauth2/token
    Headers:
        Content-Type: application/x-www-form-urlencoded
    Body:
        grant_type=password&client_id=[APP_CONSUMER_KEY]&client_secret=[APP_CONSUMER_SECRET]&username=[USERNAME]&password=[PASSWORD+TOKEN]

The response returns the Session ID (athorization token) we’ll use to get a valid Action Group Id from this REST call:

POST https://xxx.salesforce.com/services/data/v34.0/connect/action-link-group-definitions/
    Headers:
        Authorization: Bearer [SESSION_ID]
        Content-Type: application/json
    Body:
        {"templateId":"07g24000000CaTc","templateBindings":[]}

Where templateId is the ID of the Action Link Group Template (you can query for it or get it in the configuration section of the template), and templateBindings is a list of objects (with “key” and “value” fields) for custom bindings.

This call will return an instance of the template:

{
  "actionLinks": [
    {
      "actionUrl": "https://requestb.in/11jyqzq1",
      "createdDate": "2015-08-14T20:41:42.566Z",
      "excludedUserId": null,
      "groupDefault": false,
      "headers": [
        {
          "name": "content-type",
          "value": "application/json"
        }
      ],
      "id": "0An240000004FSDCA2",
      "label": "Salute!",
      "labelKey": "None",
      "method": "HttpPost",
      "modifiedDate": "2015-08-14T20:41:42.566Z",
      "requestBody": "{"message": "Hello!", "from":"{!userId}"}",
      "requiresConfirmation": false,
      "templateId": "07l24000000GmbgAAC",
      "type": "Api",
      "userId": null
    },
    {
      "actionUrl": "https://requestb.in/11jyqzq1",
      "createdDate": "2015-08-14T20:41:42.566Z",
      "excludedUserId": null,
      "groupDefault": false,
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        }
      ],
      "id": "0An240000004FSECA2",
      "label": "Insult!",
      "labelKey": "None",
      "method": "HttpPost",
      "modifiedDate": "2015-08-14T20:41:42.566Z",
      "requestBody": "{"message":"Bad Word", "from":"{!userId}"}",
      "requiresConfirmation": true,
      "templateId": "07l24000000GmblAAC",
      "type": "Api",
      "userId": null
    }
  ],
  "category": "Primary",
  "createdDate": "2015-08-14T20:41:42.566Z",
  "executionsAllowed": "Once",
  "expirationDate": null,
  "id": "0Ag240000004FSNCA2",
  "modifiedDate": "2015-08-14T20:41:42.566Z",
  "templateId": "07g24000000CaThAAK",
  "url": "/services/data/v34.0/connect/action-link-group-definitions/0Ag240000004FSNCA2"
}

Now we can use the id to post the action with a feed:

POST https://xxx.salesforce.com/services/data/v34.0/chatter/feed-elements
    Headers:
        Authorization: Bearer [SESSION_ID]
        Content-Type: application/json
    Body:
        {
          "body": {
            "messageSegments": [
              {
                "type": "Text",
                "text": "What do you want to say me?"
               }
            ]
            },
          "subjectId": "me",
          "feedElementType": "FeedItem",
          "capabilities": {
            "associatedActions": {
              "actionLinkGroupIds": ["0AgRR0000004CTr0AM"]
            }
          }
        }

Once you click on a button, the action is executed and no more action is possible:

The same sequence can be done using Apex and the ConnectApi library:

// Get the action link group template Id.
ActionLinkGroupTemplate template = [SELECT Id FROM ActionLinkGroupTemplate WHERE DeveloperName='Say_Hello'];


// Create ActionLinkTemplateBindingInput objects from the map elements.
List bindingInputs = new List();
/*
// Add binding name-value pairs to a map: our action does not have custom bindings
Map bindingMap = new Map();
bindingMap.put('aBinding','aValue');
for (String key : bindingMap.keySet()) {
    ConnectApi.ActionLinkTemplateBindingInput bindingInput = new ConnectApi.ActionLinkTemplateBindingInput();
    bindingInput.key = key;
    bindingInput.value = bindingMap.get(key);
    bindingInputs.add(bindingInput);
}
*/

// Set the template Id and template binding values in the action link group definition.
ConnectApi.ActionLinkGroupDefinitionInput actionLinkGroupDefinitionInput = new ConnectApi.ActionLinkGroupDefinitionInput();
actionLinkGroupDefinitionInput.templateId = template.id;
actionLinkGroupDefinitionInput.templateBindings = bindingInputs;

// Instantiate the action link group definition.
ConnectApi.ActionLinkGroupDefinition actionLinkGroupDefinition = 
ConnectApi.ActionLinks.createActionLinkGroupDefinition(Network.getNetworkId(), actionLinkGroupDefinitionInput);

This is what the bin has received:

Imagine you can use this feature for allowing selected users to partecipate to surveys/polls or make them doing actions to monitor their behavior.

[Salesforce / Heroku] Salesforce as Identity Provider and Heroku as Service Provider

One year from my last SSO blog post (Implementing Delegated SSO) I’m still here playing with Single Sign On, this time using SAML 2.0.

For a quick overview of the how SAML works, read SAML FEDERATION FOR DUMMIES and SAML for dummies.

We have 3 actors:

  • Client: this is your browser
  • Service Provider: this is the remote Site where you need to access your private data/services without having to log in in this portal (this is Heroku in our demo implementation)
  • Identity provider: this is the server that hosts your authentication details, which authenticates your private sessions on the Service Provider (this is Salesforce)

These actors speak to eachother to allow the client to be authenticated both in the Service Provider and the Identity Provider.

The aim of this article is to configure Salesforce as an Identity Provider using a NodeJS app hosted on Heroku as Service Provier.

Let’s say we have a Customer Community and every user should access a remote Service Provider without the need to log in again.

First let’s configure a new Community

From Setup > All Communities click on New Community, select the Salesforce Tabs + Visualforce template, select a name:

Click on the Administration tab and then to Members. Leave the System Administrator and add a customer community profile:

Click on Branding to change header image and color theme.

Click on Login & Registration to change header logo of login page and other login/logout settings.

You have setup a new community:

Setup Salesforce as Identity Provider

The next step is to setup Salesforce as Identity Provider.

Enable My Domain from Setup > Domain Managerment > My Domain.

Remember that once the My Domain subdomain is setup you cannot change it anymore.

By deploying the domain you enable it for all your ORG’s users:

  • Users will be able to login from https://yourdomain.my.salesforce.com/login
  • if you don’t change the base settings, users will still be able lo login through https:/login.salesforce.com
  • Integrations will continue to work without any problem

You can even change login brand:

Now go to Setup > Security Controls > Identity Provider click Enable Identity Provider.

Single Sign On is almost here!

For every community and the base Salesforce Identity you are provided with the SAML descriptor to configure Single Sign On on the remote Service Provider.

There is one more step to properly configure the authentication channel.

Create a connected app

From Setup > Create > Apps click on New button of the Connected Apps section.

Fill in the mandatory fields:

  • Connected App Name: name of your app
  • API Name: dev name of the app (cannot be changed once set)
  • Enable SAML: true
  • Entity Id: this is the unique identifier of your Service Provider. This field is unique across Connected Apps (any other connected app should have a different value)
  • ACS URL: this is the callback URL on your Service Provider (we’ll be configuring the app in the next chapter)
  • Subject Type: set to User ID (your user will be identified by its Salesforce ID)
  • Name ID Format: this is the format of the unique id (urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified)
  • Issuer: leave the default value

Another cool feature is the Custom Connected App Handler: you can specify an Apex class which is called when the authentication is done.

The class must extend the Auth.ConnectedAppPlugin class:

global class SamlConnectedAppPlugin extends Auth.ConnectedAppPlugin{

    global override boolean authorize(Id userId, Id connectedAppId, boolean isAdminApproved) {
        User u = [select id, First_SAML_Login__c from User where id =: userId].get(0);
        if(u.First_SAML_Login__c == null){
            u.First_SAML_Login__c = System.now();
            try{
                update u;
            }catch(exception e){
                System.debug('Exception:' + e.getMessage());
            }
        }
        return true;
    }


    global override Map customAttributes(Id userId, Map formulaDefinedAttributes) {  
        User u = [select id, Name, CommunityNickname from User where id =: userId];
        formulaDefinedAttributes.put('nickname',u.CommunityNickname);
        formulaDefinedAttributes.put('fullname',u.Name);
        return formulaDefinedAttributes;
    }
}

Where:

  • authorize(): authorized user based on custom login (e.g. you can query for a given User’s field)
  • customAttributes(): allow to specify a set of custom attributes to be sent to the Service Provider. In this example the send the user’s full name and nickname
  • refresh(): (not implemented here) is called upon token refresh (OAuth 2.0)

Configure the Service Provier

The service provider is the external App that provides a service which can be accessed only if the user if authenticated.

It will be using Salesforce as the Identity Provider (don’t you say??).

To simulate the Service Provider, here is a NodeJS app hosted on Heroku:



By clicking the Heroku Button you are redirected to the Heroku app creation:

In the Config Variables lays down the magic:

  • SAML_ENTRYPOINT: set with the value of the node md:SingleSignOnService Binding=”urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect” of the SAML descriptor for the community
  • SAML_ISSUER: set with the name you have setup in the Entity Id of your connected app
  • SAML_CERT: copy the whole certificate string in the node ds:X509Certificate of the SAML descriptor of your community
  • SAML_IDENTIFIER_FORMAT: leave with the default value urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
  • APP_NAME: set a cool name for your service provider
  • SF_HOME: this is the base URL for your community

Then click the Deploy For Free button and wait for the magic to take place!

Your Heroku app is up and running at https://heroku_app_name.herokuapp.com

Link the Service Provider in your community

Let’s add a simple link to the community sidebar.

Click to Setup > Customize > Home > Custom links:

Create a new Home Page Component:

And add it to a new Home Page Layout tailored to your community:

Let’s test it out!

Page 20 of 27

Powered by WordPress & Theme by Anders Norén