Salesforce – ABSYZ https://absyz.com Salesforce Gold Consulting and Implementation Partner Fri, 27 Nov 2020 10:59:42 +0000 en-US hourly 1 https://absyz.com/wp-content/uploads/2020/06/cropped-favicon-1-1-32x32.png Salesforce – ABSYZ https://absyz.com 32 32 REST API call from Einstein Analytics Dashboard https://absyz.com/rest-api-call-from-einstein-analytics-dashboard/ https://absyz.com/rest-api-call-from-einstein-analytics-dashboard/#comments Tue, 26 May 2020 06:22:34 +0000 http://blogs.absyz.com/?p=11219

In Einstein Analytics we can create a lens and dashboard with the dataset available in your analytics studio. You have a dataset that is created from the dataflow which is scheduled every hour to update the dataset. Here your dataset might not have the updated data for every second or minute. What if you want to get the live data from Salesforce or any external system or handling complex solution when it is not possible in analytics to be shown on your dashboard. Using the apex step type in Dashboard JSON we can achieve displaying the manipulated data or real-time data.

Using SOQL from Dashboard

Scenario 1: The user wanted to show the real-time data from salesforce on the analytics dashboard. It is simple to achieve, just follow the steps mentioned. So we create an apex step in the dashboard and using ‘@restresource’ in apex call we fetch the data to the dashboard. Create an apex class as AnalyticsDashboardStep with restResource name as accountdata. Define a @HttpPost method that will return the value to the dashboard JSON. So your code will look as shown below.

@RestResource(urlMapping='/accountdata')
global with sharing class AnalyticsDashboardStep {
    @HttpPost 
    global static String fetchAccount(String selectedIndustry) { 
         //selectedIndustry - attribute value passed from the analytics Dashboard
         //return the output
    } 
}

Define a wrapper that parses the data to the dashboard JSON. The WrappedData wrapper creates a mapping between the parameters defined and the queried fields of account. The ReturnMetadata wrapper is to define the data type for each returned column. The ChartFormatJSON wrapper is to combine the data(rows) and header(columns).

    public class WrappedData{
        public String Account_Name;
        public String Account_Id;
        public String Account_Industry;
        public String Account_AccountSource;
        public Decimal Account_AnnualRevenue;
        public WrappedData(){}
        public WrappedData(Account data){
            this.Account_Name = data.name;
            this.Account_Id = data.Id;
            this.Account_Industry = data.Industry;
            this.Account_AccountSource = data.AccountSource;
            this.Account_AnnualRevenue = data.AnnualRevenue;
        }
    }
    public class ReturnMetadata {
        public List<String> strings; // columns that return as text
        public List<String> numbers; // columns that return as numeric
        public List<String> groups;  // columns that return as groups
        public ReturnMetadata(List<String> strings, List<String> numbers, List<String> groups) {
            this.strings = strings;
            this.numbers = numbers;
            this.groups = groups;
        }
    }
    public class ChartFormatJSON {
        public List<WrappedData> data;
        public ReturnMetadata metadata;
        public ChartFormatJSON(List<WrappedData> data) {
            this.data = data;
            this.metadata = new ReturnMetadata(new List<String>{'Account_Id','Account_Name','Account_Industry'}, 
                                                new List<String>{'Account_AnnualRevenue'}, new List<String>{'Account_Name'});
        }   
    }

Since your wrapper parameters are set up, now pass the values in the @HttpPost fetchAccount method as stated above. In the below method we have a queried List of accounts as a return statement to the Dashboard. For the complete class check this link.

    @HttpPost 
    global static String fetchAccount(String selectedIndustry) {
        List<Account> dataDisplay = new List<Account>();
        List<WrappedData> wrpData = new List<WrappedData>();
        // If the Industry is not selected from the interaction step
        if (selectedIndustry == null) {
            dataDisplay = [select Id,Name,Industry,AnnualRevenue,AccountSource from account order by AnnualRevenue desc];
        }else{
            dataDisplay = [select Id,Name,Industry,AnnualRevenue,AccountSource from account where industry=:selectedIndustry order by AnnualRevenue desc];
        }
        for(Account acc : dataDisplay){
            wrpData.add(new WrappedData(acc));
        }  
        //Serialize the wrapper that you have created with account data
        return JSON.serialize(new ChartFormatJSON(wrpData));
    }

Once your apex class is ready, move to the analytics studio to create a dashboard with an apex step. Create a toggle with filter values as Account Industry. Follow the below steps to create a dashboard.

In the new dashboard if you have completed the above steps, click ctrl + E in windows or command + E in Mac to edit your Dashboard JSON. Add the apex step as shown below. The GetChartData is the query name in the dashboard. In the query parameter, you set the body value as an apex input parameter named selectedIndustry that holds the value selected from the dashboard. Define the apex class name in the path parameter and apex in the type parameter.

"GetChartData": {
	"query": {
		"body": {
			"selectedIndustry": "Agricultrue"
		},
		"path": "accountdata"
	},
	"type": "apex"
},

If you want to pass the selected industry value dynamically then use interaction syntax that can be generated from the advance editor of the GetChartData query. Edit your query value with “{{cell(Industry_1.selection, 0, \”Industry\”).asString()}}” to pass the value dynamic. You can use both Result or Selection type of interaction.

"GetChartData": {
     "query": {
          "body": {
                "selectedIndustry": "{{cell(Industry_1.selection, 0, \"Industry\").asString()}}"
          },
          "path": "accountdata"
     },
     "type": "apex"
},

Click on Done after completing the JSON edit. You can see the GetChartData query in the query list on your right-hand side of the dashboard. Drag the query to the dashboard building area. To get the full dashboard JSON click here.

Dashboard Output:

dashboard output1

REST API call from Dashboard

Scenario 2: Similarly, the user wanted to show the live updates from an external system to your analytics dashboard, then we do an apex REST API call to fetch the details whenever the dashboard is loading or refreshing. Here we have taken an example of COVID ’19 to show the number of updated cases in India. So we are using https://api.covid19india.org API to fetch the COVID updated details. Similarly, you can choose your API according to your necessity.

It is similar to scenario 1, whereas here we are creating an apex class with REST API callout and passing the parameters in the same format that is required for the dashboard. Add the URL in remote site settings.

Create two custom labels with the details mentioned below:

  • Custom Label 1
    • Name: CovidBaseEndpoint
    • Value: https://api.covid19india.org/
  • Custom Label 2
    • Name: CovidStateWise
    • Value: /data.json

CustomLabel

The next step is to create an apex class to make an Http request and get the response. The getStateWiseData method makes an API request and is serialized to the dashboard through the data_val method. Notice PackagedReturnItem wrapper where we have categorized the columns as a string, number, and group in ReturnMetadata.

@RestResource(urlMapping='/covid')
global with sharing class CovidData {
    @HttpPost  // Annotation Specified to highlight that this method needs to be called.
    global static String data_val() {
    	CovidStatusCoreData1 data = getStateWiseData();
        return JSON.serialize(new PackagedReturnItem(data.statewise));
    }
    
    public static CovidStatusCoreData1 getStateWiseData() {
        String BaseEndpoint = System.Label.covidBaseEndpoint; //Retrieve the endpoint and statewise variable from custom label
        String StateWise = System.Label.covidStateWise;
        HttpResponse resp = makeAPICallout(BaseEndpoint,StateWise);
        CovidStatusCoreData1 response = (CovidStatusCoreData1)System.JSON.deserialize(resp.getbody(), CovidStatusCoreData1.class);
        if (response != null) {
            return response;
        }
        return null;
    }
    public static HttpResponse makeAPICallout(String BaseEndpoint,String StateWise) {
        Http h = new Http();			//Make a request with the parameters set
        HttpRequest req = new HttpRequest();
        String endpoint = BaseEndpoint + StateWise;
        req.setEndpoint(endpoint);
        req.setMethod('GET');
        HttpResponse res = h.send(req);		// Send the request, and return a response
        if (res.getStatusCode() == 200 ) {
            return res;
        }
        return null;
    }
    public class ReturnMetadata {
        public List<String> strings; 
        public List<String> numbers; 
        public List<String> groups;  
        public ReturnMetadata(List<String> strings, List<String> numbers, List<String> groups) {
            this.strings = strings;
            this.numbers = numbers;
            this.groups = groups;
        }
    }
    public class PackagedReturnItem {
        public List<StateWiseData> data;
        public ReturnMetadata metadata;
        public PackagedReturnItem(List<StateWiseData> data) {
            this.data = data;
            this.metadata = new ReturnMetadata(new List<String>{'state','statecode','lastupdatedtime'}, 
                                               new List<String>{'active','recovered','deaths','confirmed','deltaconfirmed','deltadeaths','deltarecovered'}, 
                                               new List<String>{'state'});
        }   
    }  
    public class CovidStatusCoreData1 {
        public List<DailyKeyValues> key_values;
        public List<StateWiseData> statewise;
    }
    public class DailyKeyValues {
        public String confirmeddelta;
        public String counterforautotimeupdate;
        public String deceaseddelta;
        public String lastupdatedtime;
        public String recovereddelta;
        public String statesdelta;
    }
    public class StateWiseData {
        public Integer active;
        public String confirmed;
        public String deaths;
        public String recovered;
        public String state;
        public String statecode;
        public String lastupdatedtime;
        public String deltaconfirmed;
        public String deltadeaths;
        public String deltarecovered;
    }
}

Create an apex step GetChartData in dashboard JSON to display the data. After placing the apex step click on done from the dashboard. Place the query on top of the table widget as shown below.

"GetChartData": {
	"query": {
		"body": {},
		"path": "covid"
	},
	"type": "apex"
},

covid states

In the final step, we have created a Static step to filter by states in India. To show a different type of output chart in the dashboard pass the wrapper from the apex class. Set the ReturnMetadata string, number, and group by columns correctly so that you could see the correct output when you place the query in the chart widget. Likewise, create the apex classes to fetch the data and display it in different chart types as shown in the below dashboard. Refer to the link for the apex classes and dashboard JSON. Using the dashboard inspector we can check the output for each lens that helps us to identify the performance and time taken for the query. Click on show details for a query that will show the details on your right-hand side panel.

There you go! you are on the last step to verify your dashboard, check the clip below.

[wpvideo RHIjNB4j]

NOTE: When you work with API apex step in analytics dashboard remember you have certain limits provided by salesforce that you can refer here. Firstly, Maximum concurrent Analytics API calls per org limit as 100. Secondly, Maximum Analytics API calls per user per hour limit as 10,000.

]]>
https://absyz.com/rest-api-call-from-einstein-analytics-dashboard/feed/ 3
Create/Update Salesforce Picklist definitions using metadata API https://absyz.com/create-update-salesforce-picklist-definitions-using-metadata-api/ https://absyz.com/create-update-salesforce-picklist-definitions-using-metadata-api/#respond Thu, 21 May 2020 07:51:19 +0000 http://blogs.absyz.com/?p=11116
Introduction:

Imagine you have a customer who is not familiar with Salesforce and there is a need to add/update picklist values along with record types in the system, then you cannot expect him to follow the ideal steps (Setup->Object Manager->) to make the changes. So, this problem can be solved by using Lightning Component with Metadata API Service. This component enables a user to make the required changes  across multiple fields in your Salesforce instance with couple of clicks from the UI itself.

Unique features:

1. On click Create/Update picklist values.

2. On click assigning of picklist values to the record types.

3. On click removing of picklist values from the record types.

4. On click Update of records upon updation of picklist values.

Display UI:

 

Functionality:

Step1: Object Name – Displays all the object names present in your Salesforce instance

Select an Object

 

Step2: Picklist – Displays all the fields of the selected Object

Select a Picklist

 

Step3: Action – Displays two actions available Create/Update

Select an Action which you want to perform

 

Step3a: If Action selected is ‘Create’

Select Record Types: – Displays all the available record types

Select the Record Types to which you want to assign the new picklist value

 

Step3b: Description – Contains the name of the new picklist value

Give the name of the new picklist value here

 

Step4: Action – Displays two actions available Create/Update

Select an Action which you want to perform

 

Step4a: Picklist Value to Update – Displays the picklist values related to the picklist field selected

Select a picklist value which you want to update

 

Step4b: Select RecordTypes

Selected Options – Displays all the record type names in which the selected value is already present

Available Options – Displays all the record type names in which the selected value is not present

Select the record types to which the picklist value need to be assigned and remove the record types from which the picklist value need to be removed

 

Step4c: Description – Contains the updated name of the picklist value

 

Step5: Save

Clicking on

 

required operation will be performed.

UI:

UniversalPicklist.cmp

You have to build a lightning component to get UI as shown in the above snippet, you need to display five fields and a button.

 

Object Name: You can either hard code what all objects you want to display in this field or get all the objects using schema

 

Picklist: As soon as you select an Object Name you need to make a server call and get all the respective picklist fields available. You can get all the fields using schema

Action: Since we need only two different actions here, you can display those two values using <lightning:select>

 

  • Create:- If you select ‘Create’ you need to make another server call and get all the record types in that particular object and display them in a format shown in the above snippet using <lightning:dualListbox>
  • Update:- If you select ‘Update’ this means that you want to update an existing picklist value. So, here also you need to make a server call get all the picklist values for a selected picklist field. You can refer to this doc for getting the picklist values

Picklist Value to Update: The obtained picklist values will be displayed here. As soon as a picklist value is selected, you should get all the record types which has and which doesn’t have this picklist value and display on the UI accordingly, as shown in the snippet.

Description: This field holds the new or updated picklist value which user enters

Back end Logic:

MetadataApiService.apxc

You should have a MetadataApiService class.

Refer this link, you can use the same class.

UniversalPicklistController.apxc

Create/Update picklist value for custom fields: You can refer to this link for creating or updating the piclist values using metadataservices

Create/Update picklist value for standard fields: Refer the below code for standard fields:

 

//Read
        MetadataApiService.StandardValueSet StandardField = (MetadataApiService.StandardValueSet) service.readMetadata('StandardValueSet', new String[] { picklistapiname }).getRecords()[0];
        
        MetadataApiService.StandardValue sv = new MetadataApiService.StandardValue();
        sv.fullName = valueInTheDescription;
        sv.default_x = false;
        StandardField.standardValue.add(sv);
        
        // Update 
        List<MetadataApiService.SaveResult> lstResults = service.updateMetadata( new MetadataApiService.Metadata[] { StandardField });

Updating Record Types: You can refer to this link to update Record Types

UniversalPicklistCntrlSessionIdGenerator.vfp

Use this code for generating session Id

 

<apex:page >
    Start_Of_Session_Id{!$Api.Session_ID}End_Of_Session_Id
</apex:page>

UniversalPicklistControllerUtils.apxc

 

global class UniversalPicklistControllerUtils {
    global static String getSessionIdFromVFPage(PageReference visualforcePage){
            String content= visualforcePage.getContent().toString();
            Integer s = content.indexOf('Start_Of_Session_Id') + 'Start_Of_Session_Id'.length(),
                e = content.indexOf('End_Of_Session_Id');
            return userinfo.getSessionId();
    }
}

 

]]>
https://absyz.com/create-update-salesforce-picklist-definitions-using-metadata-api/feed/ 0
Composite Resources in Salesforce https://absyz.com/composite-resources-in-salesforce/ https://absyz.com/composite-resources-in-salesforce/#respond Thu, 14 May 2020 13:27:14 +0000 http://blogs.absyz.com/?p=11148

Tired of making multiple REST API calls for a simple task? Then composite resources let you batch up multiple calls in a single call.By using this you can simplify your code, reduce network overhead, and improve your app’s performance.

Salesforce provides three composite resources namely composite, batch, tree
composite: Executes a series of REST API requests in a single call. You can use the output of one request as the input to a subsequent request.

composite/batch/: Execute a set of subrequests in a single request. Subrequests are executed independently and information can’t be passed between subrequest calls.Upto 25 subrequest in a single request can be executed.

composite/tree/: Creates one or more sObject trees with root records of the specified type. An sObject tree is a collection of nested, parent-child records with a single root record.

Using Of Composite Resource

Use this whenever you need to take an input from one subrequest and use it in other subrequest. Unlike batch resource it will create dependencies between the subrequests.To make a call /services/data/version/composite/  with a body that contains an array of subrequests. In the each subrequest need to specify the Method(Post,Patch,Get) , Resource Url. For every subrequest , we need to provide a reference Id ,reference Id to pass output from one subrequest to the input of another.

Sample request at below

{
  "compositeRequest": [
    {
      "method": "POST",
      "url": "/services/data/v44.0/sobjects/Account",
      "referenceId": "newAccount",
      "body": {
        "Name": "Sample Account"
      }
    },
    {
      "method": "POST",
      "url": "/services/data/v44.0/sobjects/Contact",
      "referenceId": "newContact",
      "body": {
        "LastName": "New Contact",
        "AccountId": "@{newAccount.id}"
      }
    }
  ]
}

 

Here in the above JSON, you can see how Account ID is used while creating the contact by refering the referenceID.

Example 2:Querying the Account and passing to contact.
{
  "compositeRequest": [
    {
      "method": "GET",
      "referenceId": "refAccount",
      "url": "/services/data/v41.0/query?q=Select+Id+from+Account+Limit+1"
    },
    {
      "method": "POST",
      "url": "/services/data/v44.0/sobjects/Contact",
      "referenceId": "newContact",
      "body": {
        "LastName": "New Contact",
        "AccountId": "@{refAccount.id}"
      }
    }
  ]
}

Points to remember:
  • Subrequests can be calls to SObject and Query/QueryAll resources.
  • you can have up to 25 subrequests in a single call. Up to 10 of these subrequests can be query operations.
Using of Composite Batch

Use the batch resource when you’ve got a bunch of independent REST API calls that don’t depend on each other . To make a batch resource call, issue a POST to instance /services/data/API version/composite/batch/ with a request body that contains an array of REST API subrequests.

Subrequests execute serially in their order in the request body. When a subrequest executes successfully, it commits its data. Commits are reflected in the output of later subrequests. If a subrequest fails, commits made by previous subrequests are not rolled back. In the each subrequest need to specify the Method(Post,Patch,Get) , Resource Url.You’ll get back a response that contains an array of subrequest results.

Sample Request

 {
"batchRequests" : [
    {
    "method" : "PATCH",
    "url" : "v44.0/sobjects/account/001xxxxxxxxx",
    "richInput" : {"Name" : "Update Name"}
    },{
    "method" : "GET",
    "url" : "v44.0/sobjects/account/001XXXXXXXXXX?fields=Name,BillingPostalCode"
    }]
}
Points to remember:
  • You can make up to 25 subrequests in a single batch call.
  • While the calls can’t depend on each other, they are called in the order specified in the request data.
  • You can’t use different API headers for different subrequests. You can only use API headers that apply to the overall batch call.
  • If a particular subrequest takes longer than 10 minutes to execute, the entire batch call times out and subsequent subrequests are not executed.
Using Of Composite Tree

Creates one or more sObject trees with root records of the specified type in  a single call.To make a tree resource call, issue a POST to instance /services/data/API version/composite/tree/SObject Name/ with a request body that contains one or more SObject record trees. A sObject tree is a collection of nested, parent-child records with a single root record.

Sample Request: Creating Multiple Account records.

POST Method : /Services/data/v44.0/composite/tree/Account/
{
  "records": [
    {
      "attributes": {
        "type": "Account",
        "referenceId": "ref1"
      },
      "name": "SampleAccount1",
      "numberOfEmployees": "100",
      "industry": "Banking"
    },
    {
      "attributes": {
        "type": "Account",
        "referenceId": "ref2"
      },
      "name": "SampleAccount2",
      "numberOfEmployees": "250",
      "industry": "Banking"
    }
  ]
}

And here’s an example request body that uses one SObject tree to create a single Account record along with one child Contact record.

POST Method : /Services/data/v44.0/composite/tree/Account/
{
  "records": [
    {
      "attributes": {
        "type": "Account",
        "referenceId": "ref1"
      },
      "name": "SampleAccountWithContacts",
      "phone": "9123458899",
      "Contacts": {
        "records": [
          {
            "attributes": {
              "type": "Contact",
              "referenceId": "ref2"
            },
            "lastname": "Pranav",
            "email": "pranav@sample.com"
          }
        ]
      }
    }
  ]
}

Here the response will give you an array of created recordId along with the assosciate reference Id. Maintain Refrence id as an unique through out the transaction which helps  identify the correct record id.

Points to remember:
  • The root record of each SObject tree must be same type you specify in the resource call. So, for example if you make a tree resource call using /services/data/API version/composite/tree/Account/, all SObject trees you specify in the request body must start with an Account record.
  • You can create Up to a total of 200 records across all trees in a single call.
  • SObject trees can be a maximum of 5 levels deep, so at most a single “root” record and 4 levels of nested child records.
  • Across all your SObject trees in a given request, you can use a maximum of 5 different types of objects
]]>
https://absyz.com/composite-resources-in-salesforce/feed/ 0
Drive User Action with Floating Prompts in Lightning Experience https://absyz.com/drive-user-action-with-floating-prompts-in-lightning-experience/ https://absyz.com/drive-user-action-with-floating-prompts-in-lightning-experience/#respond Wed, 29 Apr 2020 20:42:17 +0000 http://blogs.absyz.com/?p=11202

Think of driving employees to complete compliance training, pushing them to fill out a GPTW annual survey or  sending reminders for completing Salesforce certification maintenance exams.  Salesforce Floating prompts can assist you to drive employee actions, broadcast company news, send recurring reminders & more. 

Let us look at an illustration for a Floating prompt. Below prompt is meant to drive all Project managers to complete the forecasting activity for the current month when they view their Salesforce landing page.

Image_1

Before deep diving into the creation of a Floating prompt, let us review the org-wide prompt settings.

 

Review Org-Wide Prompt Settings

Go to Setup-> In-App Guidance -> Add Prompt -> Prompt Settings.

Image_2

  1. Display between prompts: This decides the time delay between showing two prompts per app. With the current custom setting of 2 hours, you would be able to see another prompt after viewing one in the same app only after a delay of 2 hours. 
  2. Salesforce standard prompts: You would’ve come across these prompts in your org for introduction/highlighting of new Salesforce features like Recycle bin in Lightning or Controlling the density feature. These prompts are part of in-app guidance offered by Salesforce which can be activated/deactivated  from this path
  3. Custom Prompts: You can activate/deactivate all the custom prompts in your org using this option.

 

Setting up a Floating Prompt

The Authoring Bar

Let’s see the origins of the Floating prompt introduced earlier. First step would be to reach the Prompt authoring bar. You can go there from this path: “Setup-> In-App Guidance -> Add Prompt -> Open Authoring Bar”. 

Image_3

Once opened, the authoring bar would remain at the top of an App. You would be able to navigate between different pages here like you do when working within an app by switching between Home Page, record pages, list views & more. When you are on a page(see above screenshot), you would see the already added prompts to the page from 1) “Prompts on this Page” drop down. You can also add more prompts on the page you are using 2) “Add Prompt” button. 

