2The Plug-In Framework
Basics of the Plug-In Framework
Oracle Field Service Cloud is a highly developed application that can be customized for the unique purposes and specialized business needs of organizations. That extensibility is achieved in part through the use of plug-ins, which can perform actions not found in the standard solution. Plug-ins appear as selectable links on the application. They open a new window, tab, or frame in a browser where an external HTML5 application is executed.
Traditional one-way communication when the plug-in receives data from OFSC via HTTP GET and POST parameters.
Two-way communication using Oracle Field Service Cloud Plug-in API
Integration with Oracle Field Service Cloud through an API and, therefore, the ability to perform complex tasks which could previously be performed only by internal plug-ins
Ability to work offline with Mobility Cloud Service
Plug-in development by your organization or third-party developers without requiring Oracle developers
Resource
Activity
Inventory (does not include Parts Catalog)
Activity list
Inventory list
Activity list
Edit/View activity
Inventory grid
Add/Details inventory
Requirements to Use a Plug-In API
This topic provides the requirements to develop a plug-in for Oracle Field Service Cloud.
The plug-in URL must point to the main page of the plug-in source and use the HTTPS protocol. The main page must be accessible through the configured plug-in URL.
The main page must be a valid HTML/XHTML page that can load JavaScript code sources, static resources (images, .css style sheets ), or contain them directly.
A valid offline plug-in must run JavaScript code in the main page interacting with Mobility Cloud Service.
To make the plug-in accessible in the offline mode, the <html> tag should contain a manifest attribute whose value points to a file of the special offline manifest format describing what resource files must be cached by the browser for offline. The offline plug-in is processed entirely outside of Oracle Field Service Cloud.
You can also load resources for offline plug-in using Service Workers. However, you must be aware of their availability in different versions of browsers.
The plug-in can save data for offline use locally on the user’s device by means of cookies, local storage, or indexed DB.
The plug-in is loaded into an iframe. The URL points outside the Oracle Field Service Cloud domain, therefore its application cache, cookies, local storage, and indexed DB are separated from those of the application and cannot interfere with them according to the Same origin policy. Most properties of the parent window are also unavailable for plug-in JavaScript code. As a result, the only way to interact with Mobility Cloud Service is through the plug-in API.
It’s required to use a valid certificate, not a self-signed certificate.
The ability to work offline must be maintained by the plug-in developer. Oracle Field Service Cloud only provides the ability to load all plug-in files on start, by opening the plug-in in an invisible iframe. The plug-in developer must take care of the application cache state and use the plug-in properly in offline mode.
Placeholders used in the URL
{user_id }, {uid}: ID of the current user
{date}: Current date
{timestamp}: Current timestamp in ISO format
{uname}: User name
{ulanguage}: ID of the user 's language
{ulogin}: User login
{su_zid}: User timezone
{allow_desktop_notifications}: Parameter defining whether the user allows HTML5 notifications
{allow_vibration}: Parameter defining whether the user allows vibration alerts
Using HTTPS
The plug-in must be hosted on an HTTPS server. If your web server is not configured to use HTTPS, follow your web server documentation and configure it. Further, ensure that the plug-in is hosted on the same port as Oracle Field Service Cloud, which is port 443.
Basic HTTP Authentication
The basic HTTP authentication method is a standard method, which is part of the HTTP 1.0 standard ( RFC 1945) called Basic Access Authentication. It works over HTTPS as well.
Oracle Field Service Cloud: In the Add action link and Edit action link windows, select HTTP Basic authentication type, and fill up the Login and Password fields with valid values. These credentials are encrypted and saved to the Oracle Field Service Cloud database.
Server Side: Configure the web server on which the plug-in sources are hosted to return the HTTP 401 Unauthorized status, if you are requesting the configured plug-in URL without the credentials. See the NGINX and Apache documents for details. The server must return the plug-in content if its URL is requested with the HTTP header.
Authorization: Basic bXlsb2dpbjpteXBhc3M=
Where bXls ... is a valid Base64 - encoded pair of login:password. The credentials configured for the plug-in in the Add action link and Edit action link windows must be accepted as valid.-
Client Side: When the user logs in to Mobility Cloud Service, Oracle Field Service Cloud reads the credentials from the database and loads the plug-in URL into the hidden iframe as follows: <iframe src="https://mylogin:mypass@example.com/myPlugin.php"/> This way, the browser loads the plug-in sources over HTTPS using HTTP Basic Authentication:
GET /myPlugin.php HTTP/1.1 Host: example.com Authorization: Basic bXlsb2dpbjpteXBhc3M=
HMAC Authentication
HMAC (Hash - based message authentication code) lets you sign HTTP requests and their GET parameters. It authenticates to see that the data is not forged and is not received from an unauthorized source.
The MAC signature (digest) is added as an additional GET parameter at the end of query string: <!CDATA[[http://www.example.com/path?user=test§ion=D%26G&activity=33&hmac=D2BJn9P1EcLhaFrNhbAzCQTVQXCCwCBQsrg8V6h4YoU%3D]]>
.
HMAC function algorithm
hmac = BASE64(HMAC - SHA - 256(data, SHA256(SecretKey)))
. SHA - 256 accepts SecretKey as a string and returns the hash string. The secret key is configured per plug-in in the
Add action link and
Edit action link windows in Core Manage Cloud Service, hashed by SHA256, encrypted and stored in the database. HMAC-SHA-256 accepts data and key as strings and returns a binary array of HMAC signature. BASE64 accepts the binary array and returns BASE64 encoded string. Data for HMAC generation is query resource location with query parameters sorted lexicographically:
Remove the protocol identifier from the URL together with colon and slashes ( http:// or https:// ).
Remove the resource name and port from the URL.
Append query location to the output string.
If there are query parameters append the character ? to the output string.
Decode every name and value for URL parameters.
Sort the list of parameters alphabetically by name.
For each name/value pair:
Append the encoded name to the output string.
Append the ‘=’ character to the output string.
Append the encoded value to the output string.
If there are more key/value pairs remaining, append an & character to the output string.
http://www.example.com/path?user=test§ion=D%26G&activity=33 => www.example.com/path?user=test§ion=D%26G&activity=33
www.example.com/path?user=test§ion=D%26G&activity=33 => /path?user=test§ion=D%26G&activity=33
data = '/path'
data = '/path?'
['user'='test','section'='D&G','activity'=33]
['activity'=33,'section'='D&G','user'='test']
['activity'=33,'section'='D&G', 'user'='test'] => data
data = '/path? activity'
data = '/path? activity='
data = '/path? activity=33'
data = '/path? activity=33&'
data = '/path? activity=33§ion=D %26G&user=test'
The full signed URL is 'http://www.example.com/path?user=test§ion=D%26G&activity=33&hmac=D2BJn9P1EcLhaFrNhbAzC QTVQXCCwCBQsrg8V6h4YoU%3D'
Plug-In API Specification
The plug-in API is based on cross-window messaging with postMessages, which can be sent by Oracle Field Service Cloud and received by the plug-in and vice-versa. Messages are sent by JavaScript code using window.postMessage() method, and received by subscribing to messages using window.addEventListener().
Message format
The following message format is supported:window.frames['my_plugin'].postMessage('{"apiVersion": 1,"method": "open","entity":"activity","resource":{"pid": 5000038}}", targetOrigin);
function getPostMessageData(event) { var data = JSON.parse(event.data); switch (data.method) { case 'open': pluginOpen(data); break; default: showError(); } }; window.addEventListener("message", _getPostMessageData, false);
{ "apiVersion": 1, "method": "open", "entity": "activity", "resource": { "pid": 5000038 }, "inventories": { "20997919": { "invid": 20997919, "inv_pid": 5000038, } } }
apiVersion, method , entity – special fields
resource, inventories – entity data collections available only for the 'open' and 'close' methods
apiVersion – version of the plug-in API used for interaction between Mobility Cloud Service and the plug-in. It defines the available methods and data
method – describes the action initiated by Oracle Field Service Cloud or the plug-in and the actions to be performed on the other side
entity – name of the Oracle Field Service Cloud entity to be processed by the plug-in
Available Methods
Methods initiated by Oracle Field Service Cloud
init: Notifies the plug-in that it should perform initialization. For example, load metadata or offline files from the server.
open: The plug-in content is to be shown on the Mobility Cloud Service screen.
error: The data submitted by the plug-in is invalid or internal errors have occurred.
wakeup: Notifies the plug-in about the presence of network connectivity. The plug-in can perform its own synchronization after receiving this message.
Methods initiated by the plug-in
ready: The plug-in has been loaded and is ready to receive messages.
initEnd: The plug-in notifies Oracle Field Service Cloud that it has finished initialization and can be suspended until opened by the user.
close: The plug-in submits data after which its window will be closed if the data is valid.
sleep: If the plug-in is not able to synchronize with its own server due to absence of network connectivity, it can notify Oracle Field Service Cloud to wake it up in the background when connectivity is available.
Initialization
Create an iframe, if required
Execute the ready method
Execute the init method
Execute the initEnd method; here, the wakeupNeeded parameter is set to ‘true’
Destroy the iframe, if it is created
ready Method
ready Method
{ "apiVersion": 1, "method": "ready" }The optional “dataItems” field can be included in the message to limit the amount of data sent to plug-in in open method. See “Limit the amount of data sent to the plug-in” for more details.
Limit the Amount of Data Sent to the Plug-In
Mobility sends all data available for entity collections, with the "open" message. The data is sent according to the layout, where the plug-in's action link is placed and visibility of properties on plug-in API layouts. This means that when plug-in is opened from Activity List screen, the data of all activities for selected day's queue will be sent with the data of all non-scheduled activities of selected resource. To reduce the amount of data collected and serialized by Mobility and un-serialized by a plug-in, the new optional parameter dataItems is added for the "ready" message. This decreases loading time of the plug-in. The value of this parameter defines which items are present in the entity collections. Using this parameter the plug-in can prevent Mobility from sending certain items in available entity collections, but it can't broaden the set of entity collections sent to the plug-in. This set is predefined and depends on the screen from which the plug-in is opened.
Hiding the Header When the Plug-In is Open
Render the header itself
Based on the business flow, decide whether the user can leave the plug-in
If flag value is set to TRUE, then the Oracle Field Service Mobility Cloud Service header is shown.
If flag value is set to FALSE, then the Oracle Field Service Mobility Cloud Service header is hidden.
Preventing the User from Using Back
If flag value is set to TRUE, then the Oracle Field Service Mobility Cloud Service Back button is shown and the navigation is not locked.
If flag value is set to FALSE, then the Oracle Field Service Mobility Cloud Service Back button is hidden and the navigation is locked. This means, the browser's native Back and Forward buttons are disabled.
Example for ready Method
// the header is shown but the "back" button is hidden: { "apiVersion": 1, "method": "ready", "showHeader": true, "enableBackButton": false } // the header is hidden but the user can go back using browser's back button: { "apiVersion": 1, "method": "ready", "showHeader": false, "enableBackButton": true } // the header is hidden and the user can leave the plugin only when the plugin sends the "close" message via Plugin API (the browser's back button does not work): { "apiVersion": 1, "method": "ready", "showHeader": false, "enableBackButton": false }
Plug-In Action Flow
This topic describes the flow of the plug-in, when a user opens it to perform an action.
The flow of steps is as follows:
The user clicks the action link in Oracle Field Service Mobility Cloud Service.
The iframe is created, if required.
The Ready method is executed.
The Open method is executed.
The Close method is executed; here, the wakeupNeeded parameter is set to ‘true’.
The iframe is destroyed, if it is created.
Available Entities and Data Collections
The 'entity' field and entity data collections are available only for methods 'open' and 'close'. The value of the special 'entity' field depends on the Oracle Field Service Mobility Cloud Service screen on which the plug-in is used. The availability of entity data collections sent within the message data depends on the value of 'entity'.
Screen | Entity Field Value | Available Collections |
---|---|---|
Activity List | activityList |
|
Activity List > Inventory List | inventoryList | |
Activity List > Activity Details | activity |
|
Activity List > Activity Details> Inventory List | activityInventoryList | |
Activity List > Inventory List > Inventory Details | inventory |
|
Activity List > Activity Details > Inventory List > Inventory Details | activityInventory |
|
resource – element in the resource tree representing a defined company asset
activity – entity of Oracle Field Service Cloud that represents any time-consuming activity of the resource
activities – activity list
inventory – equipment that can be installed or de-installed during an activity
inventories – inventory list
File Properties Support
The "close" method is extended with the support of file properties. The file properties can be sent both through data collections and through actions. Due to performance limitations, it's not rational to send the file contents using JSON strings, so the plug-in API now accepts raw JS objects as values for PostMessage data. The original JSON strings are still supported, so backward compatibility with existing plug-ins framework implemented in release 17.2 is retained, but the file properties cannot be updated in this case.
fileName - name of the file, which is shown on the user interface
fileContents - Blob object, which contains the file contents. It can be constructed and filled with the generated data by JS code in runtime, or just obtained from file input and sent to Oracle Field Service Mobility Cloud Service without any transformation, as the File object inherits the Blob.
Example: How to Send the Uploaded File
var file = document.querySelector('input[type=file]').files[0]; window.parent.postMessage ( { apiVersion: 1, method: 'close', activity: { ccity: 'Cleveland', door_photo: { fileName: 'DCIM_20170425_203115.jpg', fileContents: file } } }, targetOrigin );
Example: How to Send the Generated File Contents
var text = '<?xml version="1.0" encoding="UTF-8"?>' + '<test></test>'; var blob = new Blob([text], { type: 'text/xml' }); window.parent.postMessage ( { apiVersion: 1, method: 'close', activity: { ccity: 'Cleveland', XML_DATA_PROP: { fileName: 'test_data.xml', fileContents: blob } } }, targetOrigin );
Example: How to Delete a File
window.parent.postMessage ( { apiVersion: 1, method: 'close', activity: { file_property: '' } }, targetOrigin );
Order of Execution of Actions
Actions are applied in the same order, as sent in the "actions" array of the plug-in. Actions are executed after applying of all data collection updates that are sent by plug-in in the same "close" message. No actions are applied if there are errors during the validation of data collections and the actions that are sent by the plug-in. All validation errors are sent to the plug-in within the "error" message. If some actions fail to execute, the remaining actions are applied, and the execution errors are sent to the plug-in within the "error" message.
Validate entity data collections.
Validate actions.
If there are any validation errors, send an "error" message to the plug-in; otherwise proceed to the next step.
Apply data collections update.
Apply actions.
If there are any errors, occurred during update of data collections or execution of actions, send "error" message to a plug-in; otherwise proceed to the next step.
Close the plug-in window.
Action Parameters
entity - must be equal to "inventory"
action - must be equal to one of the supported inventory actions (e.g. "install", "create")
{ ACTION: "INSTALL" entity: "Inventory", Inv_Aid: "" }
Creating and Deleting Inventory
Install
Deinstall
Undo install
Undo deinstall
Create
Delete
Key | Affected Collections | Description |
---|---|---|
resource | resource | Properties of the currently selected resource |
scheduledActivites | activityList | Activities, scheduled (belongs to the queue) for the selected date |
nonScheduledActivites | activityList | Non-scheduled activities, that do not belong to any date's queue |
resourceInventories | inventoryList | Inventories in the "provider" pool |
installedInventories | inventoryList | Inventories in the "install" pool |
deinstalledInventories | inventoryList | Inventories in the "deinstall" pool |
customerInventories | inventoryList | Inventories in the "customer" pool |
Support for Non-Serialized Inventory
Create an inventory assigned to a resource
Create a customer inventory linked with a specific activity
Create an inventory in the "installed" pool
Create an inventory in the "deinstalled" pool
Delete inventory
If there is no inventory of the same type (or type and model, depending on the configuration) in the "installed" pool, then it is created and the quantity is set to the amount installed, for example, 10.
If installed inventory exists, it is updated and it's quantity is increased by the amount installed. For example, there is 20 feet (or 20 meter) of cable already installed, then the total amount is set to 30.
Supported Inventory Actions
Param Name | Mandatory | Type | Description |
---|---|---|---|
invid | Yes | String | ID of the existing inventory that is in the "provider" pool of the current resource or their teammates. |
inv_aid | Yes | String | ID of the started activity. Inventory will be installed to its "install" pool. Must contain the ID of the started segment for multi-day activities. |
quantity | Yes/No | Number |
|
properties | No | Object |
|
Param name | Mandatory | Type | Description |
---|---|---|---|
invid | Yes | String | ID of the existing inventory that is in the "customer" pool of the current resource or their teammates. |
inv_pid | Yes | String | ID of the current resource or their teammates. Inventory will be de-installed to its "deinstall" pool. |
quantity | Yes/No | Number |
|
properties | No | Object |
|
Param name | Mandatory | Type | Description |
---|---|---|---|
invid | Yes | String | ID of the existing inventory that is in the "install" pool of the started activity. |
quantity | Yes/No | Number |
|
properties | No | Object |
|
Param name | Mandatory | Type | Description |
---|---|---|---|
invid | Yes | String | ID of the existing inventory that is in the "de-install" pool of the current resource or their teammates. |
quantity | Yes/No | Number |
|
properties | No | Object |
|
Param name | Mandatory | Type | Description |
---|---|---|---|
invtype | Yes | String | Label of one of the configured Inventory Types, for example "NT". |
invpool | Yes | String | Inventory pool in which the inventory will be created. It is one of: "customer", "install", "deinstall", and "provider". |
inv_aid | Yes/No | String |
|
inv_pid | Yes/No | String |
|
quantity | Yes/No | Number |
Note: When quantity is not present in the screen configuration
or is present and set to Read only, then it is set to "1" for non-serialized inventory by
Oracle Field Service Mobility Cloud Service.
|
properties | No | Object |
|
Param name | Mandatory | Type | Description |
---|---|---|---|
invid | Yes | String | Id of the existing inventory that is in the "provider" pool of the current resource or their teammates or in "install", "deinstall" or "customer" pool of the started activity. The quantity parameter is not available for the Delete action. The entire record with any quantity will be deleted from the selected pool. |
Returning from the Plug-In
The "close" method allows a plug-in to define the screen to which the user is redirected to, after executing this method. The behavior is controlled by the "backScreen" parameter.
default - the user is redirected to the screen that was opened before the plug-in was opened. For example, if plug-in was open from the activity list then the user is redirected back to the activity list
activity_by_id - the user is redirected to the activity details of the activity that is identified by the id set in the parameter "backActivityId". If it is not possible to do then the user is redirected to the activity list
next_activity - the user is redirected to the activity details of the next pending activity in the list. If it is not possible to do then the user is redirected to the activity list
activity_list - the user is redirected to the activity list
start_activity - the user is redirected to the "start activity" screen of the activity that is identified by the id set in the parameter "backActivityId". If it is not possible to do then the user is redirected to the activity list
end_activity - the user is redirected to the "complete activity" screen of the activity that is identified by the id set in the parameter "backActivityId". If it is not possible to do then the user is redirected to the activity list
cancel_activity - the user is redirected to the "cancel activity" screen of the activity that is identified by the id set in the parameter "backActivityId". If it is not possible to do then the user is redirected to the activity list
notdone_activity - the user is redirected to the "not done" screen of the activity that is identified by the id set in the parameter "backActivityId". If it is not possible to do then the user is redirected to the activity list
suspend_activity - the user is redirected to the "suspend activity" screen of the activity that is identified by the id set in the parameter "backActivityId". If it is not possible to do then the user is redirected to the activity list
delay_activity - the user is redirected to the "suspend activity" screen of the activity that is identified by the id set in the parameter "backActivityId". If it is not possible to do then the user is redirected to the activity list
Example of "close" Method with Different "backScreen" Cases
// start_activity { "apiVersion": 1, "method": "close", "backScreen": "start_activity", "backActivityId": "4225473" } // end_activity { "apiVersion": 1, "method": "close", "backScreen": "end_activity", "backActivityId": "4225473" } // cancel_activity { "apiVersion": 1, "method": "close", "backScreen": "cancel_activity", "backActivityId": "4225473" } // notdone_cancel { "apiVersion": 1, "method": "close", "backScreen": "notdone_activity", "backActivityId": "4225473" } // suspend_activity { "apiVersion": 1, "method": "close", "backScreen": "suspend_activity", "backActivityId": "4225473" } // delay_activity { "apiVersion": 1, "method": "close", "backScreen": "delay_activity", "backActivityId": "4225473" }
Storing and Passing Sensitive Information
You can store and pass sensitive data such as login credentials in the plug-in.
You add this information in the Secure parameters section in the Add action link or Edit action link screens, when you configure a plug-in. The Secure parameters section includes the Parameter name and Parameter value fields. The data entered in these fields is encrypted and stored in the database. Changes to this data are sent to Oracle Field Service Mobility Cloud Service during the next synchronization. This data is sent to the plug-in when the next message is sent. The plug-in also receives the up-to-date data with every message.
When sensitive data is sent to the plug-in, the message contains the new field securedData. securedData is included only if at least one key-value pair is configured on the Add action link screen. The init, open, and wakeup methods support the securedData field.
Each key is a String, which equals to the contents of "key" text input on the Add action link or the Edit action link screen.
Each value is a String, which equals to the contents of "value" text input for the corresponding key on the Add action link or the Edit action link screen.
Order of entries may not be identical to the order of key-value pairs on the Add action link or the Edit action link screen.
The plug-in receives the following message upon opening:
{ "apiVersion": 1, "method": "open", "entity": "activityList", "resource": { "external_id": "33001", "manager": "admin" }, "activityList": { "4224031": { "aid": "4224031" } }, "inventoryList": { "21064417": { "invid": "21064417" } }, "securedData": { "ofscInstance": "company.test", "ofscRestEndpoint": "https://api.etadirect.com/rest/", "ofscRestClientId": "sample_app", "ofscRestClientSecret": "d1e0f03636747b968cd66ead50bd53984e1f1393a3e1503c4e4be9421be00aa5" } }
Error Handling
When Oracle Field Service Cloud receives a message from a plug-in with the close method, it validates all entity properties and their values. It applies the updates only if no rules are violated. After updating, the plug-in is closed. Otherwise, the application sends a message with the error method containing the list of found errors.
Error method
{ "apiVersion": 1, "method": "error", "entity": "activityList", "errors": [ { "type": "TYPE_ENTITY_PROPERTY", "code": "CODE_ACTIVITY_STATUS_INVALID", "entity": "activity", "entityId": "3956532", "propertyLabel": "astatus" }, { "type": "TYPE_ENTITY_PROPERTY", "code": "CODE_MANDATORY_PROPERTY_EMPTY", "entity": "inventory", "entityId": "20998086", "propertyLabel": "inv_aid" } ] }Each element of the error list is an object always containing the following fields:
type: Describes the type of error which occurred during the message processing, for example, invalid property value, internal error, and so on. Type determines the additional fields available in the error object, for example, property label.
code: Contains a more detailed description of the error, for example, validation rule violated by the data sent by the plug-in.
Type | Occurs When… | Available Message Fields |
---|---|---|
Error types related to entities | ||
TYPE_ENTITY_ACTION | The requested action is not applicable for the specified entity. |
|
TYPE_ENTITY_PROPERTY | The value of one of the properties submitted by the plug-in to be updated is invalid. or It violates some business rule for the given entity as well as the conditions. |
|
TYPE_INTERNAL | Oracle Field Service Cloud is unable to process the message due to: Invalid format or contents of the message, or Unexpected internal error | — |
Error types related to action | ||
TYPE_ACTION_ERROR | Action have an invalid format or in inapplicable |
|
TYPE_ACTION_PARAM | Action param has an invalid value or mandatory param is missing |
|
TYPE_ACTION_PROPERTY | Value of one of the properties, submitted by the plug-in to be updated in the "properties" param, has invalid value |
|
TYPE_ACTION_FAILED | Action is rejected due to incorrect value of action params, which can't be checked at the validation stage |
|
entity: The entity whose data is invalid (activity or inventory).
entityId: The ID of the entity whose data is invalid (equals aid for activity and invid for inventory, for example, 10028719).
actionId: The zero-based order number of erroneous action in the actions list, sent by plugin. E.g. 0, 1, 17 etc.
propertyLabel: The label of the property, the value of which is invalid, for example, customer_number, WO_TYPE.
Error Codes for Entities
The following table describes the error codes for entities:
Code | Occurs When... |
---|---|
TYPE_ENTITY_ACTION | |
CODE_ACTION_ON_PAST_DATE_NOT_ALLOWED | The requested action is forbidden for the entity if it is assigned for an archived (past) route:
|
TYPE_ENTITY_PROPERTY | |
CODE_PROPERTY_VALUE_TOO_LARGE | Any of the following:
|
CODE_MANDATORY_PROPERTY_EMPTY | Any of the following: For activity: astatus value is empty For inventory:
|
CODE_ACTIVITY_STATUS_INVALID | Any of the following:
|
CODE_INVENTORY_POOL_INVALID | Any of the following:
|
CODE_INVENTORY_AID_INVALID | Any of the following:
|
CODE_INVENTORY_PID_INVALID | Any of the following:
|
CODE_ACTIVITY_STATUS_INVALID_FOR_FUTURE | astatus is not pending or cancelled and activity is assigned for the day in future relative to the current date in the resources time zone |
CODE_ACTIVITY_STATUS_STARTED_ALREADY_IN_QUEUE | astatus is started and there is another started activity in the same route |
CODE_ACTIVITY_STATUS_INVALID_FOR_INACTIVE_QUEUE | astatus is not pending or cancelled and activity is assigned to a not activated or deactivated route |
TYPE_INTERNAL | |
CODE_UNKNOWN | Oracle Field Service Cloud is unable to process message due to: Invalid format or content of the message or Unexpected Oracle Field Service Cloud internal error occurred |
Error Codes for Actions
The following table describes the error codes for actions:
Code | Caused by Action | Cause |
---|---|---|
TYPE_ACTION_ERROR | ||
CODE_ACTION_ON_PAST_DATE_NOT_ALLOWED |
|
Any of these:
|
CODE_ENTITY_ID_INVALID |
|
"invid" param is not equal to id of any inventory in available pools |
CODE_ACTION_UNKNOWN | — | "action" param is not equal to one of the supported inventory actions (e.g. "install", "create") |
CODE_ACTION_ENTITY_UNKNOWN | — | "entity" param is not equal to "inventory" |
TYPE_ACTION_PARAM | ||
CODE_ACTION_INVENTORY_AID_INVALID | create | "inv_aid" param is sent for "create" action, and "invpool" is "provider" |
CODE_ACTION_INVENTORY_PID_INVALID |
|
Any of these:
|
CODE_ACTION_INVENTORY_POOL_INVALID | create | "invpool" param value is not equal to one of: "customer", "install", "deinstall", "provider" |
CODE_ACTION_INVENTORY_TYPE_INVALID | create | "invtype" param value is not equal to label of one of Inventory Types, configured for Oracle Field Service Cloud |
CODE_ACTION_MANDATORY_PARAM_EMPTY |
|
Any of these:
|
CODE_ACTION_PARAM_VALUE_INVALID |
|
Any of these:
|
TYPE_ACTION_PROPERTY | ||
CODE_ACTION_MANDATORY_PROPERTY_EMPTY |
|
[Reserved] |
CODE_ACTION_PROPERTY_VALUE_INVALID |
|
Any of these:
|
CODE_ACTION_PROPERTY_VALUE_TOO_LARGE |
|
Any of these:
|
TYPE_ACTION_FAILED | ||
CODE_ACTION_INVENTORY_ACTIVITY_STATUS_INVALID |
|
Any of these:
|
CODE_ACTION_INVENTORY_ACTIVITY_TYPE_INVALID |
|
"inv_aid" param equal to id of activity, whose type doesn't support inventories |
CODE_ACTION_INVENTORY_ACTIVITY_UNKNOWN |
|
"inv_aid" param isn't equal to:
|
CODE_ACTION_INVENTORY_POOL_TRANSITION_INVALID |
|
Any of these:
|
TYPE_INTERNAL | ||
CODE_UNKNOWN |
|
Oracle Field Service Cloud is unable to process the message due to unexpected change of the system's state |
Notifying When Offline or Online
The steps that are executed when the application switches online are:
Oracle Field Service Mobility Cloud Service switches to online mode.
The iframe is created, if required.
The Ready method is executed.
The Wakeup method is executed; here, the event is ‘online’.
The Sleep method is executed; here, the wakeupNeeded parameter is set to ‘true’.
The iframe is destroyed, if it is created.
Oracle Field Service Mobility Cloud Service waits for five minutes.
The iframe is created, if required.
The Ready method is executed.
The Wakeup method is executed; here, the event is ‘online’.
The Sleep method is executed; here, the wakeupNeeded parameter is set to ‘false’.
The iframe is destroyed, if it is created.
The flow of steps when the plug-in doesn’t respond to the application’s requests at the time of switching online is:
Oracle Field Service Mobility Cloud Service switches to online mode.
The iframe is created, if required.
The Ready method is executed.
The Wakeup method is executed; here, the event is ‘online’.
Oracle Field Service Mobility Cloud Service waits for two minutes.
The iframe is destroyed, if it is created.
The flow of steps when the plug-in is opened by a user after the application switches to online mode is:
Oracle Field Service Mobility Cloud Service detects the active connection to the Internet.
The iframe is created, if required.
The Ready method is executed.
The Wakeup method is executed.
The user clicks the action link.
The iframe is destroyed, if it is created.
The iframe is created, if required.
The Ready method is executed.
The Open method is executed.
The Close method is executed.
The iframe is destroyed, if it is created.
The plug-in's iframe window is killed after the Close message is processed, regardless of whether the device is online or offline. So, no code runs after the plug-in is closed. It may have data that must be synchronized with its server, OFSC REST API, or with third-party services. So, the plug-in sends a message to Mobility when the data is synchronized and Mobility sends a message to the plug-in when it is online. If the plug-in supports offline mode, request Mobility to invoke it when the network connectivity is established.
A new parameter "wakeupNeeded" is added to the "close" message to perform the synchronization. If it's set to true, the hidden plug-in's iframe is opened in the background, as soon as Mobility goes online, but not earlier than 5 minutes (300 s) after the plug-in is closed. See updated JSON Schema of the "close" message. After the plug-in's iframe is opened, Mobility sends the "wakeup" message to the plug-in in response to the "ready" message.
The plug-in must send the "sleep" message back to Mobility when it finishes synchronization, to allow the application to destroy the iframe. If the plug-in tried to synchronize, but couldn't sent all the data, it must send the "sleep" message with the "wakeupNeeded" param set to true. In this case, Mobility opens the plug-in's iframe in the background again, as soon as Mobility goes online, but no earlier than 5 minutes after the plug-in is closed. If the plug-in doesn't send the "sleep" message in two minutes (120 s) after the "wakeup" message is sent, Mobility destroys its iframe and reopens it, as if the plug-in sent the "sleep" message with the "wakeupNeeded" param set to true.
To allow the plug-in to synchronize even after refreshing Mobility's page or closing the browser, the new parameter "wakeupNeeded" is added to the "initEnd" message. If the plug-in didn't synchronize during the two minutes that is allowed for initialization, it must send the "initEnd" message with the "wakeupNeeded" param set to true. In this case, Mobility opens the plug-in's iframe in the background, as soon as Mobility goes online, but no earlier than 5 minutes after "initEnd" receiving the message.
The plug-in is opened in 5 minutes after it's closed, if the "wakeupNeeded" param of "close", "sleep" or "initEnd" messages are set to true, even if Mobility didn't detect offline when the plug-in was opened or closed. If the user opens the plug-in by clicking its action link in Mobility, the background iframe is destroyed without sending any messages to it. If plug-in still has data to be synchronized, it must send the "close" message with the "wakeupNeeded" param set to true.
Property Value Length Limits
Limits are applied to the property values that are submitted by the plug-in through the plug-in API. If the length of a value exceeds the limit, an error is returned as part of the message, using the error method.
Fields (property type is field): Fields are encrypted by AES and stored in the TINYBLOB columns. AES output block is 16 bytes, so the ciphertext length is always divisible by 16 (L % 16 = 0). Additionally, AES requires one extra block for the data whose length is divisible by 16. So, the maximum plain data to store is ceil(255/16)*16 - 1 = 239. JavaScript uses UTF-16 for strings, so one Unicode character may take up 2 to 4 bytes. But the String.length property uses UTF-16 code points for counting, which is 2 bytes. Therefore, the length of the string containing one 4-byte UTF-16 char will be 2. So, only ceil(239/2) = 119 code points can be stored without truncating.
Signatures (property type is file and GUI element is Signature): We assume that the value contains only MIME-type and correct base64 string, so each character takes up 2 bytes as JavaScript uses UTF-16. To avoid overflow of the LocalStorage we limit each signature to 200 KB (1024*200/2 = 102400 characters).
Properties (any other property type): Properties are stored in TEXT columns. The maximum amount of data to store is 65 535 bytes (2^16 - 1). JavaScript uses UTF-16 for strings, so one character may take up 2 to 4 bytes. But the String.length property uses UTF-16 code points for counting, which is 2 bytes. Therefore, length of the string containing one 4-byte UTF-16 char will be 2. So, in the worst case scenario, only ceil(65535/2) = 32767 code points can be stored without truncating.
JSON Schema for Message Data
ready Method
{ "type": "object", "properties": { "apiVersion": { "type": "number", "enum": [1] }, "method": { "type": "string", "enum": ["ready"] }, "sendInitData": { "type": "boolean" }, "showHeader": { "type": "boolean" }, "enableBackButton": { "type": "boolean" }, "dataItems": { "type": "array", "items": { "type": "string", "enum": [ "resource", "scheduledActivites", "nonScheduledActivites", "resourceInventories", "installedInventories", "deinstalledInventories", "customerInventories" ] } } }, "required": ["apiVersion", "method"] }
init Method
{ "type": "object", "properties": { "apiVersion": { "type": "number", "enum": [1] }, "method": { "type": "string", "enum": ["init"] } }, "required": ["apiVersion", "method"] }
initEnd Method
{ "type": "object", "properties": { "apiVersion": { "type": "number", "enum": [1] }, "method": { "type": "string", "enum": ["initEnd"] }, "wakeupNeeded": { "type": "boolean" } }, "required": ["apiVersion", "method"] }
open Method
{ "apiVersion": {"type": "number", "enum": ["1"]}, "method": {"type": "string", "enum": ["open"]}, "entity": { "type": "string", "enum": ["activity", "activitiyList", "activityInventory", "activityInventoryList", "inventory", "inventoryList"] }, "resource": { "type": "object", "properties": { "pid": {"type": "number"}, "pname": {"type": "string"}, "external_id": {"type": "string"}, "ptype": {"type": "string"}, "email": {"type": "string"}, "pphone": {"type": "string"}, "pcapacity_bucket": {"type": "number"} }, "patternProperties": { "^.+$": { "anyOf": [ {"type": "string"}, {"type": "number"} ], "description": "Key: Any property of an OFSC Activity; Value: value of this property" } } }, "activity": { "type": "object", "properties": { "cname": {"type": "string"}, "caddress": {"type": "string"}, "ccity": {"type": "string"}, "czip": {"type": "number"}, "cstate": {"type": "string"}, "customer_number": {"type": "string"}, "c_zid": {"type": "number"}, "cphone": {"type": "string"}, "cemail": {"type": "string"}, "ccell": {"type": "string"}, "atype": {"type": "string"}, "aworktype": {"type": "string"}, "time_slot": {"type": "number"}, "service_window": {"type": "string"}, "appt_number": {"type": "string"}, "clanguage": {"type": "number"}, "cmessagetime": {"type": "number"}, "activity_workskills": {"type": "string"}, "length": {"type": "number"}, "ETA": {"type": "string"}, "astatus": {"type": "string"}, "aid": {"type": "number"}, "aworkzone": {"type": "number"}, "end_time": {"type": "string"}, "delivery_window": {"type": "string"}, "acoord_status": {"type": "string"}, "acoord_x": {"type": "number"}, "acoord_y": {"type": "number"}, "travel": {"type": "number"}, "sla_window_start": {"type": "string"}, "sla_window_end": {"type": "string"}, "apoints": {"type": "number"}, "activity_capacity_categories": {"type": "string"}, "atime_of_booking": {"type": "string"}, "atime_of_assignment": {"type": "string"}, "auto_routed_to_provider_id": {"type": "number"}, "auto_routed_to_date": {"type": "string"}, "auto_routed_to_provider_name": {"type": "string"}, "first_manual_operation": {"type": "number"}, "first_manual_operation_interface": {"type": "number"}, "first_manual_operation_user_id": {"type": "number"}, "first_manual_operation_user_login": {"type": "string"}, "first_manual_operation_user_name": {"type": "string"} }, "patternProperties": { "^.+$": { "anyOf": [ {"type": "string"}, {"type": "number"} ], "description": "Key: Any property of an OFSC Activity; Value: value of this property" } } }, "inventory": { "type": "object", "properties": { "invid": {"type": "number"}, "inv_aid": {"type": "number"}, "inv_pid": {"type": "number"}, "invpool": {"type": "string"}, "invsn": {"type": "string"}, "invtype": {"type": "string"}, "quantity": {"type": "number"}, "inv_change_invid": {"type": "number"} }, "patternProperties": { "^.+$": { "anyOf": [ {"type": "string"}, {"type": "number"} ], "description": "Key: Any property of an OFSC Activity; Value: value of this property" } } }, "activityList": { "type": "object", "patternProperties": { "^//d+$": { "type": "object", "properties": { "cname": {"type": "string"}, "caddress": {"type": "string"}, "ccity": {"type": "string"}, "czip": {"type": "number"}, "cstate": {"type": "string"}, "customer_number": {"type": "string"}, "c_zid": {"type": "number"}, "cphone": {"type": "string"}, "cemail": {"type": "string"}, "ccell": {"type": "string"}, "atype": {"type": "string"}, "aworktype": {"type": "string"}, "time_slot": {"type": "number"}, "service_window": {"type": "string"}, "appt_number": {"type": "string"}, "clanguage": {"type": "number"}, "cmessagetime": {"type": "number"}, "activity_workskills": {"type": "string"}, "length": {"type": "number"}, "ETA": {"type": "string"}, "astatus": {"type": "string"}, "aid": {"type": "number"}, "aworkzone": {"type": "number"}, "end_time": {"type": "string"}, "delivery_window": {"type": "string"}, "acoord_status": {"type": "string"}, "acoord_x": {"type": "number"}, "acoord_y": {"type": "number"}, "travel": {"type": "number"}, "sla_window_start": {"type": "string"}, "sla_window_end": {"type": "string"}, "apoints": {"type": "number"}, "activity_capacity_categories": {"type": "string"}, "atime_of_booking": {"type": "string"}, "atime_of_assignment": {"type": "string"}, "auto_routed_to_provider_id": {"type": "number"}, "auto_routed_to_date": {"type": "string"}, "auto_routed_to_provider_name": {"type": "string"}, "first_manual_operation": {"type": "number"}, "first_manual_operation_interface": {"type": "number"}, "first_manual_operation_user_id": {"type": "number"}, "first_manual_operation_user_login": {"type": "string"}, "first_manual_operation_user_name": {"type": "string"} }, "patternProperties": { "^.+$": { "anyOf": [ {"type": "string"}, {"type": "number"} ], "description": "Key: Any property of an OFSC Activity; Value: value of this property" } } } } }, "inventoryList": { "type": "object", "patternProperties": { "^//d+$": { "type": "object", "properties": { "invid": {"type": "number"}, "inv_aid": {"type": "number"}, "inv_pid": {"type": "number"}, "invpool": {"type": "string"}, "invsn": {"type": "string"}, "invtype": {"type": "string"}, "quantity": {"type": "number"}, "inv_change_invid": {"type": "number"} }, "patternProperties": { "^.+$": { "anyOf": [ {"type": "string"}, {"type": "number"} ], "description": "Key: Any property of an OFSC Activity; Value: value of this property" } } } } }, "required": ["apiVersion", "method", "entity", "resource"] }
close Method
{ "type": "object", "properties": { "apiVersion": { "type": "number", "enum": [1] }, "method": { "type": "string", "enum": ["close"] }, "backScreen": { "type": "string", "enum": ["default", "activity_by_id", "next_activity", "activity_list", "start_activity", "end_activity", "cancel_activity", "notdone_activity", "suspend_activity", "delay_activity"] }, "backActivityId": { "type": "string" }, "wakupNeeded": { "type": "boolean" }, "activity": { "type": "object", "required": ["aid"], "properties": { "cname": { "type": "string" }, "caddress": { "type": "string" }, "ccity": { "type": "string" }, "czip": { "type": "number" }, "cstate": { "type": "string" }, "customer_number": { "type": "string" }, "c_zid": { "type": "number" }, "cphone": { "type": "string" }, "cemail": { "type": "string" }, "ccell": { "type": "string" }, "time_slot": { "type": "number" }, "service_window": { "type": "string" }, "appt_number": { "type": "string" }, "clanguage": { "type": "number" }, "cmessagetime": { "type": "number" }, "length": { "type": "number" }, "astatus": { "type": "string" }, "aid": { "type": "string" }, "sla_window_start": { "type": "string" }, "sla_window_end": { "type": "string" }, "apoints": { "type": "number" } }, "patternProperties": { "^.+$": { "anyOf": [ { "type": "string" }, { "type": "number" }, { "type": "object", "properties": { "fileName" : { "type": "string" }, "fileContents" : { "type": "object" } }, "required": ["fileName", "fileContents"] } ], "description": "Key: Any property of an OFSC Activity; Value: value of this property" } } }, "inventory": { "type": "object", "required": ["invid"], "properties": { "invid": { "type": "string" }, "inv_aid": { "type": "string" }, "inv_pid": { "type": "string" }, "invpool": { "type": "string" }, "invsn": { "type": "string" }, "invtype": { "type": "string" }, "quantity": { "type": "number" } }, "patternProperties": { "^.+$": { "anyOf": [ { "type": "string" }, { "type": "number" }, { "type": "object", "properties": { "fileName" : { "type": "string" }, "fileContents" : { "type": "object" } }, "required": ["fileName", "fileContents"] } ], "description": "Key: Any property of an OFSC Activity; Value: value of this property" } } }, "activityList": { "type": "object", "patternProperties": { "^\\d+$": { "type": "object", "properties": { "cname": { "type": "string" }, "caddress": { "type": "string" }, "ccity": { "type": "string" }, "czip": { "type": "number" }, "cstate": { "type": "string" }, "customer_number": { "type": "string" }, "c_zid": { "type": "number" }, "cphone": { "type": "string" }, "cemail": { "type": "string" }, "ccell": { "type": "string" }, "time_slot": { "type": "number" }, "service_window": { "type": "string" }, "appt_number": { "type": "string" }, "clanguage": { "type": "number" }, "cmessagetime": { "type": "number" }, "length": { "type": "number" }, "astatus": { "type": "string" }, "aid": { "type": "string" }, "sla_window_start": { "type": "string" }, "sla_window_end": { "type": "string" }, "apoints": { "type": "number" } }, "patternProperties": { "^.+$": { "anyOf": [ { "type": "string" }, { "type": "number" }, { "type": "object", "properties": { "fileName" : { "type": "string" }, "fileContents" : { "type": "object" } }, "required": ["fileName", "fileContents"] } ], "description": "Key: Any property of an OFSC Activity; Value: value of this property" } }, "required": ["aid"] } }, "inventoryList": { "type": "object", "patternProperties": { "^\\d+$": { "type": "object", "required": false, "properties": { "invid": { "type": "string" }, "inv_aid": { "type": "string" }, "inv_pid": { "type": "string" }, "invpool": { "type": "string" }, "invsn": { "type": "string" }, "invtype": { "type": "string" }, "quantity": { "type": "number" } }, "patternProperties": { "^.+$": { "anyOf": [ { "type": "string" }, { "type": "number" }, { "type": "object", "properties": { "fileName" : { "type": "string" }, "fileContents" : { "type": "object" } }, "required": ["fileName", "fileContents"] } ], "description": "Key: Any property of an OFSC Activity; Value: value of this property" } } } } }, "actions": { "type": "array", "items": { "type": "object", "properties": { "entity": { "type": "string" }, "action": { "type": "string" }, "invtype": { "type": "string" }, "invpool": { "type": "string" }, "invid": { "type": "string" }, "inv_aid": { "type": "string" }, "inv_pid": { "type": "string" }, "quantity": { "type": "number" }, "properties": { "type": "object", "patternProperties": { "^.+$": { "anyOf": [ { "type": "string" }, { "type": "number" }, { "type": "object", "properties": { "fileName" : { "type": "string" }, "fileContents" : { "type": "object" } }, "required": ["fileName", "fileContents"] } ] } } } }, "required": [ "entity", "action" ] } } } }, "required": ["apiVersion", "method"] }
wakeup Method
{ "type": "object", "properties": { "apiVersion": { "type": "number", "enum": [1] }, "method": { "type": "string", "enum": ["wakup"] }, "event": { "type": "string", "enum": ["online"] } }, "required": ["apiVersion", "method"] }
sleep Method
{ "type": "object", "properties": { "apiVersion": { "type": "number", "enum": [1] }, "method": { "type": "string", "enum": ["sleep"] }, "wakeupNeeded": { "type": "boolean" } }, "required": ["apiVersion", "method"] }
error Method
{ "apiVersion": {"type": "number", "enum": ["1"]}, "method": {"type": "string", "enum": ["error"]}, "entity": { "type": "string", "enum": ["activity", "activitiyList", "activityInventory", "activityInventoryList", "inventory", "inventoryList"] }, "errors": { "type": "object", "fields": { "type": {"type": "string", "enum": ["TYPE_ENTITY_ACTION", "TYPE_ENTITY_PROPERTY", "TYPE_INTERNAL"]}, "code": { "type": "string", "enum": [ "CODE_ACTION_ON_PAST_DATE_NOT_ALLOWED", "CODE_PROPERTY_VALUE_TOO_LARGE", "CODE_MANDATORY_PROPERTY_EMPTY", "CODE_ACTIVITY_STATUS_INVALID", "CODE_INVENTORY_POOL_INVALID", "CODE_INVENTORY_AID_INVALID", "CODE_INVENTORY_PID_INVALID", "CODE_ACTIVITY_STATUS_INVALID_FOR_FUTURE", "CODE_ACTIVITY_STATUS_STARTED_ALREADY_IN_QUEUE", "CODE_ACTIVITY_STATUS_INVALID_FOR_INACTIVE_QUEUE", "CODE_UNKNOWN" ] }, "entity": {"type": "string", "enum": ["activity", "inventory"]}, "entityId": {"type": "string"}, "propertyLabel": {"type": "string"} }, "required": ["type", "code"] }, "required": ["apiVersion", "method"] }
JSON Example
open Method
{ "apiVersion": 1, "method": "open", "entity": "activity", "resource": { "pid": 5000038, "pname": "RAYNER, Faye", "external_id": "55038", "gender": "1" }, "activity": { "WO_COMMENTS": "AUTOMATIC TRANSFER WORK ORDER/n/n", "astatus": "started", "aid": 3956534 }, "inventories": { "20997919": { "invid": 20997919, "inv_aid": 3956534, "inv_pid": 5000038, "invpool": "install", "invsn": "SABDFWKNZ" }, "20998078": { "invid": 20998078, "inv_aid": 3956532, "invpool": "customer", "invsn": "5CTBME4AW090379" }, "20998080": { "invid": 20998080, "inv_aid": 3956533, "invpool": "customer", "invsn": "SABGZTWGM" } } }
close Method
{ "apiVersion": 1, "method": "close", "backScreen": "default", "actions": [ // INSTALL { "entity": "inventory", "action": "install", "invid": 21258426, "inv_aid": 4224031, "properties": // Properties can be updated too { "PORT_INFO": "A0|1|1|0|7.9|QF9-0719537", "EQUIPMENT_ETHERNET": "08:00:27:ea:d1:bd", } }, { "entity": "inventory", "action": "install", "invid": 21229417, "inv_aid": 4224031, "quantity": 12, // Install only 12 pieces of NSI "properties": // Model should be set if needed { "inventory_model": "RG6 - BLK", } }, // DEINSTALL { "entity": "inventory", "action": "deinstall", "invid": 21064418, "inv_pid": 3000001 }, // CREATE { "entity": "inventory", "action": "create", "invtype": "NT", "invpool": "installed", "inv_aid": 4224031, "inv_pid": 3000001, "quantity": 100, "properties": { "inventory_model": "RG6 - BLK", } }, // DELETE { "entity": "inventory", "action": "delete", "invid": "1484311067004-9891" } ] }
Barcode Scanner Method
The plug-in framework supports remote procedure calls from Oracle Field Service Mobility Cloud Service Barcode Scanner Service. You can use this method and create a plug-in to scan barcodes. The Barcode Scanner button is available in the Inventory search screen, if you open Mobility through the native application. If you open the inventory list through a browser, the button is not available. The plug-in framework in release 18A is backward compatible with the version introduced in release 17.8.
If you use this method to create a relevant plug-in, your Android device turns into a scanner, so you need not use external tools such as custom Android keyboards. You can use the barcode Plug-in API to:
Find inventory in the pools and show where the inventory is currently.
Search an activity related to a specific inventory, in other words, scan inventory to search activities related to this inventory.
Show details of the inventory searched (Name, Label, etc.).
Depending on the pool where the inventory is available, suggest actions such as install, undo deinstall, undo exchange to the technician.
Allow entering and updating a barcode number through a keyboard.
Syntax
scanBarcode
Parts and equipment usually have barcodes printed on their package or on the device. This procedure provides barcode and 2D (for example, QR, DATAMATRIX) code scanner functionality to make the search easier for the required items in the inventory pools. When this procedure is called, the scanner window is opened, which shows the live camera picture. When the barcode is recognized, the scanner windows are closed, and the resulting values are sent to the plug-in through the callProcedureResult method. If the barcode scanner is unavailable or Oracle Field Service Mobility Cloud Service is not run inside the Mobile app, the corresponding error code is returned to the plug-in through an error message. Having Oracle Field Service Cloud Mobile (for Android or iOS) is a prerequisite to use the Barcode Scanner Mobile Plug-in Framework to search by barcode.
Barcode Type | Andriod | iOS |
---|---|---|
QR_CODE | Yes | Yes |
DATA_MATRIX | Yes | Yes |
UPC_A | Yes | Yes |
UPC_E | Yes | Yes |
EAN_8 | Yes | Yes |
EAN_13 | Yes | Yes |
CODE_39 | Yes | Yes |
CODE_93 | Yes | No |
CODE_128 | Yes | Yes |
CODABAR | Yes | No |
ITF | Yes | Yes |
RSS14 | Yes | No |
PDF417 | Yes | No |
RSS_EXPANDED | Yes | No |
openLink
This procedure provides a common way to open external URLs either with Oracle Field Service Mobility Cloud Service run in the web browser or in Mobile app. When Oracle Field Service Cloud Mobile is used, the URL opens externally in a web browser. If not, it opens as a new browser tab.
{ "apiVersion":1, "method":"callProcedure", "procedure":"scanBarcode", "callid":"123abc" } { "apiVersion": 1, "method": "callProcedure", "procedure": "openLink", "callId": "123abc", "params": { "url": "https://play.google.com/store/apps/details?id=com.oracle.ofsc" } }callProcedure Method Parameters:
Param Name | Mandatory | Type | Description |
---|---|---|---|
apiVersion | Yes | Integer | API version |
method | Yes | String | Must equal "callProcedure" |
procedure | Yes | String | Procedure name |
callId | Yes | String | Unique string identifier which is used to apply procedure response within plug-in |
function generateCallId() { return btoa(String.fromCharCode.apply(null, window.crypto.getRandomValues(new Uint8Array(16)))); }callProcedureResult method
Param Name | Mandatory | Type | Description |
---|---|---|---|
apiVersion | Yes | Integer | API version |
method | Yes | String | Must equal "callProcedureResult" |
procedure | Yes | String | Procedure name |
resultData | No | String | Result of procedure execution |
Key | Type | Description |
---|---|---|
apiVersion | String | API version |
Format | String | Type of recognized barcode. See Supported barcode and 2D code types. |
Cancelled | String | Equals true, if user closes the scanner window before the code is recognized. |
{ "apiVersion": 1, "method": "callProcedureResult", "callid": "123abc" "resultData": { "text": "PT9012308", "format": "QR_CODE", "cancelled": false } }When the method is canceled:
{ "apiVersion": 1, "method": "callProcedureResult", "callid": "123abc" "resultData": { "text": "PT9012308", "format": "QR_CODE", "cancelled": true } }
Errors
{ code: "CODE_PROCEDURE_UNKNOWN", procedure: "scanBarcode" }Type of errors
Type | Occurs When... | Available Message Fields |
---|---|---|
TYPE_PROCEDURE_ERROR | Procedure call not valid due to missed params, and procedure executed with error. | Procedure [Name of the procedure where the error occurred.] |
TYPE_PROCEDURE_PARAM | Procedure params not valid or missed. | Procedure [Name of the procedure where the error occurred.] |
Code | Caused by Procedure | Error Type | Cause |
---|---|---|---|
TYPE_PROCEDURE_ERROR | |||
CODE_CALL_ID_EMPTY | scanBarcode | Validation error | Empty callId param. |
CODE_CALL_ID_INVALID | scanBarcode | Validation error | Invalid callId param. |
CODE_CALL_ID_DUPLICATE | scanBarcode | Validation error | Duplicate callId param. |
CODE_PROCEDURE_FAILED - | scanBarcode | Execution error | Procedure execution failed to various reasons. |
CODE_PROCEDURE_UNKNOWN | scanBarcode | Execution error | Procedure was called with unknown procedure name. |
CODE_PROCEDURE_UNAVAILABLE | scanBarcode | Internal error | Oracle Field Service Mobility Cloud Serviceservice related to procedure is not available. |
CODE_PROCEDURE_ACCEPTS_NO_PARAMS | scanBarcode | Validation error | Procedure was called with params. |
TYPE_PROCEDURE_PARAM | |||
CODE_PROCEDURE_MANDATORY_PARAM_EMPTY | openLink | Validation error | Mandatory param is missed. |
CODE_PROCEDURE_PARAM_VALUE_INVALID | openLink | Validation error | Param value is not valid. |
A Sample Plug-In
index.html
styles.css
plugin.js
signature.js
cache.manifest
logo.svg
index.html File
<!DOCTYPE html> <html manifest="cache.manifest"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Sample Plugin</title> <link rel="stylesheet" href="./style.css"> <script src="//code.jquery.com/jquery-3.1.1.min.js"></script> <script src="signature.js"></script> <script src="plugin.js"></script> <script> (function($){ $(document).ready(function(){ var plugin = new Plugin(true); plugin.init(); }); })(jQuery); </script> </head> <body> <div class="header"> <img class="header__logo" src="./logo.svg" alt="Oracle logo"> </div> <h1>OFSC Sample Plugin</h1> <div class="content"> <div class="section section__local-storage"> <h2>Local Storage</h2> <button class="button json_local_storage_toggle" type="button">View Local Storage</button> <div class="json json__local-storage"></div> </div> <div class="section section__ofsc-data"> <h2>OFSC Data</h2> <div class="row"> <button class="button submit" type="button">Submit</button> <div class="back_method">and go to <select class="back_method_select" title="Redirect to screen after submit"> <option value="default">Previous screen</option> <option value="activity_by_id">Activity #</option> <option value="next_activity">Next pending Activity</option> <option value="activity_list">Activity List</option> </select> <input class="back_activity_id" type="text" title="Activity ID" placeholder="Activity ID"> </div> </div> <div class="row"> <button class="button json_request_toggle" type="button">View received JSON</button> <button class="button json_response_toggle" type="button">Edit JSON to send</button> </div> <div class="column-holder"> <div class="column-item column-item--request"> <div class="json json__request"></div> </div> <div class="column-item"> <div class="section section--visible"> <h2>Inventory actions (will be applied after entity)</h2> <!-- this part will be inserted on adding of new property --> <div class="example-property item"> <div class="item-expander"></div> <div class="key writable" contenteditable></div><span class="delimiter">: </span> <div class="value value__item writable" contenteditable></div> <button class="button button--remove-item">Remove</button> </div> <!-- this part will be inserted on adding of new action --> <div class="example-action item"> <div class="item-expander"></div> <div class="key action-key clickable">0</div> <button class="button button--remove-item">Remove</button> <br> <div class="value value__collection"> <div class="items"> <div class="item"> <div class="item-expander"></div> <div class="key">action</div><span class="delimiter">: </span> <select class="value value__item writable select-inventory-action" title=""> <option value="">Select action</option> <option value="create">Create</option> <option value="delete">Delete</option> <option value="install">Install</option> <option value="deinstall">Deinstall</option> <option value="undo_install">Undo install</option> <option value="undo_deinstall">Undo deinstall</option> </select> </div> <div class="item"> <div class="item-expander"></div> <div class="key">entity</div><span class="delimiter">: </span> <div class="value value__item">inventory</div> </div> <!-- new params will be inserted here --> <div class="item item--excluded"> <div class="item-expander"></div> <button class="button button--item-value button--add-property" type="button">Add parameter</button> </div> <div class="item"> <div class="item-expander"></div> <div class="key">properties</div> <br> <div class="value value__collection"> <div class="items"> <!-- new properties will be inserted here --> <div class="item item--excluded"> <div class="item-expander"></div> <button class="button button--item-value button--add-property" type="button">Add property</button> </div> </div> </div> </div> </div> </div> </div> <div class="actions-form"> <div class="item"> <div class="key">actions</div> <br> <div class="value value__collection"> <div class="items items--without-key"> <!-- new actions will be inserted here --> <div class="item item--excluded"> <div class="item-expander"></div> <button class="button button--item-value button--add-action" type="button">Add action</button> </div> </div> </div> </div> </div> </div> <div class="section section--visible"> <h2>Entity</h2> <div class="form"></div> </div> </div> <div class="column-item column-item--response"> <div class="json json__response" contenteditable></div> </div> </div> <div class="section section--visible"> <h2>Entity data items</h2> <input class="data-items" type="checkbox" id="dataItems"> <label for="dataItems">Ask OFSC to send only selected items on next open</label> <br> <div class="value value__collection data-items-holder" style="display: none;"> <div class="items"> <div class="item"> <div class="item-expander"></div> <div class="value value__item"> <input type="checkbox" value="resource" id="resource"> <label for="resource">Resource information</label> </div> </div> <div class="item"> <div class="item-expander"></div> <div class="value value__item"> <input type="checkbox" value="scheduledActivities" id="scheduledActivities"> <label for="scheduledActivities">Scheduled activity list</label> </div> </div> <div class="item"> <div class="item-expander"></div> <div class="value value__item"> <input type="checkbox" value="nonScheduledActivities" id="nonScheduledActivities"> <label for="nonScheduledActivities">Non-scheduled activity list</label> </div> </div> <div class="item"> <div class="item-expander"></div> <div class="value value__item"> <input type="checkbox" value="resourceInventories" id="resourceInventories"> <label for="resourceInventories">Inventories of resource</label> </div> </div> <div class="item"> <div class="item-expander"></div> <div class="value value__item"> <input type="checkbox" value="installedInventories" id="installedInventories"> <label for="installedInventories">Installed inventories</label> </div> </div> <div class="item"> <div class="item-expander"></div> <div class="value value__item"> <input type="checkbox" value="customerInventories" id="customerInventories"> <label for="customerInventories">Inventories of customer</label> </div> </div> <div class="item"> <div class="item-expander"></div> <div class="value value__item"> <input type="checkbox" value="deinstalledInventories" id="deinstalledInventories"> <label for="deinstalledInventories">Deinstalled inventories</label> </div> </div> </div> </div> </div> <div class="section section--visible"> <h2>Background synchronization</h2> <div class="wakeup-form"> <div class="wakeup-form-row" id="wakeup_row"> <label><input type="checkbox" id="wakeup"><span class="checkbox-label">Ask OFSC to wake up the Plugin when Mobility goes online</span></label><input id="repeat_count" class="repeat" type="number" min="0" step="1" size="1" value="1">times </div> <div class="wakeup-form-row" id="dont_respond_row"> <label><input type="checkbox" id="dont_respond"><span class="checkbox-label">Don't respond on the</span></label><input id="dont_respond_on" class="repeat" type="number" min="1" step="1" size="1" value="1">wakeup </div> </div> </div> </div> </div> </body> </html>
style.css
/* Copyright (c) 2013, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://yuilibrary.com/license/ version: 3.18.1 */ html{ color:#000; background:#FFF; } body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td { margin:0; padding:0; } table { border-collapse:collapse; border-spacing:0; } fieldset, img { border:0; } address, caption, cite, code, dfn, em, strong, th, var { font-style:normal; font-weight:normal; } ol, ul { list-style:none; } caption, th { text-align:left; } h1, h2, h3, h4, h5, h6 { font-size:100%; font-weight:normal; } q:before, q:after { content:''; } abbr, acronym { border:0; font-variant:normal; } /* to preserve line-height and selector appearance */ sup { vertical-align:text-top; } sub { vertical-align:text-bottom; } input, textarea, select { font-family:inherit; font-size:inherit; font-weight:inherit; *font-size:100%; /*to enable resizing for IE*/ } /*because legend doesn't inherit in IE */ legend { color:#000; } /*-------------------------------------------------------------------------*/ html { height: 100%; } body { font-family: Arial, serif; color: #444; background: #F0F0F0; min-height: 100%; } h1 { padding: 15px; font-size: 20px; color: #F00; text-align: center; } h2 { color: #F00; padding: 0 0 10px; } select { border-radius: 5px; padding: 5px 7px; font-size: 16px; border: 1px solid #B9B9B9; margin-bottom: 10px; vertical-align: baseline; background: #F1F1F1; } input { border-radius: 5px; padding: 6px 7px; font-size: 16px; border: 1px solid #B9B9B9; margin-bottom: 10px; vertical-align: baseline; background: #F1F1F1; } .header { width: 100%; background: #ea1b22; text-align: center; padding: 10px 15px; box-sizing: border-box; } .header__logo { width: 300px; } @media (max-width: 300px) { .header__logo { width: 100%; } } .content { padding: 0 15px; } .section { border: 1px solid #DEDEDE; background: white; border-radius: 5px; padding: 10px; margin: 0 0 10px; overflow: hidden; display: none; } .form { padding: 10px 0; white-space: nowrap; overflow: auto; } .items { background: url(items.png) 0 0 repeat-y; } .form > .item { margin-left: 0; } .item { padding: 0 0 0 25px; background: url(item.png) 0 0 no-repeat; } .item:last-child { background: url(item.png) 0 0 no-repeat #FFF; } .key { display: inline-block; font-weight: 700; margin: 4px 0; } .clickable { cursor: pointer; } .collapsed:after { content: " ..."; } .delimiter { font-weight: 700; } .value { display: inline-block; background: none; border: none; font-size: 100%; white-space: pre; vertical-align: top; margin: 4px 0; } select.value { font-size: 16px; height: 19px; -webkit-appearance: none; -moz-appearance: none; appearance: none; color: #444; padding: 0; border-radius: 0; vertical-align: baseline; } .writable { border-bottom: 1px dashed black; min-width: 60px; } .button { display: inline-block; padding: 7px 11px; border: 1px solid #B9B9B9; border-radius: 5px; cursor: pointer; outline: none; font-weight: bold; font-size: 16px; background: #F0F0F0; margin-bottom: 10px; margin-right: 7px; } .button__generate_sign { margin: 0; font-weight: 400; padding: 2px 7px; } .submit { background: #ee3526; color: #fff; border-color: #B50000; } .json { padding: 10px; white-space: pre; font-family: monospace; border-radius: 5px; margin: 0 0 10px; background: #353535; color: white; } .json__request { display: none; } .json__response { display: none; background: #F0F0F0; color: #444; border: 1px solid #DEDEDE; } .back_method { display: inline-block; } .back_activity_id { display: none; }
plugin.js
"use strict"; (function($){ window.Plugin = function(debugMode) { this.debugMode = debugMode || false; this._messageListener = null; }; $.extend(window.Plugin.prototype, { /** * Dictionary of enums */ dictionary: { astatus: { pending: { label: 'pending', translation: 'Pending', outs: ['started', 'cancelled'], color: '#FFDE00' }, started: { label: 'started', translation: 'Started', outs: ['complete', 'suspended', 'notdone', 'cancelled'], color: '#A2DE61' }, complete: { label: 'complete', translation: 'Completed', outs: [], color: '#79B6EB' }, suspended: { label: 'suspended', translation: 'Suspended', outs: [], color: '#9FF' }, notdone: { label: 'notdone', translation: 'Not done', outs: [], color: '#60CECE' }, cancelled: { label: 'cancelled', translation: 'Cancelled', outs: [], color: '#80FF80' } }, invpool: { customer: { label: 'customer', translation: 'Customer', outs: ['deinstall'], color: '#04D330' }, install: { label: 'install', translation: 'Installed', outs: ['provider'], color: '#00A6F0' }, deinstall: { label: 'deinstall', translation: 'Deinstalled', outs: ['customer'], color: '#00F8E8' }, provider: { label: 'provider', translation: 'Resource', outs: ['install'], color: '#FFE43B' } } }, /** * Which field shouldn't be editable * * format: * * parent: { * key: true|false * } * */ renderReadOnlyFieldsByParent: { data: { apiVersion: true, method: true, entity: true }, resource: { pid: true, pname: true, gender: true } }, /** * Check for string is valid JSON * * @param {*} str - String that should be validated * * @returns {boolean} * * @private */ _isJson: function(str) { try { JSON.parse(str); } catch (e) { return false; } return true; }, /** * Return origin of URL (protocol + domain) * * @param {String} url * * @returns {String} * * @private */ _getOrigin: function(url) { if (url != '') { if (url.indexOf("://") > -1) { return 'https://' + url.split('/')[2]; } else { return 'https://' + url.split('/')[0]; } } return ''; }, /** * Return domain of URL * * @param {String} url * * @returns {String} * * @private */ _getDomain: function(url) { if (url != '') { if (url.indexOf("://") > -1) { return url.split('/')[2]; } else { return url.split('/')[0]; } } return ''; }, /** * Sends postMessage to document.referrer * * @param {Object} data - Data that will be sent * * @private */ _sendPostMessageData: function(data) { if (document.referrer !== '') { this._log(window.location.host + ' -> ' + data.method + ' ' + this._getDomain(document.referrer), JSON.stringify(data, null, 4)); parent.postMessage(JSON.stringify(data), this._getOrigin(document.referrer)); } }, /** * Handles during receiving postMessage * * @param {MessageEvent} event - Javascript event * * @private */ _getPostMessageData: function(event) { if (typeof event.data !== 'undefined') { if (this._isJson(event.data)) { var data = JSON.parse(event.data); if (data.method) { this._log(window.location.host + ' <- ' + data.method + ' ' + this._getDomain(event.origin), JSON.stringify(data, null, 4)); switch (data.method) { case 'open': this.pluginOpen(data); break; case 'error': data.errors = data.errors || {error: 'Unknown error'}; this._showError(data.errors); break; default: alert('Unknown method'); break; } } else { this._log(window.location.host + ' <- NO METHOD ' + this._getDomain(event.origin), null, null, true); } } else { this._log(window.location.host + ' <- NOT JSON ' + this._getDomain(event.origin), null, null, true); } } else { this._log(window.location.host + ' <- NO DATA ' + this._getDomain(event.origin), null, null, true); } }, /** * Show alert with error * * @param {Object} errorData - Object with errors * * @private */ _showError: function(errorData) { alert(JSON.stringify(errorData, null, 4)); }, /** * Logs to console * * @param {String} title - Message that will be log * @param {String} [data] - Formatted data that will be collapsed * @param {String} [color] - Color in Hex format * @param {Boolean} [warning] - Is it warning message? * * @private */ _log: function(title, data, color, warning) { if (!this.debugMode) { return; } if (!color) { color = '#0066FF'; } if (!!data) { console.groupCollapsed('%c[Plugin API] ' + title, 'color: ' + color + '; ' + (!!warning ? 'font-weight: bold;' : 'font-weight: normal;')); console.log('[Plugin API] ' + data); console.groupEnd(); } else { console.log('%c[Plugin API] ' + title, 'color: ' + color + '; ' + (!!warning ? 'font-weight: bold;' : '')); } }, /** * Business login on plugin init */ pluginInit: function() { var data = { initTime: new Date().getTime() }; this._log(window.location.host + ' INIT. SET DATA TO LOCAL STORAGE', JSON.stringify(data, null, 4)); localStorage.setItem('pluginInitData', JSON.stringify(data)); }, /** * Business login on plugin open * * @param {Object} receivedData - JSON object that contain data from OFSC */ pluginOpen: function(receivedData) { this._log(window.location.host + ' OPEN. GET DATA FROM LOCAL STORAGE', JSON.stringify(JSON.parse(localStorage.getItem('pluginInitData')), null, 4)); $('.json__local-storage').text(localStorage.getItem('pluginInitData')); $('.section__local-storage').show(); $('.form').html(this.renderForm(receivedData)); $('.key').each(function(index, item) { if ($(item).siblings('.value').has('.items').size() !== 0) { $(item).addClass('clickable'); } }).on('click', function() { if ($(this).siblings('.value').has('.items').size() !== 0) { $(this).siblings('.value').toggle(); $(this).toggleClass('collapsed'); } }); $('.button__generate_sign').on('click', function(e) { var canvasElement = $('<canvas>').addClass('value').attr({height: 240, width: 320}).get(0); $(e.target).after(canvasElement); drawSampleSignature(canvasElement); $(e.target).parents('.item').addClass('edited'); this._updateResponseJSON(); $(e.target).remove(); }.bind(this)); $('.value__item').on('input, change', function(e) { //IE10+ $(e.target).parents('.item').addClass('edited'); this._updateResponseJSON(); }.bind(this)); $('.back_method_select, .back_activity_id').on('change', function(e) { //IE10+ this._updateResponseJSON(); }.bind(this)); $('.json__request').text(JSON.stringify(receivedData, null, 4)); $('.submit').click(function() { var json_response = $('.json__response'); if (json_response.is(":hidden") === true) { var form = this.parseForm($('.form')); this._sendPostMessageData(form.data); } else { if (this._isJson(json_response.text())) { var data = JSON.parse(json_response.text()); this._sendPostMessageData(data); } else { alert('JSON parse error!'); } } }.bind(this)); $('.section__ofsc-data').show(); }, /** * Render JSON object to DOM * * @param {Object} data - JSON object * * @returns {jQuery} */ renderForm: function(data) { return this.renderCollection('data', data, true); }, /** * Render JSON object to follow HTML: * * <div class="item"> * <div class="key">{key}</div> * <div class="value">{value}</div> * </div> * <div class="item"> * <div class="key">{key}</div> * <div class="value"> * <div class="items"> * <div class="item"> * <div class="key">{key}</div> * <div class="value">{value}</div> * </div> * <div class="item"> * <div class="key">{key}</div> * <div class="value">{value}</div> * </div> * ... * </div> * </div> * </div> * ... * * @param {String} key - Collection name * @param {Object} items - Child items of collection * @param {Boolean} [isWritable] - Will render as writable? * @param {number} [level] - Level of recursion * @param {string} [parentKey] - parent Key * * @returns {jQuery} */ renderCollection: function(key, items, isWritable, level, parentKey) { var render_item = $('<div>').addClass('item'); var render_key = $('<div>').addClass('key').text(key); var render_value = $('<div>').addClass('value value__collection'); var render_items = $('<div>').addClass('items'); isWritable = isWritable || false; level = level || 1; parentKey = parentKey || ''; var newParentKey = key; if (items) { $.each(items, function(key, value) { if (value && typeof value === 'object') { render_items.append(this.renderCollection(key, value, isWritable, level + 1, newParentKey)); } else { render_items.append(this.renderItem(key, value, isWritable, level + 1, newParentKey).get(0)); } }.bind(this)); } render_item.append(render_key)/*.append('<span>: </span>')*/; render_value.append(render_items); render_item.append($('<br>')); render_item.append(render_value); return render_item; }, /** * Render key and value to follow HTML: * * <div class="item"> * <div class="key">{key}</div> * <div class="value">{value}</div> * </div> * * @param {String} key - Key * @param {String} value - Value * @param {Boolean} [isWritable] - Will render as writable? * @param {number} [level] - Level of recursion * @param {string} [parentKey] - parent Key * * @returns {jQuery} */ renderItem: function(key, value, isWritable, level, parentKey) { var render_item = $('<div>').addClass('item'); var render_value; var render_key; isWritable = isWritable || false; level = level || 1; parentKey = parentKey || ''; render_key = $('<div>').addClass('key').text(key); render_item.append(render_key).append('<span class="delimiter">: </span>'); if (value === null) { value = ''; } if (typeof this.renderReadOnlyFieldsByParent[parentKey] !== 'undefined' && typeof this.renderReadOnlyFieldsByParent[parentKey][key] !== 'undefined' && this.renderReadOnlyFieldsByParent[parentKey][key] === true) { isWritable = false; } switch (key) { case "csign": if (isWritable) { render_value = $('<button>').addClass('button but-ton__generate_sign').text('Generate'); } break; default: if (this.dictionary[key]) { render_value = this.renderSelect(this.dictionary, key, value, isWritable).addClass('value value__item'); } else { render_value = $('<div>').addClass('value val-ue__item').text(value); if (isWritable) { ren-der_value.addClass('writable').attr('contenteditable', true); } } break; } render_item.append(render_value); return render_item; }, /** * Render enums * * <select class="value [writable]" [disabled]> * <option value="{value}" [selected]>{dictionary}</option> * ... * </select> * * @param {Object} dictionary - Dictionary that will be used for Enum rendering * @param {String} key - Just field name * @param {String} value - Selected value * @param {Boolean} isWritable - Will render as writable? * * @returns {HTMLElement} */ renderSelect: function(dictionary, key, value, isWritable) { var render_value; var outs = dictionary[key][value].outs; var allowedValues = [value].concat(outs); var disabled = ''; render_value = $('<select>').css({background: diction-ary[key][value].color}); if (isWritable) { render_value.addClass('writable'); } if (!outs.length || !isWritable) { render_value.attr('disabled', true); render_value.removeClass('writable'); } $.each(allowedValues, function(index, label) { render_value.append('<option' + (label === value ? ' selected' : '') + ' value="' + dictionary[key][label].label + '">' + diction-ary[key][label].translation + '</option>'); }); return render_value; }, /** * Parse form (root HTML element that was rendered) * * @param {HTMLElement} element - root HTML element * * @returns {Object} */ parseForm: function(element) { var form = {}; form.data = { apiVersion: 1, method: 'close', backScreen: $('.back_method_select').val() }; if (form.data.backScreen === 'activity_by_id') { $.extend(form.data, { backActivityId: $('.back_activity_id').val() }); } $.extend(form.data, this.parseCollection(element).data); delete form.data.entity; delete form.data.resource; return form; }, /** * Convert HTML elements to JSON * * @param {HTMLElement} rootElement - Root element that should be parsed recursively * * @returns {Object} * * <div class="key">activity</div> * <div class="value value__collection"> * <div class="items"> <-------------------------------- rootElement !!! * <div class="item edited"> * <div class="key">WO_COMMENTS</div> * <div class="value">text_comments</div> * </div> * <div class="item"> * <div class="key">aid</div> * <div class="value">4225274</div> * </div> * <div class="item"> * <div class="key">caddress</div> * <div class="value">text_address</div> * </div> * </div> * </div> * * converts to: * * { * "aid": "4225274", * "WO_COMMENTS": "text_comments" * } * */ parseCollection: function(rootElement) { var returnObject = {}; $(rootElement).children('.item').each(function(itemIndex, item) { var parentKey; var valueKey; var value; var mandatoryField = false; parentKey = $(rootElement).parent().siblings('.key').get(0); valueKey = $(item).children('.key').get(0); //Logic of mandatory fields if ((parentKey !== undefined) && ( ($(parentKey).text() == 'activity' && $(valueKey).text() == 'aid') || ($(parentKey).text() == 'inventory' && $(valueKey).text() == 'invid') )) { mandatoryField = true; } if ($(item).hasClass('item') === true && ($(item).hasClass('edited') === true || mandatoryField)) { value = $(item).children('.value').get(0); if ($(value).children('.items').size() > 0) { returnObject[$(valueKey).text()] = this.parseCollection($(value).children('.items').get(0)); } else { switch ($(value).prop("tagName")) { case 'SELECT': returnObject[$(valueKey).text()] = $(val-ue).val(); break; case 'CANVAS': returnObject[$(valueKey).text()] = val-ue.toDataURL(); break; default: returnObject[$(valueKey).text()] = $(val-ue).text(); break; } } } }.bind(this)); return returnObject; }, /** * Update JSON * * @private */ _updateResponseJSON: function() { var form = this.parseForm($('.form')); $('.json__response').text(JSON.stringify(form.data, null, 4)); }, /** * Initialization function */ init: function() { $('.back_method_select').change(function() { if ($('.back_method_select').val() == 'activity_by_id') { $('.back_activity_id').show(); } else { $('.back_activity_id').val('').hide(); } }); $('.json_request_toggle').click(function() { $('.json__response').hide(); $('.json__request').toggle(); }); $('.json_response_toggle').click(function() { $('.json__request').hide(); this._updateResponseJSON(); $('.json__response').toggle(); }.bind(this)); this._messageListener = this._getPostMessageData.bind(this); window.addEventListener("message", this._messageListener, false); //Only IE9+ this.pluginInit(); this._sendPostMessageData({ apiVersion: 1, method: 'ready' }); } }); })(jQuery);
signature.js
function drawSampleSignature(canvas) { if (!canvas.getContext) { return; } var c = canvas.getContext('2d'); c.fillStyle="#ffffff"; c.strokeStyle = "#000000"; c.lineWidth = 1.5; c.lineCap = "round"; // White background c.rect(0, 0, 320, 230); c.fill(); c.beginPath(); // C c.moveTo(38, 57); c.bezierCurveTo(38, 57, 39, 78, 44, 77); c.bezierCurveTo(50, 76, 58, 21, 43, 21); c.bezierCurveTo(26, 21, 20, 119, 35, 120); c.bezierCurveTo(50, 120, 68, 91, 77, 65); // usto c.moveTo(66, 76); c.bezierCurveTo(66, 76, 67, 112, 71, 111); c.bezierCurveTo(75, 110, 80, 77, 81, 76); c.bezierCurveTo(82, 74, 78, 100, 84, 100); c.bezierCurveTo(87, 100, 95, 73, 95, 70); c.bezierCurveTo(96, 66, 79, 63, 90, 84); c.bezierCurveTo(95, 94, 100, 90, 111, 102); c.bezierCurveTo(115, 107, 96, 121, 96, 118); c.bezierCurveTo(97, 108, 108, 101, 113, 86); c.bezierCurveTo(118, 72, 117, 38, 118, 45); c.bezierCurveTo(119, 52, 126, 114, 129, 111); c.bezierCurveTo(130, 110, 121, 88, 114, 89); c.bezierCurveTo(108, 89, 133, 87, 138, 77); c.bezierCurveTo(142, 66, 148, 76, 148, 81); c.bezierCurveTo(149, 86, 149, 95, 145, 95); c.bezierCurveTo(137, 95, 138, 77, 138, 77); // mer c.moveTo(149, 87); c.bezierCurveTo(149, 87, 151, 72, 153, 71); c.bezierCurveTo(155, 71, 158, 91, 159, 95); c.bezierCurveTo(160, 99, 158, 68, 161, 69); c.bezierCurveTo(164, 69, 161, 85, 164, 84); c.bezierCurveTo(166, 84, 164, 66, 166, 67); c.bezierCurveTo(168, 67, 170, 89, 176, 87); c.bezierCurveTo(182, 86, 186, 52, 180, 52); c.bezierCurveTo(175, 51, 173, 84, 194, 89); c.bezierCurveTo(209, 93, 190, 64, 202, 61); c.bezierCurveTo(209, 60, 208, 72, 213, 71); c.bezierCurveTo(215, 71, 212, 61, 214, 60); c.bezierCurveTo(215, 58, 208, 100, 215, 98); c.bezierCurveTo(222, 95, 243, 64, 245, 57); // S c.moveTo(137, 131); c.bezierCurveTo(143, 122, 123, 106, 117, 127); c.bezierCurveTo(112, 147, 131, 157, 138, 161); c.bezierCurveTo(152, 169, 138, 181, 126, 184); c.bezierCurveTo(116, 187, 123, 179, 123, 179); // i c.moveTo(148, 149); c.bezierCurveTo(148, 149, 147, 152, 149, 153); c.moveTo(152, 158); c.bezierCurveTo(156, 167, 150, 183, 153, 184); // gna c.moveTo(170, 160); c.bezierCurveTo(170, 160, 166, 151, 162, 152); c.bezierCurveTo(158, 153, 160, 168, 164, 168); c.bezierCurveTo(168, 168, 174, 165, 170, 152); c.bezierCurveTo(167, 140, 181, 187, 180, 199); c.bezierCurveTo(180, 215, 164, 219, 164, 202); c.bezierCurveTo(165, 194, 182, 168, 183, 154); c.bezierCurveTo(184, 148, 176, 184, 182, 184); c.bezierCurveTo(187, 185, 184, 159, 186, 161); c.bezierCurveTo(188, 162, 189, 175, 193, 174); c.bezierCurveTo(196, 173, 195, 156, 199, 155); c.bezierCurveTo(202, 153, 207, 164, 205, 169); c.bezierCurveTo(203, 175, 196, 171, 196, 163); c.bezierCurveTo(197, 155, 208, 169, 207, 155); c.bezierCurveTo(207, 143, 211, 171, 215, 169); c.bezierCurveTo(218, 167, 215, 153, 216, 153); // t c.moveTo(218, 132); c.bezierCurveTo(222, 127, 222, 174, 227, 175); c.bezierCurveTo(231, 176, 233, 171, 233, 171); // -ure c.moveTo(212, 156); c.bezierCurveTo(212, 156, 231, 143, 234, 144); c.bezierCurveTo(236, 144, 234, 162, 238, 162); c.bezierCurveTo(243, 162, 242, 145, 244, 146); c.bezierCurveTo(247, 146, 247, 158, 249, 158); c.bezierCurveTo(251, 158, 249, 137, 251, 138); c.bezierCurveTo(254, 139, 252, 142, 256, 143); c.bezierCurveTo(259, 143, 254, 168, 269, 161); c.bezierCurveTo(284, 154, 271, 127, 268, 126); c.bezierCurveTo(266, 125, 260, 139, 274, 157); c.bezierCurveTo(288, 175, 298, 173, 298, 173); c.stroke(); }
cache.manifest
CACHE: ./index.html ./style.css ./plugin.js ./signature.js ./logo.svg //code.jquery.com/jquery-2.1.4.min.js FALLBACK: NETWORK: *
logo.svg
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.0" width="662.84644" height="94.145668" id="svg115845"> <defs id="defs115847"> <clipPath id="clp82"> <path d="M 1001.6,870.49 L 1036.3,870.49 L 1036.3,857.41 L 1001.6,857.41 L 1001.6,870.49 z " id="path1826"/> </clipPath> <clipPath id="clp83"> <path d="M 1001.6,870.49 L 1036.3,870.49 L 1036.3,857.41 L 1001.6,857.41 L 1001.6,870.49 z " id="path1835"/> </clipPath> <clipPath id="clp84"> <path d="M 1001.6,870.49 L 1036.3,870.49 L 1036.3,857.41 L 1001.6,857.41 L 1001.6,870.49 z " id="path1844"/> </clipPath> <clipPath id="clp81"> <path d="M 1000.9,934.34 L 1038.6,934.34 L 1038.6,922.24 L 1000.9,922.24 L 1000.9,934.34 z " id="path1800"/> </clipPath> <clipPath id="clipPath116030"> <path d="M 1001.6,870.49 L 1036.3,870.49 L 1036.3,857.41 L 1001.6,857.41 L 1001.6,870.49 z " id="path116032"/> </clipPath> <clipPath id="clipPath116038"> <path d="M 1001.6,870.49 L 1036.3,870.49 L 1036.3,857.41 L 1001.6,857.41 L 1001.6,870.49 z " id="path116040"/> </clipPath> <clipPath id="clipPath116046"> <path d="M 1001.6,870.49 L 1036.3,870.49 L 1036.3,857.41 L 1001.6,857.41 L 1001.6,870.49 z " id="path116048"/> </clipPath> </defs> <g transform="translate(-702.6538,-712.5837)" id="layer1"> <g id="g16337"> <path d="M 980.65099,771.70039 L 1021.3029,771.70039 L 999.80762,737.1177 L 960.35637,799.64472 L 942.40142,799.64472 L 990.38729,724.53644 C 992.47368,721.50175 995.95082,719.66834 999.80762,719.66834 C 1003.5375,719.66834 1007.0147,721.43856 1009.0379,724.41 L 1057.2134,799.64472 L 1039.2584,799.64472 L 1030.7865,785.67256 L 989.62847,785.67256 L 980.65099,771.70039 z M 1167.1573,785.67256 L 1167.1573,720.42701 L 1151.9207,720.42701 L 1151.9207,792.05805 C 1151.9207,794.01795 1152.6795,795.9146 1154.1335,797.3687 C 1155.5874,798.82285 1157.5474,799.64472 1159.697,799.64472 L 1229.1786,799.64472 L 1238.1561,785.67256 L 1167.1573,785.67256 z M 915.08933,773.97641 C 929.88361,773.97641 941.89588,762.02739 941.89588,747.23331 C 941.89588,732.43928 929.88361,720.42701 915.08933,720.42701 L 848.43367,720.42701 L 848.43367,799.64472 L 863.66423,799.64472 L 863.66423,734.39918 L 914.07773,734.39918 C 921.15891,734.39918 926.84882,740.15238 926.84882,747.23331 C 926.84882,754.31423 921.15891,760.06749 914.07773,760.06749 L 871.12457,760.00424 L 916.60647,799.64472 L 938.7347,799.64472 L 908.13505,773.97641 L 915.08933,773.97641 z M 754.67521,799.64472 C 732.80632,799.64472 715.05966,781.94244 715.05966,760.06749 C 715.05966,738.19249 732.80632,720.42701 754.67521,720.42701 L 800.71978,720.42701 C 822.59473,720.42701 840.32876,738.19249 840.32876,760.06749 C 840.32876,781.94244 822.59473,799.64472 800.71978,799.64472 L 754.67521,799.64472 z M 799.69555,785.67256 C 813.86396,785.67256 825.33883,774.22928 825.33883,760.06749 C 825.33883,745.90564 813.86396,734.39918 799.69555,734.39918 L 755.69287,734.39918 C 741.53103,734.39918 730.04958,745.90564 730.04958,760.06749 C 730.04958,774.22928 741.53103,785.67256 755.69287,785.67256 L 799.69555,785.67256 z M 1089.0142,799.64472 C 1067.1392,799.64472 1049.3739,781.94244 1049.3739,760.06749 C 1049.3739,738.19249 1067.1392,720.42701 1089.0142,720.42701 L 1143.7016,720.42701 L 1134.7873,734.39918 L 1090.0258,734.39918 C 1075.8639,734.39918 1064.3577,745.90564 1064.3577,760.06749 C 1064.3577,774.22928 1075.8639,785.67256 1090.0258,785.67256 L 1144.9659,785.67256 L 1135.9885,799.64472 L 1089.0142,799.64472 z M 1275.3309,785.67256 C 1263.6346,785.67256 1253.7087,777.83296 1250.6739,767.02192 L 1315.7932,767.02192 L 1324.7707,753.04976 L 1250.6739,753.04976 C 1253.7087,742.30196 1263.6346,734.39918 1275.3309,734.39918 L 1320.0292,734.39918 L 1329.0699,720.42701 L 1274.3193,720.42701 C 1252.4443,720.42701 1234.679,738.19249 1234.679,760.06749 C 1234.679,781.94244 1252.4443,799.64472 1274.3193,799.64472 L 1321.2936,799.64472 L 1330.271,785.67256 L 1275.3309,785.67256" id="path16197" style="fill:#fff;fill-rule:nonzero;stroke:none"/> <path d="M 1337.2258,728.8356 C 1337.2258,724.97899 1340.3233,721.88111 1344.1801,721.88111 C 1348.1001,721.88111 1351.1976,724.97899 1351.1976,728.8356 C 1351.1976,732.75534 1348.1001,735.85328 1344.1801,735.85328 C 1340.3233,735.85328 1337.2258,732.75534 1337.2258,728.8356 z M 1344.1801,737.81317 C 1349.1112,737.81317 1353.0944,733.83013 1353.0944,728.89879 C 1353.0944,723.96745 1349.1112,719.98446 1344.1801,719.98446 C 1339.3117,719.98446 1335.329,723.96745 1335.329,728.89879 C 1335.329,733.83013 1339.3117,737.81317 1344.1801,737.81317 z M 1343.3581,723.58814 C 1344.7489,723.58814 1345.3181,723.65133 1345.9505,723.9042 C 1347.7205,724.47325 1347.91,726.05378 1347.91,726.68602 C 1347.91,726.81246 1347.91,727.12857 1347.7836,727.50788 C 1347.7205,727.88725 1347.4677,728.64592 1346.5825,729.21491 C 1346.4561,729.27815 1346.3929,729.34135 1346.1401,729.46778 L 1348.4161,733.57726 L 1346.2033,733.57726 L 1344.1801,729.7839 L 1342.7894,729.7839 L 1342.7894,733.57726 L 1340.8294,733.57726 L 1340.8294,723.58814 L 1343.3581,723.58814 z M 1344.0537,728.14012 C 1344.6857,728.07693 1345.3181,728.07693 1345.6973,727.50788 C 1345.8868,727.25501 1345.9505,727.00214 1345.9505,726.62277 C 1345.9505,726.11703 1345.6341,725.67447 1345.1917,725.42155 C 1344.7489,725.23192 1344.3065,725.23192 1343.3581,725.23192 L 1342.7894,725.23192 L 1342.7894,728.14012 L 1344.0537,728.14012" id="path16199" style="fill:#fff;fill-rule:nonzero;stroke:none"/> </g> </g> </svg>
Upgrading from Previous Versions
Suppose you have created a plug-in API for a specific version of Oracle Field Service Cloud and Oracle Field Service Cloud is upgraded with the next version. You must upgrade your plug-in API to make it compatible with the latest release of Oracle Field Service Cloud.
Existing external plug-ins will continue functioning according to their settings; their type will change from External to HTML5 Application.
You can edit the settings of the existing external plug-ins in version 16.2.
Caution: Selecting the Use Plugin API check box for a plug-in created in a previous version makes the plug-in unusable.Existing internal plug-ins will continue functioning according to their settings.
You cannot edit the settings of the existing internal plug-ins; they will be displayed as Read-Only.
No new internal plug-ins can be created.
The plug-in API now accepts raw JS objects as values for PostMessage data. Original JSON strings are still supported, so backward compatibility with the existing plug-ins framework that were implemented in release 17.2 are retained. However, file properties cannot be updated in this case.