DevBlog

Integrieren Sie die geführten interaktiven Dialoge der DocBridge® Communication Suite in Salesforce.com

Thorsten Meudt |

Dieses Tutorial zeigt Ihnen Schritt für Schritt, wie Sie die Guided-Interactive-(GID)-Funktionalität der DocBridge® Communication Suite mithilfe von Visualforce und Apex in Salesforce einbetten können.

Hinweis: Das Tutorial gilt für DBCS Spring 2025 (Version 1.3) und spätere Versionen.

Voraussetzungen

  • Salesforce Org mit aktiviertem Visualforce.
  • DocBridge® Communication Suite mit Guided Interactive Plugin.
  • Benutzerdefinierte Einstellung/Metadaten DocBridgeGuidedInteractive__c mit folgenden Inhalten::
    DocBridgeCommunicationSuiteBaseUrl__c
    GuidedInteractiveIntegrationBaseUrl__c
    GuidedInteractiveAuthClientId__c
    GuidedInteractiveAuthClientSecret__c
    GuidedInteractiveAuthClientScope__c
    KeyCloakTokenUrl__c (or your OAuth token endpoint)
  • Benutzerdefiniertes Objekt CpGuidedInteractiveQueries__c (oder Metadaten) zum Zuordnen von SObject-Typen zu SOQL-Vorlagen:
      Felder: ObjectType__c, Query_String__c
  • Apex-Dienstprogramm CpGidTools zum Abrufen von SOQL-Vorlagen und Serialisieren von Ergebnissen.

High-Level-Ablauf

1. Benutzeraktion

  • Klicken Sie auf eine Schaltfläche/einen Link „Launch Guided Interactive” auf einer Salesforce-Datensatzseite.

2. Visualforce Seite

  • Lädt DocBridge JS/CSS Bibliotheken.
  • Initialisiert die Ländereinstellung basierend auf html-lang.
  • Ruft "Apex getters" auf, um Folgendes abzurufen:
    - OAuth token über Keycloak (Client-Anmeldedaten).
    - Upstream der JSON-Daten aus dem aktuellen Salesforce-Datensatz.
  • Ruft GuidedInteractive.createGuidedInteractive() auf.

3. DocBridge® Communication Suite Backend

  • Verwendet das Token zur Authentifizierung.
  • Lädt die angegebene DFF-Vorlage (template).
  • Führt Upstream-Daten zusammen.
  • Rendert den interaktiven Dialog auf der VF-Seite.

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 Seite

<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>

Lösungsdurchgang

1. Benutzerdefinierte Einstellungen

  • Zentralisieren Sie alle URLs & OAuth-Anmeldedaten in DocBridgeGuidedInteractive__c.
  • Ermöglicht Administratoren die Konfiguration von Endpunkten ohne Änderung des Codes.

2. CpGidContact Controller

  • Constructor: Liest benutzerdefinierte Einstellungen, erfasst die Datensatz-ID und bestimmt den SObject-Typ.
  • Getters: Gibt Basis-URLs und Token über Visualforce "merge" Felder frei ({!getToken}, {!getDbcsBaseBaseUrl}, usw.).
  • getContactInitiateGid(): Nutzt CpGidTools, um eine SOQL-Vorlage (template) abzurufen, auszuführen und die Ergebnisse für GIDs "upstreamData" zu serialisieren.

3. CpGidTools Dienstprogramm

  • getQueryStringForObjectType():  Fragt Ihre CpGuidedInteractiveQueries__c Tabelle nach dem richtigen SOQL-Snippet basierend auf SObject ab.
  • getPayload(): Führt beliebige SOQL-Zeichenfolgen aus und serialisiert die zurückgegebenen Datensätze in JSON.

4. Visualforce Seite

  • Skript-/CSS-Einbindungen: Laden Sie nur das, was Sie benötigen – Kern-Utilities, optionale Plugins, Cockpit-UI und das Guided Interactive-Integrationspaket (bundle).
  • Spracheneinstellung (Locale Handling): Stellt sicher, dass die Beschriftungen/Meldungen des Dialogfelds mit der Sprache des Benutzers übereinstimmen.
  • createGuidedInteractive(): Übergibt Ihr Token, Basis-URLs, DFF-Vorlagen-Locator, optionale Upstream-Daten und die Übermittlungskonfiguration.

 

Anpassung & Best Practices

  • Dynamische Vorlagen (Dynamic Templates): Leiten Sie den dffTemplate Pfad aus einem benutzerdefinierten Feld oder Datensatztyp, um mehrere Dokumentformulare zu unterstützen.
  • Automatische Erkennung der Spracheinstellung: Verwenden Sie ApexPages.currentPage().getHeaders().get('Accept-Language') im Controller, um die bevorzugte Sprache des Browsers zu übergeben.
  • Verbesserte Fehlerbehandlung: Umschließen Sie createGuidedInteractive mit try/catch, zeigen Sie bei Fehlern benutzerfreundliche Warnmeldungen oder Fallback-Inhalte an.
  • Sicherheit: Stellen Sie sicher, dass Ihre Keycloak-Client-Anmeldedaten sicher gespeichert sind geschützte benutzerdefinierte Einstellungen (Protected Custom Settings) und wechseln Sie sie regelmäßig.
  • Theming: Überschreiben oder erweitern Sie das geladene CSS, um den Dialog an Ihr Unternehmensbranding anzupassen.
  • Testing: Verwenden Sie "mock HTTP responses" in Apex tests für die Token-Anforderung und SOQL-Abfragen; halten Sie eine Abdeckung von mindestens 75 % ein.