When clicking on “Prompts on this page”,  you will be able preview & edit prompts already defined on the current page.

Add a New Prompt: Select Type & Position

From the “Add Prompt” option on the authoring bar, you will be taken to the prompt wizard. Select “Floating Prompt” from the below screen:

Image_4

Select the “Floating Prompt” option on the above screen. Next you can decide Prompt’s location. There are 6 locations where a floating prompt can be placed. Be sure you do not have important information on that part of the page as you can’t temporarily minimise the prompt without interacting with it. You can select the “Top Left” option for the current case. 

Image_5

Visibility

On the next screen, you’ll have to decide whether to restrict this prompt to certain profiles & permission set assignees or keep it open to all. In the current example, you will be allocating this to a specific profile since this message is targeted towards the Project managers group. 

Image_6

Additionally, you can add permission sets from the next screen which essentially means you can set the prompt’s visibility based on a combination of Profile & Permission sets. You can currently have a combination of upto 10 profiles & permissions on a Prompt.

Add Content

Next, comes the all important content section. 

Image_7

A floating prompt’s content consists of: 

  • Title – Header for the prompt
  • Body –  Accepts plain text only with 240 chars. The content should be concise  & convey the message effectively without any ambiguity.
  • Dismiss Button Label – Name of the Dismiss Button label
  • Action Button Label (Optional) – Name of the Action Button label
  • Action button URL (If Action button Label has values) – The “Action” button can redirect you to another URL. It does not invoke a flow or a process. For the current example, you can redirect the user to another app page where forecasting activity is done.

Scheduling

Finally, fix a date range during which you want the prompt to show up for the target audience. Since the expectation is to complete the forecasting activity by end of 1st week of May, we would keep the date range from 1st – 8th May’20

Configure the frequency at which this prompt would show.  Add the no. of times it should show & the days between each occurrence in the “Times to Show” & the “Days in Between” fields respectively. 

Image_8

The “Show prompt when the page loads” checkbox overrides the default time gap set  in “Prompt Settings” & shows the prompt every time the page loads if the user hasn’t interacted with the prompt. You can use this option to reinforce the importance of the current prompt’s message over other configured prompts.

Add remaining details & Preview

Post configuring the schedule, you can add remaining details for the prompt like Prompt Name, API name & description on the last page of the creation wizard. 

Image_9

Once done, you can Save & Preview the Prompt. 

Image_10

You can make further edits to the prompt using the “Edit” button or make it ready for use by marking it “Done”.

 

User Interaction & its impact on Floating Prompt’s Occurrences

Scenario:1 “Action” button is defined on the Floating Prompt

Image_11

Since an “Action” button is defined, the intent is for the user to complete the action. Till the action button is clicked, the Prompt would show up based on its schedule settings. 

Below are the Prompt behaviours which occur based on User Actions:

Sr. No.  User Action Prompt Behaviour
1 Do nothing, stay on the page Prompt stays
2 Do nothing on Prompt – Navigate to another page & come back to the first page where prompt is defined Show prompt when user navigates back to same page**
3 Close the prompt using ‘X’ on top right Show prompt when user navigates back to same page**
4 Click on “Do it Later” Show prompt after 1 day***
5 Click on “Forecast Now” Stop showing prompt even if more recurrences are scheduled

** Org-wide delay of 2 hrs set earlier in “Prompt Settings” is overwritten by “Show Prompt when Page loads” checkbox set in the Floating prompt’s schedule we’ve defined. If the checkbox was unchecked, the system would wait for 2 hrs from occurrence of any other prompt on the current app before showing the Floating Prompt on the current page again. 

*** Since in the Prompt’s Schedule settings “Days in between” is set to 1.

Scenario:2 “Action” button is not created on the floating prompt

With all remaining settings being  the same as in Scenario:1, Sr. No. 1-3 from the “User Action – Prompt Behaviour” mapping table in the previous scenario would still hold. Sr. No. 4’s behaviour would change to -> Stop showing prompt even if more recurrences are scheduled. This is because without the action button, the prompt only expects the user to view the message. An appropriate example can be:

Image_12

Conclusion

As mentioned in the introduction, Floating prompts are meant to drive user action. But there is another engagement activity which Salesforce prompts support – User Adoption. The other category of prompts i.e Docked prompts are primarily used for driving adoption using step by step instructions & in-prompt videos. You can expect another post on it soon 🙂 ! Meanwhile, hope you enjoyed this blog !!  Thank you for your time !!!

]]>
https://absyz.com/drive-user-action-with-floating-prompts-in-lightning-experience/feed/ 0
Summer’20 Release Treasure Hunt: Top Features https://absyz.com/summer20-release-treasure-hunt-top-features/ https://absyz.com/summer20-release-treasure-hunt-top-features/#respond Wed, 29 Apr 2020 06:04:49 +0000 http://blogs.absyz.com/?p=11168

Summer’20 is just around the corner… and with every release Salesforce introduces new features and enhancements made to the platform. Thus enhancing our productivity by using the native declarative and programmatic features.

Summer’20 official Release notes are not yet out, below are few features we have identified as few and among the coolest ones!

Let’s look at few out of the box features which are going to be introduced with this release!! 🙂

1. Flows

1.1  Before/After Handling in Flow Triggers

We recently saw Flows inherit an ability from Apex: the ability to act as a Trigger. You could select criteria which a Flow would be called automatically, and handle data.

With Summer ’20, we’re seeing yet another step in Flow becoming a fully-fledged declarative alternative to Apex: the ability to handle before and after events. This is a massive step for Flow and will grant even more power to declarative users.

Instead of being an Auto-Launched Flow, this will now be known as a ‘Record Changed’ Flow (one of a number of new Flow Types).

 

Screenshot (208)

You can choose when to trigger the flow(see image below).

FlowTrigger

1.2 Run Flows in System Mode

Now you can execute/run your flows overriding the current user’s permissions by setting your flow to run in system context with sharing. The flow still respects org-wide default settings, role hierarchies, sharing rules, manual sharing, teams, and territories, but it ignores object permissions, field-level access, or other permissions of the running user.

Flow003

1.3 Flow Loop Variables are Automatically Created

In every iteration of Flow Builder and Designer to date, we’ve had to create our loops, then create a separate loop variable resource. It’s just an extra step that felt so unnecessary.

Salesforce has addressed this by creating this resource automatically when you create a loop. Let’s say you had a loop called ‘lead_loop’, Flow Builder will automatically create a resource called ‘Current Item from lead_loop’. The result? It will save you a ton of time, and make it much easier for new Flow users to learn how to use the tool.

Flow004

1.4  Roll back Mode in Flow Debugger

With rollback mode enabled in your Flow Debugger, any changes that are executed by your Flow won’t be saved while you test it. This helps to keep your data clean and tidy while you create and test your Flows. This is one of those tiny changes that will have a huge impact.

Flow005

2. Lightning

2.1 Navigate to a record page with default values

Now, you can create  custom button or links that pass default field values to a new record using formulas.

This feature is not available for the Lightning communities, or the Salesforce mobile app.

To construct a custom button or link that launches a new record with prepopulated field values, use this sample formula:

lightning/o/Account/new?defaultFieldValues=
    Name={!URLENCODE(Account.Name)},
    OwnerId={!Account.OwnerId},
    AccountNumber={!Account.AccountNumber},
    NumberOfEmployees=35000,
RecordType=Account_Record_Type_Test,
    CustomCheckbox__c={!IF(Account.SomeCheckbox__c, true, false)}
2.2 Empty the Recycle Bin in One Step

Empty your Salesforce org’s Recycle Bin in Lightning Experience with a single click. Previously, you either selected individual items to delete, or had to switch to Salesforce Classic  to remove all items at once permanently.

224_rn_recycle_bin_admin

2.3 Split View in Standard Navigation

