- Mobile Plugin Framework
- Connect the Visual Builder plugin to the Oracle Fusion Field Service Plugin API
Connect the Visual Builder plugin to the Oracle Fusion Field Service Plugin API
To use the Plugin API in a custom plugin, you must include the Oracle Fusion Field Service connector file in your plugin.
- Create the ofsc-connector.js file in the Resources > js folder in Visual Builder.
-
Copy the following example code into the ofsc-connector.js file:
/** * @licence * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * Oracle Technology Network Developer License Terms (https://www.oracle.com/downloads/licenses/production-modify-license.html) */ "use strict"; define([ ], () => { const OFSC_API_VERSION = 1; class OfscConnector extends EventTarget { constructor() { super(); window.addEventListener("message", this.onPostMessage.bind(this), false); this._currentCommunicationCallback = null; this._currentCommunicationPromise = null; } /** * @param {Object} dataToSend * @returns {Promise.<*>} */ sendMessage(dataToSend) { if (this._currentCommunicationPromise) { return Promise.reject(new Error('Communication channel is busy')); } this._currentCommunicationPromise = new Promise((resolve, reject) => { const originUrl = this.constructor._getOriginUrl(); const origin = originUrl ? this.constructor._getOrigin(originUrl) : '*'; this._currentCommunicationCallback = (receivedData) => { this._currentCommunicationCallback = null; this._currentCommunicationPromise = null; if (receivedData instanceof Error) { return reject(receivedData); } if (receivedData.method && receivedData.method === 'error') { return reject(receivedData); } return resolve(receivedData); }; dataToSend.apiVersion = OFSC_API_VERSION; parent.postMessage(dataToSend, origin); this.dispatchEvent(new CustomEvent('debugMessageSent', { detail: dataToSend })); }); return this._currentCommunicationPromise; } onPostMessage(event) { // Ignore internal JET messages if (event.source === window) { return; } if (typeof event.data === 'undefined') { this.dispatchEvent(new CustomEvent('debugIncorrectMessageReceived', { detail: "No data" })); if (this._currentCommunicationCallback) { this._currentCommunicationCallback(new Error('No data')); return; } return false; } const data = this.constructor.parseJSON(event.data, null); if (data === null) { if (this._currentCommunicationCallback) { this._currentCommunicationCallback(new Error('Incorrect JSON')); return; } this.dispatchEvent(new CustomEvent('debugIncorrectMessageReceived', { detail: event.data })); return false; } this.dispatchEvent(new CustomEvent('debugMessageReceived', { detail: data })); if (this._currentCommunicationCallback) { this._currentCommunicationCallback(data); } else { this.dispatchEvent(new CustomEvent('messageFromOfs', { detail: data })); } } static generateCallId() { return window.btoa(String.fromCharCode.apply(null, window.crypto.getRandomValues(new Uint8Array(16)))); } static _getOrigin(url) { if (typeof url === 'string' && url !== '') { if (url.indexOf("://") > -1) { return (window.location.protocol || 'https:') + url.split('/')[2]; } else { return (window.location.protocol || 'https:') + url.split('/')[0]; } } return ''; } static _getOriginUrl() { if (document.referrer) { return document.referrer; } if (document.location.ancestorOrigins && document.location.ancestorOrigins[0]) { return document.location.ancestorOrigins[0]; } return null; } static parseJSON(text, defaultValue = null) { try { return JSON.parse(text); } catch (err) { return defaultValue; } } } return OfscConnector; });
-
Include a new file in the plugin.
This screenshot shows the JSON tab in Visual Builder, where a new file is added:
-
Create a new Action Chain with the name
initOFSConnector.
This screenshot shows the new Action Chain, initOFSConnector:
-
Add the following chain using the UI or Code tab:
- UI tab: This screenshot shows the new Action Chain added to the plugin
using the UI:
- Code tab:
define([ 'vb/action/actionChain', 'resources/js/ofsc-connector' ], ( ActionChain, OfscConnector ) => { 'use strict'; class initOfsConnector extends ActionChain { /** * @param {Object} context */ async run(context) { const { $page, $flow, $application, $constants, $variables } = context; const callFunction = await this.pluginStartAction(context); } /** * @private * @param {Object} context */ async pluginStartAction(context) { const { $page, $flow, $application, $constants, $variables } = context; const ofscConnector = new OfscConnector(); $application.variables.ofscConnector = ofscConnector; ofscConnector.sendMessage({ method: 'ready', sendInitData: true }).then(this.onMessage.bind(this, $application)); } /** * @private */ onMessage($application, messageData) { if (!messageData) { return; } switch (messageData.method) { case 'init': this.initPlugin($application, messageData); break; case 'open': this.openPlugin($application, messageData); break; // default: // console.log('method', messageData.method, 'data', messageData); } } /** * @private */ initPlugin($application, jsonData) { this.sendInitEnd($application.variables.ofscConnector); } /** * @private */ sendInitEnd(ofscConnector) { ofscConnector.sendMessage({ method: 'initEnd' }); } /** * @private */ openPlugin($application, jsonData) { this.setUserLocale(jsonData.user); } /** * @private */ setUserLocale(user) { const currentLocale = window.localStorage.getItem('ofs_plugin_locale'); const locale = user.locale || user.languageCode || window.navigator.language; if (currentLocale !== locale) { window.localStorage.setItem('ofs_plugin_locale', locale); window.location.href = this.getPluginUrl(); } } /** * @private */ getPluginUrl() { const url = window.location.href; if (url.endsWith('/')) { return url + 'index.html'; } return url; } } return initOfsConnector; });
- UI tab: This screenshot shows the new Action Chain added to the plugin
using the UI: