DevBlog

Integrate DBCS Guided Interactive Dialogs with Salesforce.com

Thorsten Meudt |

This tutorial walks you through embedding DocBridge® Communication Suite’s Guided Interactive (GID) functionality into Salesforce via Visualforce and Apex.

Note: The tutorial applies to DBCS Spring 2025 (Version 1.3) and later.

Prerequisites

  • Salesforce Org with Visualforce enabled.
  • DocBridge® Communication Suite account with Guided Interactive plugin.
  • Custom Setting / Metadata DocBridgeGuidedInteractive__c containing:
    DocBridgeCommunicationSuiteBaseUrl__c
    GuidedInteractiveIntegrationBaseUrl__c
    GuidedInteractiveAuthClientId__c
    GuidedInteractiveAuthClientSecret__c
    GuidedInteractiveAuthClientScope__c
    KeyCloakTokenUrl__c (or your OAuth token endpoint)
  • Custom Object CpGuidedInteractiveQueries__c (or metadata) mapping SObject types to SOQL templates:
      Fields: ObjectType__c, Query_String__c
  • Apex Utility CpGidTools to fetch SOQL templates and serialize results.

High-Level Flow

1. User Action

  • Click a “Launch Guided Interactive” button/link on a Salesforce record page.

2. Visualforce Page

  • Loads DocBridge JS/CSS libraries.
  • Initializes locale based on html-lang.
  • Calls Apex getters to fetch:
    - OAuth token via Keycloak (client credentials).
    - Upstream JSON data from the current Salesforce record.
  • Invokes GuidedInteractive.createGuidedInteractive().

3. DocBridge® Communication Suite Backend

  • Uses the token to authenticate.
  • Loads the specified DFF template.
  • Merges upstream data.
  • Renders the interactive dialog in the VF page.

Apex Controller CpGidContact

/*
 BLUEPRINT provided without maintenance, support or warranty
 (C) Compart GmbH
 Controller for Visualforce page that supplies URLs, OAuth token,
 and upstream data for Guided Interactive.
*/
public with sharing class CpGidContact {
    // Record Id of the current SObject (Contact, Opportunity, etc.)
    public Id id { get; set; }
    // Configuration values from custom setting DocBridgeGuidedInteractive__c
    private String dbcsBaseUrl;
    private String gearBaseUrl;
    private String keyCloakTokenUrl;
    private String documentViewerBaseUrl;
    private String giIntegrationBaseUrl;
    private String clientId;
    private String clientSecret;
    private String scope;
    private String sourceObjectType;
    /** 
     * Constructor: initialize URLs/credentials and capture record Id/type.
     */
    public CpGidContact() {
        // Load all endpoints & credentials from custom setting
        DocBridgeGuidedInteractive__c cfg = DocBridgeGuidedInteractive__c.getInstance();
        dbcsBaseUrl           = cfg.DocBridgeCommunicationSuiteBaseUrl__c;
        gearBaseUrl           = cfg.DocBridgeGearBaseUrl__c;
        keyCloakTokenUrl      = cfg.KeyCloakTokenUrl__c;
        documentViewerBaseUrl = cfg.DocumentViewerBaseUrl__c;
        giIntegrationBaseUrl  = cfg.GuidedInteractiveIntegrationBaseUrl__c;
        clientId              = cfg.GuidedInteractiveAuthClientId__c;
        clientSecret          = cfg.GuidedInteractiveAuthClientSecret__c;
        scope                 = cfg.GuidedInteractiveAuthClientScope__c;
        // Capture record Id from URL and determine its type
        id = ApexPages.CurrentPage().getParameters().get('Id');
        sourceObjectType = id.getSObjectType().getDescribe().getName();
    }
    /** @return Base URL for DocBridge Communication Suite assets */
    public String getDbcsBaseBaseUrl() {
        return dbcsBaseUrl;
    }
    /** @return Base URL for Guided Interactive integration assets */
    public String getGiIntegrationBaseUrl() {
        return giIntegrationBaseUrl;
    }
    /**
     * @return OAuth access token via client_credentials flow
     *   against Keycloak (or your IdP).
     */
    public String getToken() {
        return requestToken(clientId, clientSecret, scope, keyCloakTokenUrl);
    }
    /**
     * @return JSON payload for upstreamData: executes
     *   a SOQL template fetched from CpGidTools.
     */
    public String getContactInitiateGid() {
        CpGidTools tools = new CpGidTools();
        String baseSoql  = tools.getQueryStringForObjectType(sourceObjectType);
        String fullSoql  = baseSoql + '\'' + id + '\'';
        return tools.getPayload(fullSoql);
    }
    /**
     * Helper: performs a POST to tokenUrl with client_id,
     * client_secret, scope, grant_type=client_credentials,
     * then parses JSON response for access_token.
     */
    private static String requestToken(
        String clientId,
        String clientSecret,
        String scope,
        String tokenUrl
    ) {
        // Build URL-encoded form body
        String body = 'client_id='     + EncodingUtil.urlEncode(clientId, 'UTF-8')
                    + '&client_secret=' + EncodingUtil.urlEncode(clientSecret, 'UTF-8')
                    + '&scope='         + EncodingUtil.urlEncode(scope, 'UTF-8')
                    + '&grant_type=client_credentials';
        HttpRequest req = new HttpRequest();
        req.setEndpoint(tokenUrl);
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
        req.setHeader('Content-Length', String.valueOf(body.length()));
        req.setBody(body);
        HttpResponse res = new Http().send(req);
        if (res.getStatusCode() == 200) {
            // Deserialize into generic map and extract access_token
            Map<String,Object> parsed = (Map<String,Object>) JSON.deserializeUntyped(res.getBody());
            return (String) parsed.get('access_token');
        }
        // On failure, log and return empty
        System.debug('Token request failed: ' + res.getStatus());
        return '';
    }
}

Helper Class CpGidTools

/*
 BLUEPRINT provided without maintenance, support or warranty
 (C) Compart GmbH
 Utility methods to fetch SOQL templates and serialize data.
*/
public with sharing class CpGidTools {
    /** Default constructor (no init needed). */
    public CpGidTools() {}
    /**
     * Fetch the SOQL query template for the given objectType
     * from custom object CpGuidedInteractiveQueries__c.
     *
     * @param objectType  SObject API name (e.g. 'Contact')
     * @return            Query_String__c, e.g. "SELECT Id, Name FROM Contact WHERE Id = "
     */
    public String getQueryStringForObjectType(String objectType) {
        String soql = 'SELECT Query_String__c '
                    + 'FROM CpGuidedInteractiveQueries__c '
                    + 'WHERE ObjectType__c = \'' + objectType + '\'';
        CpGuidedInteractiveQueries__c record =
            (CpGuidedInteractiveQueries__c) Database.query(soql);
        return record.Query_String__c;
    }
    /**
     * Execute a full SOQL and serialize the results to JSON.
     * Used for upstreamData in the Guided Interactive dialog.
     *
     * @param queryString  Complete SOQL including WHERE clause
     * @return             JSON string of the resulting sObject list
     */
    public String getPayload(String queryString) {
        List<sObject> records = Database.query(queryString);
        return JSON.serialize(records);
    }
}

Visualforce Page

<apex:page controller="CpGidContact"
           html-lang="en-US"
           showHeader="false"
           standardStylesheets="false">
  <!-- 1) Core DBCS scripts -->
  <script src="{!getDbcsBaseBaseUrl}/dbcs/ui/core/@compart/cp-base-utils/dist/index.js"></script>
  <script src="{!getDbcsBaseBaseUrl}/dbcs/ui/core/@compart/cp-base-logging/dist/index.js"></script>
  <script src="{!getDbcsBaseBaseUrl}/dbcs/ui/core/@compart/cp-web-utils/dist/index.js"></script>
  <script src="{!getDbcsBaseBaseUrl}/dbcs/ui/core/@compart/cp-web-wui/dist/index.js"></script>
  <script src="{!getDbcsBaseBaseUrl}/dbcs/ui/core/@compart/cp-web-cockpit/dist/index.js"></script>
  <!-- 2) DBCS plugins -->
  <script src="{!getDbcsBaseBaseUrl}/dbcs/ui/plugins/@compart/docbridge-central-2-plugin-resource-director/dist/index.bundle.js"></script>
  <script src="{!getDbcsBaseBaseUrl}/dbcs/ui/plugins/@compart/docbridge-central-2-plugin-document-viewer/dist/webpack/index.bundle.js"></script>
  <script src="{!getDbcsBaseBaseUrl}/dbcs/ui/plugins/@compart/docbridge-central-2-plugin-impress/dist/webpack/index.bundle.js"></script>
  <!-- 3) DBCS CSS -->
  <link rel="stylesheet" href="{!getDbcsBaseBaseUrl}/dbcs/ui/core/@compart/cp-web-cockpit/dist/bundle.css" />
  <link rel="stylesheet" href="{!getDbcsBaseBaseUrl}/dbcs/ui/core/@compart/cp-web-cockpit/dist/compart.css" />
  <link rel="stylesheet" href="{!getDbcsBaseBaseUrl}/dbcs/ui/core/@compart/cp-web-wui-themes/dist/theme-base.css" />
  <!-- 4) Guided Interactive integration -->
  <script src="{!getDbcsBaseBaseUrl}/dbcs/ui/plugins/@compart/docbridge-central-2-plugin-guided-interactive/dist/integration/index.js"></script>
  <!-- 5) Container for GID UI -->
  <div id="gid" style="height: 98.5vh; border: 1px solid lightgray;"></div>
  <script>
    window.onload = async () => {
      // Load locale files (fallback to 'en' if unsupported)
      await cpWebCockpit.LocaleHandler.initLocaleFiles('{!getGiIntegrationBaseUrl}/');
      await cpWebCockpit.LocaleHandler.loadLocale('en-US', '{!getGiIntegrationBaseUrl}/');
      // Initialize the Guided Interactive dialog
      initDialog();
    };
    async function initDialog() {
      // 1) Fetch OAuth token
      const token = '{!getToken}';
      // 2) Fetch upstream JSON payload (if any)
      const payload = '{!contactInitiateGid}';
      let upstreamData = undefined;
      if (payload) {
        upstreamData = { content: payload, type: 'application/json' };
      }
      // 3) Create the GID dialog
      GuidedInteractive.createGuidedInteractive(
        document.getElementById('gid'),
        {
          token,
          config: {
            dbcsBaseUrl: '{!getDbcsBaseBaseUrl}/dbcs',
            // Adjust to your DFF template locator
            dffTemplate: 'resdir://static/example/components/master.dff',
            upstreamData,
            submitConfig: {
              library: 'processing',
              worklet: 'submit'
            }
          }
        }
      );
    }
  </script>
</apex:page>

Walkthrough

1. Custom Settings

  • Centralize all URLs & OAuth credentials in DocBridgeGuidedInteractive__c.
  • Allows admins to configure endpoints without changing code.

2. CpGidContact Controller

  • Constructor: Reads custom setting, captures record Id, and determines SObject type.
  • Getters: Expose Base URLs and token via Visualforce merge fields ({!getToken}, {!getDbcsBaseBaseUrl}, etc.).
  • getContactInitiateGid(): Leverages CpGidTools to fetch a SOQL template, run it, and serialize the results for GID’s upstreamData.

3. CpGidTools Utility

  • getQueryStringForObjectType(): Queries your CpGuidedInteractiveQueries__c table for the proper SOQL snippet based on SObject.
  • getPayload(): Executes any SOQL string and JSON-serializes the returned records.

4. Visualforce Page

  • Script/CSS Includes: Load only what you need—core utils, optional plugins, cockpit UI, and the Guided Interactive integration bundle.
  • Locale Handling: Ensures the dialog’s labels/messages match the user’s language.
  • createGuidedInteractive(): Passes in your token, base URLs, DFF template locator, optional upstream data, and submit config.

 

Customization & Best Practices

  • Dynamic Templates: Drive the dffTemplate path from a custom field or record type to support multiple document forms.
  • Automatic Locale Detection: Use ApexPages.currentPage().getHeaders().get('Accept-Language') in the controller to pass the browser’s preferred language.
  • Enhanced Error Handling: Wrap createGuidedInteractive in try/catch, display friendly alerts or fallback content on failure.
  • Security: Ensure your Keycloak client credentials are stored securely (Protected Custom Settings) and rotate them periodically.
  • Theming: Override or extend the loaded CSS to align the dialog with your corporate branding.
  • Testing: Use mock HTTP responses in Apex tests for the token request and SOQL queries; maintain at least 75% coverage.