Standard Navigation gets a Console makeover! Console view displays multiple records and their related records on the same screen, and ‘Split View’ displays a List View within a record page:

This means that you can quickly work through records – ideal for updating overdue Tasks, all Opportunities in your pipeline, or the new Leads assigned to you…or anything you use List Views for!

SplitView
2.4 Source Tracking in Sandboxes

You can now enable Source Tracking in Developer and Developer Pro to use the Salesforce Command Line Interface (CLI) to view, pull, and push changes in the Sandbox. Once enabled, any new Sandboxes you create will have tracking enabled automatically, and after refresh for any existing Sandboxes.

3. Development

3.1 The @track decorator is no longer needed to make the Lightning Web Component Class Reactive

In order to capture changes to a field’s value and to make it reactive to changes you had to decorate it with @track before Spring’20.

But with Summer’20 @track decorators are no more needed to make the fields reactive, the fields in LWC are reactive by default now.

If a field’s value changes, and the field is used in a template or a getter of a property that’s used in a template, the component rerenders and displays the new value.

3.2 Style Lightning Web Components with Custom Aura Design Tokens

A Lightning web component’s CSS file can use a custom Aura token created in your org or installed from an unmanaged package. Tokens make it easy to ensure that your design is consistent, and even easier to update it as your design evolves.

You can define a token value once and reuse it in your Lightning component’s CSS.

Create a custom Aura token in the Developer Console by creating a Lightning Tokens bundle.

<aura:tokens>
    <aura:token name="myBackgroundColor" value="#f4f6f9"/>
</aura:tokens>

Here “myBackgroundColor” is a custom aura token.

Custom Aura tokens aren’t new, but now you can use them in a Lightning web component’s CSS file by using the standard var() CSS function. Prepend –c- to the custom Aura token.

// myLightningWebComponent.css
color: var(--c-myBackgroundColor);
3.3 Smarter Source Tracking for Lightning Web Components in Scratch Orgs

Until now, CLI commands tracked source changes for Lightning web components in a local project, but didn’t track changes to Lightning web components in a scratch org.

With Summer’20 release Salesforce command-line interface (CLI) now tracks changes to Lightning web components in a scratch org. The CLI output lists any changes, and alerts you to any conflicts between your local project and a scratch org.

This change is available in Salesforce CLI with salesforcedx v48, which released on February 15, 2020.

sfdx force:source:pullPulls 
changed source from a scratch org to your project.

sfdx force:source:pushPushes 
changed source from your project to a scratch org.

sfdx force:source:statusTracks 
changes between your project and a scratch org.

3.4  DOM API Changes May Require UI Test Updates

The content and structure of HTML, CSS, and the DOM in Lightning Experience can change at any time and can’t be considered a stable API. Automated UI tests that use tools like the Selenium WebDriver to reach into component internals require your ongoing maintenance.

4. Reports and Dashboards

With Summer’20 release now we can send the attachment while scheduling the Report/Dashboards from Salesforce Lightning.

Navigate to Report & Dashboards Setting & Enable “Let users attach reports as files to report subscription emails in Lightning Experience” Setting.

 

reports&amp;dashboards

reports&amp;dashboards002

5. Federation Id can be made case sensitive

With Summer’20 release you now have the option to make the Federation Id as case-sensitive. Previously this feature was not available.

Go to setup, from setup select single sign on settings and enable the checkbox “Make Federation Id case sensitive”.

Screenshot (214)

6. AWS Private Link

With summer’20 release Salesforce introduces the Private Connect. Assuming this will facilitate establishing connection between Salesforce and AWS VPC and other AWS Services.

AWS PrivateLink simplifies the security of data (hosted on the AWS platform) shared with cloud-based applications by eliminating the exposure of data to the public Internet. AWS PrivateLink provides private connectivity between VPCs, AWS services, and on-premises applications, securely on the Amazon network.

With AWS PrivateLink, you can connect your VPCs to AWS services and SaaS applications in a secure and scalable manner.

Screenshot (210)

From setup, go to Private Connect, choose whether you want to create an Inbound Connection/Outbound Connection.

Inbound connection will allow you to access data shared with your Salesforce Application from an AWS VPC(Virtual Private Cloud) which is an additional layer of an Amazon EC2 (Elastic Compute Cloud)Instance.

Outbound Connection will allow you to share/proxy your Salesforce Data into an AWS VPC.

Screenshot (212)

You would need to specify the end point Id of your AWS VPC and also the region in which the service is hosted.

You can also specify the option of provisioning the connections and choose whether to provision connection immediately or at a later point of time.

 

These are some of the features that we have explored out.

We will be back with Part I and Part II of the Release highlights with more information on the hot and newest features that shall roll into your Salesforce org’s with the summer’20 release.

Till then, you can sign up here for a Pre-Release Org and keep on exploring!! 🙂

]]>
https://absyz.com/summer20-release-treasure-hunt-top-features/feed/ 0
Increase your Productivity with the Apex Metadata API https://absyz.com/increase-your-productivity-with-the-apex-metadata-api/ https://absyz.com/increase-your-productivity-with-the-apex-metadata-api/#respond Mon, 23 Mar 2020 10:45:09 +0000 http://blogs.absyz.com/?p=10385

The force.com Platform provides us with features which are out of the box and helps increase productivity! Such is the “Metadata API”.

Here, we will see how we can leverage the Metadata API and avoid doing tasks manually thus ultimately saving a lot of time!

What is Metadata API?

Launched back in the Summer’17 release the Apex Metadata API lets you make metadata changes directly from apex.

We can use the handy features of apex in order to build custom setup UI scripts and perform automation actions behind the script.

Let’s consider an example and see how it can help :

Suppose you are an ISV and have created an app involving automation’s and many other features which are not generally available.You have packaged all your changes and is ready to be installed across org’s.

Now, the admin team comes into picture for rolling out the changes. The admin team has to perform a lot of post installation steps manually using the setup UI over and over.

This is where the metadata API comes in, you can create custom setup UI scripts to guide the admins through the process, and perform back end change actions using metadata api which updates the metadata while they go through the process over clicks and that’s it the changes will be available.

Thus, avoiding repetitive steps.

Let’s look at few use cases where we can use the Metadata API:

  1. Using the Metadata API we can build tools which can automate configuration changes.
  2. We can update our metadata changes in multiple org’s by deploying it across multiple org’s from apex using the Metadata API Deploycallback class which uses a deployment container and executes based on actions specified.
  3. We can control multiple org metadata in a single org. And can perform CRUD operations.

There are lots of use cases around, let’s implement few and see how actually the Metadata API can help us increase productivity!

In our example here we will be retrieving the metadata from the contact object. We will retrieve a section named ‘Additional Information’ from the page layout of contact, add a new field into that section all from apex!!

Sounds Interesting ain’t that? Let’s check it out…

[sourcecode language=”Java”]

public class UpdateContactPageLayout {
public Metadata.layout addLayoutItem(){

//Retreive metadata from the contact page layout using Metadata.Operations.retrieve method
List Metadata.Metadata layoutsList =
Metadata.Operations.retrieve(Metadata.MetadataType.Layout,
new List String{‘Contact-Contact Layout’});

//Getting Layout from the contact object
Metadata.Layout layoutMetadata = (Metadata.Layout) layoutsList.get(0);
Metadata.LayoutSection contactLayoutSection = null;
List Metadata.LayoutSection layoutSections = layoutMetadata.layoutSections;

for (Metadata.LayoutSection section : layoutSections) {
/** Assigning section value to contactLayoutSection Variable if section label satisfies condition **/

if (section.label == ‘Additional Information’) {
contactLayoutSection = section;
break;
}

}

List Metadata.LayoutColumn contactColumns = contactLayoutSection.layoutColumns;
List Metadata.LayoutItem contactLayoutItems = contactColumns.get(0).layoutItems;

Metadata.LayoutItem newField = new Metadata.LayoutItem();
newField.behavior = Metadata.UiBehavior.Edit;
newField.field = ‘AMAPI__Apex_MD_API_Twitter_name__c’;
contactLayoutItems.add(newField);

system.debug(‘newfield Inserted’);
system.debug(‘contactLayoutItems’ + contactLayoutItems);
system.debug(newField);

return layoutMetadata;
}
}

[/sourcecode]

 

Let’s check out the logs and see whether a new field was created or not!

If you are trying this out instead of “amapi” in the newField.field variable specify your org’s namespace followed by the field you want to create.

E.g: ‘yourOrgNameSpace_Field_Name__c’

Screenshot (73)

We can see that the field got created, but in order to finally add the field in your layout we need to deploy the Metadata changes using Deploy container. This will finally add your metadata to the page layout.

Refer here for Deployment Container Class.

Now, let’s quickly look at another example of creating a record in a Custom Metadata Type.

In our case we have already created a Custom Metadata type called “Vat Rate”.

And we will be creating a new record for the custom Metadata Type using Custom Metadata API from apex.

If you are a newbie and have no idea how to create a custom metadata type refer this “Create Custom MetadataType“!

Let’s look at the code:

[sourcecode language=”Java”]

public class MetadataExample {
public void updateMetadata(){

//Retreive custom metadata type details from org
Metadata.CustomMetadata customMetadata = new Metadata.CustomMetadata();

//insert the fullName and Label value for the record you want to create

customMetadata.fullName = ‘VAT_Rate.NewRecordFromMetadataAPI’;
customMetadata.label = ‘NewRecordFromMetadataAPI’;

//We will populate the field called Rate in the Custom Metadata type with some value
Metadata.CustomMetadataValue customField = new Metadata.CustomMetadataValue();
customField.field = ‘Rate__c’;
customField.value = 10;

/**Adding record values to the customMetadata type ***/
customMetadata.values.add(customField);

/** Deployment Container to deploy the metadata changes **/

Metadata.DeployContainer deployContainer = new Metadata.DeployContainer();
deployContainer.addMetadata(customMetadata);

/** This will invoke the deployment Process of the changes **/
system.debug(‘customMetadata’+customMetadata);
system.debug(‘Metadata deployed’);

Id asyncResultId = Metadata.Operations.enqueueDeployment(deployContainer, null);
}
}

[/sourcecode]

Also, check the logs just to be sure there are no errors! 😀

Screenshot (74)

You can track your metadata change deployment in the Deployment Status.

Navigate to Setup –>In Quick Find type “Deployment Status” and click on the record initiated with proper timestamp details.

Screenshot (77)

Once the changes are deployed, we will navigate to the Custom Metadata Type and check whether a record has been created or not.

Let’s navigate to the Vat Rate Custom metadata type and see.

Screenshot (78)

 

Now let’s navigate to the record detail page and check whether the value of “rate” specified in code is populated or not.

Screenshot (79)

That’s it! We have created a metadata record type using the Metadata API from Apex and not manually!!

Advantages of using the Metadata API:

  1. You can deploy your metadata changes using deployable packages across multiple org’s without needing to create change sets and doing tasks manually.
  2. You can write custom scripts to perform backend actions for automating metadata changes across the org. thus eliminating a major heck of manually work for the SF admins out there!! Interesting? Ain’t that.

You can also create CustomMetadata types using the MetadataService class library.

That shall be all for now!

We will continue to dive deep on this in the next follow up blog and also check how to test your MetadataAPI and discuss possibilities of designing a solution for Bulk Creation of Custom Metadata Types and automate Metadata Changes.

Thank you for reading!!

Don’t be shy to try it out, and keep on blazing your trails!! 🙂 

 

 

 

 

 

]]>
https://absyz.com/increase-your-productivity-with-the-apex-metadata-api/feed/ 0
Outlook connection with Salesforce through custom Objects https://absyz.com/outlook-connection-with-salesforce-through-custom-objects/ https://absyz.com/outlook-connection-with-salesforce-through-custom-objects/#respond Wed, 19 Feb 2020 07:21:41 +0000 http://blogs.absyz.com/?p=10941

This blog post helps you to integrate Microsoft Outlook with Salesforce by using Lightning for Outlook sync by OAuth 2.0 connection method and create Custom Objects data from outlook.

Set Up Lightning for Outlook in the Microsoft Outlook

For setting up of Outlook connection to salesforce, please follow the below blog from points 1-9 to setup the users in outlook who needs to have sync with Salesforce.

.https://blogs.absyz.com/2018/07/11/step-by-step-process-of-integration-between-outlook-and-salesforce-by-service-account-connection-method/

Lightning for Outlook and Lightning Sync from Salesforce

 

  1. Enter Outlook Integration and Sync in the quick find box
  2. Enable Outlook Integration and Lightning Sync.

 

 

  • Expand the Lightning Sync section after enabling it. You could see two types of connection methods.
  • OAuth 2.0
  • Service account

Select the OAuth 2.0 and click on Log in Button.

 

Once you click on Login button, It will automatically redirect to outlook login page.

Enter the details of Global admin User of Microsoft Account and click on Accept.

 

The connection gets accepted and we will be getting the Office 365 Tenant ID automatically which is shown in below screenshot.

 

Click on Connect. The connection gets authorised and ready for testing the user connection with Outlook.

 

Enter the user’s email address whose email is same in both the systems (Salesforce & Outlook) in the Test your connection prompt.

Click on test after entering the email address. This will verify with Outlook server and enables a secure connection between both the systems.

 

Now we have successfully connected with email server, so the exchange happens between both the systems.

Define the sync settings and click on Go to Configs

 

Define lightning sync configurations.

Click on New lightning sync configurations and give the exchange behaviour as shown below.

 

 

Click on Save.

Outlook Connection Establishment:

Open your Outlook mailbox and click on mail and you can find more actions on right side.

 

Click on Get Add-ins.

 

From the search bar you can search for salesforce add in and add to outlook.

 

Once the salesforce plugin is added, login to salesforce where you can select your environment (Sandbox / Production).

 

Enter the salesforce login credentials and click on login. After successful connection we can see the standard objects displayed in the connector.

 

Now we can directly create Task, Event, Account, Contact and Lead from outlook to salesforce.

Now, we are going to learn how to add Custom Objects to this standard list.

I have used a custom Object (Agent) and some custom fields to link with Outlook.

Go to salesforce and search for global action from search bar and then create a new global action.

 

After creation of new global action (New Agent), it will take you through page layout of that action where we can add the necessary fields to that layout.

 

Enter Outlook in the search bar and click on Outlook Integration and Sync.

Expand the section of Outlook Integration and click on New Email Application Publisher Layout.

 

Create a new Layout and add the global action (New Agent) to Quick Action.

 

Now add the layout to profiles.

Click on Publisher layout assignment and add the layout to profiles.

 

 

Once this is done, we can able to view custom objects along with standard objects in Outlook.

 

create a custom object data from outlook to salesforce.

 

Limitations for creating Global action in salesforce.

 

  • Adding too many fields to an action layout can impact user efficiency. We recommend a maximum of 8 fields. To reduce the number of fields, you can set predefined values for fields that need a value but won’t often be edited. You can safely remove those fields from the layout. Set predefined values from the action detail page.

 

 

  • If we have record types for an object, we need to create global action for each record type because each record type has its own page layout.
  • Custom Objects which are on the detail side of a master-detail relationship cannot be created via Global Action. A detail object record requires a master record and can’t be created in a non-entity-specific context.
  • Ref—– https://help.salesforce.com/articleView?id=000337323&type=1&mode=1

 

]]>
https://absyz.com/outlook-connection-with-salesforce-through-custom-objects/feed/ 0
Archive of Salesforce data https://absyz.com/archive-of-salesforce-data/ https://absyz.com/archive-of-salesforce-data/#comments Fri, 31 Jan 2020 12:36:39 +0000 http://blogs.absyz.com/?p=10098

The Salesforce has many regulatory limits and one such is Data Storage. As the CRM ages, it accumulates large amounts of information and many enterprises eventually run into data storage limitations. Purchasing additional Salesforce storage space is not economical when you add the operational burden. To overcome this challenge a comprehensive archiving mechanism is recommended and is an essential part of every progressive business.

What is Archiving?

Archiving is transferring the data to a less frequently used storage medium. Archiving enables organizations to create and retain more data than ever before.

Why data should be archived in Salesforce?

An archive includes historical, rarely-used Salesforce data that can be entirely removed from your Salesforce environments. Upon archival, the data is moved to long-term retention and can be used for future reference. Data archiving

  • Allows only to use Salesforce provided storage limits.
  • Reduces storage costs and increased storage limits for relevant data.
  • Keeps your customer sensitive data safe.
  • Consistent application performance.

Does Salesforce provide Archival mechanism?

The answer is yes, Salesforce has a built-in archiving capability. It is limited to tasks, events, and activities that are older than a year and this doesn’t count against your company’s storage allocation. Salesforce doesn’t address other huge objects data that should be archived depending on the business process. Another option that Salesforce proposes for archiving is Big Objects, which has its own advantages.

Are there any third-party vendors that provide archival functionality for Salesforce?

Yes, there are vendors, one such is DataArchiva, it gives a lot of options to choose storage like AWS, Azure, Heroku, etc. The pain part is, it comes with pricing.

Challenges for Archiving data in Salesforce

Approach
Challenge
Building the application on the Force.com platform using the Zippex library. This works only for the total file size of less than 3MB.
Building the application on Force.com platform using javascript libraries in the Visualforce page. This solution cannot run in the background as a scheduled job.
Hosting the above-mentioned application on the site page and invoking site page through the batch apex. Javascript code of Visualforce page is not compiled and as a result no execution of Archival Logic.

Now let’s see an Economical, limitation less solution for the Archival of Salesforce data.

APIs are always there to enhance a platform and based on this the solution is implemented to overcome the platform limits. This blog provides the solution for Archiving large files using the Salesforce REST API and Java Application hosted on Heroku. 

Solution Architecture

ArchivalBlogg.pngThere will be three entities in the solution

  1. Salesforce
  2. Java Application
  3. Online file storage web service (here AWS Glacier)

The Salesforce data is pushed to AWS Glacier via Java Application. In order to archive a specific record, the approach would be 

  1. Get the HTML view of the record in a content document.
  2. The Java Application will connect to Salesforce and get the content versions, convert the HTML page of record to PDF/A, zip them and upload the zip to the glacier.  In simple we are Wrapping PDF of the record and its related files together and putting them in the glacier.
  3. Glacier gives back a response Id for every zip file you insert. Store this Id in the Salesforce or any other database.
  4. Use this Id to retrieve the data when you need it for any evidence.

Note, to get the HTML view of the record to create a custom Visualforce page that shows all the data of the record along with related lists and chatter, and convert that VF page to HTML dynamically.

Since the Force.com platform has limitations, we are moving the logic of processing files to another open-source platform like Java and performing necessary operations and inserting back the result to Salesforce through API. This Java entity can be hosted on any PAAS like Heroku. It can even be scheduled to make Archival an automated job.

Steps to connect to Salesforce from Java application

  1. Create a basic connected application in Salesforce, and select the OAuth Scopes to Access and manage your data (API)
  2. Once the connected app is created, note the consumer key and secret key.
  3. Configure OAuth2.0  authentication.
  4. Once Authentication is successful, using the Oauth token you can communicate with Salesforce. Make the required queries to get the files from the Salesforce.

Note, instead of making multiple callouts from the external application to get the content version Id (File Id), populate the contentversionIds in a field on the record using SOQL in Salesforce.

Criteria for Archiving a record

It can be defined at the org level or object level or record level. If it is at org level or object level, a custom setting can be created with a field that stores the Archival Period. If you want to use it for org level, create a single record with the archival period, and if you want to use it for object level, create a record for each object specifying the archival period. If you want at the record level, then just create a field on every object that stores the Archival Period. Note, data satisfying this Archival Condition only should be queried from Java.

Zipping in Java

Once we query all the files under a record which includes HTML also,  keep them in a folder with the name as Record Name. Before placing the files convert the HTML page of record to PDF/A. Now, this folder can be  Zipped using any of the libraries present in java. For reference use this link

Sending it to Cloud storage (here AWS Glacier)

Here we can leverage the Java language, cause whatever cloud storage you choose, there will be some material available online where we can easily get the code in Java to connect to that storage. For AWS Glacier refer to this link.

This uses SDK to connect to the glacier, and integration becomes pretty simple with this SDK.

Now we have a fully working application, the question is where do we host it. If you choose Heroku as a platform to host follow the below steps.

  1. You will require a one-off dyno to host it and to be scheduled.
  2.  For scheduling instead of using the Heroku scheduler, use the custom clock process in Java, refer to this link.  

Want to implement an economical archiving solution for your Org then get in touch with us.



		
]]>
https://absyz.com/archive-of-salesforce-data/feed/ 2
Pardot : Understanding Layout Templates https://absyz.com/pardot-understanding-layout-templates/ https://absyz.com/pardot-understanding-layout-templates/#respond Tue, 07 Jan 2020 07:34:39 +0000 http://blogs.absyz.com/?p=10733

INTRODUCTION

A layout template controls the style of various web elements managed within Pardot. They are used for landing pages, forms, and site search results. Layout templates are used to format custom forms and landing pages. In a way, they are reusable blocks of code that give your marketing assets a defined structure and style. Layout templates are a great solution if you want to enhance your forms and landing pages with styling options that are not available using default Pardot functionality.

Note: Using layout templates requires knowledge of front-end coding languages like HTML, CSS, and JavaScript.

To create a Layout Template

  1. In Pardot Lightning App, Navigate to Marketing > Landing Pages > Layout Templates. In Pardot Classic, go to Marketing > Forms > Layout Templates or Marketing > Landing Pages > Layout Templates. Both paths will take you to the same layout template builder.
  2. Click the + Add Layout Template button.Layout Template Creation
  3. Enter a Name that can be internally used to organize your templates. You can use something descriptive (e.g., “Site Search Results Layout”) if you plan to use multiple layout templates. This will help you track the template which controls the styling for each of your marketing elements.
  4. The Layout tab controls the look and feel of your marketing elements using HTML and CSS. Enter %%content%% wherever you would like for the form, site search results, or landing page opening general content to appear within your design. You can also use %%title%% to pull in the title of the landing page and %%description%% to pull in a description from you landing page. We will the  Variable Tags further below.
  5. There is also import option, you can import your styling by clicking Import layout from URL. Simply enter the URL and click Import now. You will need to edit the output by qualifying any URLs as they will all need to be absolute instead of relative.
  6. The Form and Site Search tabs consist of Pardot substitution logic statement, illustrating how the various form fields, content, and other elements will be displayed. Advanced users can customize this logic based on their business needs.

Variable Tags:

There are three Pardot variable tags you could add to your layout template.

  • %%title%% — The %%title%% variable tag will pull in the title for the page. For landing pages, the title entered in the first step when creating a landing page will replace the %%title%%. Pardot will fill in the title for your unsubscribe and email preference center pages.
  • %%content%% — Enter the %%content%% variable tag where you would like the form, site search results, or landing page opening general content to appear within your design.
  • %%description%% — The %%description%% variable tag can be added to the head section of your layout template. This will pull in the description that is added in step 1 of the creating a landing page.

USING THE FORM TAB

The Form tab controls the structure of your forms. It contains variable tags like %%formfield-input%% that tell Pardot how to arrange form elements in the browser. You should only modify this tab if you need to rearrange or remove certain form elements. For example, you could move %%form-field-input%% above %%form-if-field-label%% if you want your field labels to appear below your form fields.

STYLING LANDING PAGES WITH LAYOUT TEMPLATES
Using Your Own Custom Code

1. In Pardot Lightning App, go to Content > Layout Templates. In Pardot Classic, go to
Marketing > Forms > Layout Templates or Marketing > Landing Pages > Layout Templates. Both paths will take you to the same layout template builder.
2. Click + Add Layout Template.
3. Give your template a Name, Folder, and Tags. These features will help you stay organized and find the right layout template quickly when you build landing pages.
4. From the Layout tab:
5. Add the required %%title%%, %%description%%, and %%content%% variable
tags.
6. Add the pardot-region attribute to the HTML elements in your layout templates to make the content editable in the Pardot landing page
WYSIWYG editor.
7. Style your form by adding CSS above the tag.
8. Make changes in the Form tab, if needed.
9. Click Save.

Starting from a Responsive Layout

If you don’t want to create a completely custom layout template, you can start from one of Pardot’s responsive layouts. These layouts already contain all of the necessary variable tags, code for basic form and landing page styling, and content regions for an easy editing experience. Edit the code to add your own colors, fonts, logos, and more to customize the template for your business.

    1. In Pardot Lightning App, go to Content > Layout Templates. In Pardot Classic, go to Marketing > Forms > Layout Templates or Marketing > Landing Pages > Layout Templates. Both paths will take you to the same layout template builder.
    2. Click + Add Layout Template.
    3. Give your template a Name, Folder, and Tags. These features will help you stay organized and find the right layout template quickly when building future forms.
    4. From the Layout tab, select a responsive layout from the Import Layout dropdown and click Import Now.Download responsive Layout Templates
    5. Customize the template code for your business needs.
    6. Make changes in the Form tab, if needed.
    7. Click Save. As you make edits, click Preview Changes to see a live mock-up of the edits in your browser.

Responsive Template - Gated download

Applying your Template

When your template is finished, go to Landing Pages and apply it to a new or existing landing page. In Step 3, Content Layout, select your template.

Landing Page Creation

How Layout Templates, Forms, and Landing Pages Work Together

Let us take a final look at how all of those pieces fit together to create a full web experience.

Pardot - Form, Landing Page, Layout Template.png

]]>
https://absyz.com/pardot-understanding-layout-templates/feed/ 0
Displaying standard report data in Lightning component https://absyz.com/displaying-standard-report-data-in-lightning-component/ https://absyz.com/displaying-standard-report-data-in-lightning-component/#respond Mon, 30 Dec 2019 15:37:25 +0000 http://blogs.absyz.com/?p=10822

Sometime we might end up the slow performance of showing custom charts by querying the object data- if the data is in millions. The best way to improve the performance is by creating a standard report and getting data of report to apex with ReportManager class.
we can also pass the filter values based on your requirement and display charts in the Lightning component with the help of Javascript chartCanvas class.

 

blog-4

FYI : The above charts are constructed with standard reports- Matrix and Summary type.

 

 

 

 

 

 

 

 

 

We need to consider some standard classes.

ReportManager class helps to run a report synchronously or asynchronously.

ReportResults class contains the results of running a report.

ReportFilter class contains information about a report filter, including column, operator, and value.

and some Methods of above classes

 

describeReport(reportId) Retrieves report, report type, and extended metadata for a tabular, summary, or matrix report.

Example:Reports.ReportDescribeResult describe =  Reports.ReportManager.describeReport(reportId);

getReportMetadata() Returns metadata about the report, including grouping and summary information.

Example:Reports.ReportMetadata reportMd = describe.getReportMetadata();

getReportFilters() Returns a list of each custom filter in the report along with the field name, filter operator, and filter value.

Example: Reports.ReportFilter filterlist = reportMd.getReportFilters()[0];
filterlist .setValue(filterId);

Apex code

 

[sourcecode language=”java”]
@AuraEnabled
public static string Getreportplanned(String AccId){
Report repRec=[SELECT Id,Name,DeveloperName from Report where DeveloperName=:system.Label.CLNOV19SLS04];
string reportPlannedId=repRec.Id;
string filterId=String.valueOf(AccId).substring(0, 15);
// Retrieves report metadata
Reports.ReportDescribeResult describe = Reports.ReportManager.describeReport(reportPlannedId);
Reports.ReportMetadata reportMd = describe.getReportMetadata();
// Add/Override filters
Reports.ReportFilter filterlist = reportMd.getReportFilters()[0];
filterlist .setValue(filterId);
//and Run report
Reports.ReportResults reportResult = Reports.ReportManager.runReport(reportPlannedId,reportMd);
system.debug(‘ReportResultsJSON’+JSON.serialize(reportResult));
return JSON.serialize(reportResult);
}

[/sourcecode]

Aura component

To load chart on page load.

 

<ltng:require scripts="{!$Resource.chartjs}" afterScriptsLoaded="{!c.createPlannedchart}"/>

For displaying the report

 

 <canvas id="chartCanvas" height="300" width="300"></canvas>

To view standard report

[sourcecode language=”java”]
View Report
[/sourcecode]


Js Code

[sourcecode language=”java”]
var urlPlannedreport = ‘/lightning/r/Report/’+getPlannedReportId+’/view?queryScope=userFolders&fv0=’+accIdsub;

createPlannedchart: function(component) {
var chartCanvas = component.find(“plannedChart”).getElement();
var action = component.get(“c.getreportplanned”);

action.setParams({
“AccId”: component.get(“v.accrecId”)
});
action.setCallback(this, function(response) {
var state = response.getState();
if (state === “SUCCESS”) {
var reportResultData = JSON.parse(response.getReturnValue());
// alert(JSON.stringify(reportResultData));
var chartData = [];
var chartLabels = [];
var nullcheck = reportResultData.groupingsDown.groupings;
if (nullcheck !== null) {
for (var i = 0; i < (reportResultData.groupingsDown.groupings.length); i++) {
//Iterate and prepare the list of Labels for the chart
var labelItem = reportResultData.groupingsDown.groupings[i].label;
chartLabels.push(labelItem);
var keyTemp = reportResultData.groupingsDown.groupings[i].key;
//Prepeare the chart data to be plotted.
var valueTemp = reportResultData.factMap[keyTemp + “!T”].aggregates[0].value;
chartData.push(valueTemp);
}
}
//Construct chart-doughnut/bar
var chart = new Chart(chartCanvas, {
type: ‘bar’,
data: {
labels: chartLabels,
datasets: [{
label: “Count”,
data: chartData,
backgroundColor: [
“#52BE80”,
“#76D7C4”,
“#1E8449”,
“#2ECC71”,
“#FFB74D”,
“#E67E22”,
“#F8C471”,
“#3498DB”,
“#00BCD4”,
“#D32F2F”,
“#82E0AA”,
“#AFB42B”
]
}]
},
options: {
responsive: true,
title: {
display: true,
text: ‘Planned Visits’
},
scales: {

yAxes: [{
ticks: {
// max: 100,
stepSize: 25,
beginAtZero: true
},
scaleLabel: {
display: true,
labelString: ‘Visits Count’
},
}],
xAxes: [{
scaleLabel: {
display: true,
labelString: ‘Created Date’
}
}]
},
maintainAspectRatio: false,
legend: {
display: false,
position: “right”,
fullWidth: false,
}
}
});

} else(state === “ERROR”) {
console.log(‘Problem, response state: ‘ + state);
}

});
$A.enqueueAction(action);
}
[/sourcecode]

You can change the look of charts with the help of chartcanvas methods.

 

 

 

]]>
https://absyz.com/displaying-standard-report-data-in-lightning-component/feed/ 0