Act! Plugins Library
The @act/plugins
library was built using TypeScript. This allows us to provide defined types for our objects to help you in your development. This does not need you mean to use TypeScript within your own project (although we would recommend it!), but some parts of the documentation may have TypeScript syntax for type definitions.
To communicate with Act!, initialize an ActPlugin object.
import { ActPlugin } from '@act/plugins';
const actPlugin = new ActPlugin();
actPlugin.init(window);
Calling init
will create the link between the Act! application and your application. You must pass the window
object as that is the object that event handling for messages will take place from.
Properties
claims
- returns a promise with the current user's claims.
context$
- a stream object that emits the current context for Act!. To use this object, .subscribe
to the property and pass the function to handle the context.
// The entity is off of the `payload` property of the context object
actPlugin.context$.subscribe(context => this.handleContext(context));
// Destructure the context object and pull the payload property
handleContext({ payload }) {
// If the context is a Contact entity, the fullName will be printed (since this property only exists on a Contact)
const {
fullName,
} = payload;
if (fullName) {
console.log(fullName);
}
}
locale$
- a stream object that emits the current locale for Act!. To use this object, .subscribe
to the property and pass the function to handle the context.
actPlugin.locale$.subscribe(locale => this.handleLocale(locale));
handleLocale(locale) {
this.locale = locale;
}
searchContext$
- a stream object that emits the most recent search context for Act!. The object passed to the stream has the following structure:
actPlugin.searchContext$.subscribe((searchContext: { entity: 'contact' | 'company' | 'group' | 'opportunity', query: ODataQuery, searchTerm: string }) => {
this.searchContext = searchContext;
});
The stream will always return the most recent search context. Therefore if you are on the detail view when this subscription occurs, you will receive a searchContext (if there was one). Be sure to filter the context based on the entity passed to receive the desired search context for the entity you plugin works off of.
Functions
init
- initialize the ActPlugin
object. Pass the window context to listen to messages on (default to the global window).
localStorage
- a localStorage wrapper to expose that library functionality for plugin usage. This object has two functions:
getItem
: takes a string as the key and returns a promise with the value stored in localStorage or undefined otherwise.setItem
: takes a string as the key and a value as the item to store in localStorage. Returns a promise that resolves when the item has been stored.
// The shared parameter, used to share storage values between your company's plugins
async () => {
var isShared = false;
await actPlugin.localStorage.setItem('your-localStorage-item', 'test', isShared);
const storageItem = await actPlugin.localStorage.getItem('your-localStorage-item', isShared);
}
Note: each option has a shared
parameter that can be used to share storage items across your plugins via the company name
navigate
- navigates the Act! application to the passed path. Use the Act! application to discover the desired path. Note: do not include the origin within the path, it should only be the route as the path
// Here is an example call that navigates to the contact detail view for a specific contact and
// shows the tab with the name of `your-plugin-tab`
actPlugin.navigate(`contacts/${contactId}#your-plugin-tab`);
openUrl
- opens a new window with the provided url. This is necessary because Act! isolates your plugin into an iframe with a certain security level. In order to open a new URL, your application must request Act! to open it for you.
registerAction
- dynamically registers an action to the passed entity. The action has the same form as the extension payload for an action extension.
Note: make sure you have the actions and translations already set in the manifest for any dynamic action as you cannot dynamically load a manifest
actPlugin.registerAction('contact', {
functionName: 'onClick',
functionParams: [{ name: 'Rodney McKay', role: 'Scientist' }], // add dynamic parameters to be passed to your actions
label: 'PLUGIN_DOCS',
staticLabel: 'A button', // use this to override label with a static value not existent in the manifest
name: 'test',
position: {
name: 'call',
type: 'after'
},
});
requestData
- returns a promise with the response body as the resolve value. The parameter for the function should be an object of the following structure:
{
body?: any,
context?: any;
entity: string,
relatedEntity?: string;
request: 'get' | 'create' | 'update' | 'delete' | 'patch',
query?: ODataQuery,
}
This function passes the parameters to the correct service handler within the Act! application to make your query. It will then return the results of that query to your plugin for use. Because the application handles the data for you, we can provide this service with offline functionality built in.
For basic requests (such as list or single entity fetching), the call would look like:
// Gets the list of contacts
actPlugin.requestData({ entity: 'contacts' });
// Gets a single contact with the id of '000' (note this is not a valid Act! id, but is simply for demonstration purposes)
actPlugin.requestData({ entity: 'contacts', query: '000' });
The query can be either a string to represent the id
of the entity you wish to fetch, or an ODataQuery
object from the @act/api
package. The query object will be the path into more complex style data fetching. Here is an example of fetching:
actPlugin.requestData({
entity: 'contacts',
query: new ODataQuery(
null,
new ODataSort('startTime', ODataSortDirection.DESCENDING)
),
});
Related entities can be accessed with relation to the main entity via the relatedEntity
property in the request object. Requesting data like this requires that you pass the context in the request object:
actPlugin.context$.subscribe((context) => {
requestData({ context, entity: 'contacts', relatedEntity: 'activities' });
});
Furthermore, you can use the ODataQuery
object to create complex queries off of those related entities:
actPlugin.context$.subscribe((context) => {
requestData({
context,
entity: 'contacts',
relatedEntity: 'activities',
query: new ODataQuery(
null,
new ODataSort('startTime', ODataSortDirection.DESCENDING)
),
});
});
It is highly recommended to check out the Web API
documentation for the available data to query via the API, the respective models for each, and the available HTTP request types for each entity.
requestUser
- returns a promise with the User
object as the resolve value.
sessionStorage
- a sessionStorage wrapper to expose that library functionality for plugin usage. This object has two functions:
getItem
: takes a string as the key and returns a promise with the value stored in sessionStorage or undefined otherwise.setItem
: takes a string as the key and a value as the item to store in sessionStorage. Returns a promise that resolves when the item has been stored.
// The shared parameter, used to share storage values between your company's plugins
async () => {
var isShared = false;
await actPlugin.sessionStorage.setItem('your-sessionStorage-item', 'test', isShared);
const storageItem = await actPlugin.sessionStorage.getItem('your-sessionStorage-item', isShared);
}
Note: each option has a shared
parameter that can be used to share storage items across your plugins via the company name
setActions
- sets the global actions used by your plugin. This should only be necessary if your plugin extends the Act! application through the addition of contextual action buttons or any functions that exist outside of your plugin application (i.e. within the Act! application).
closeDialog
- tells the Act! application to close the dialog currently displayed by the showDialog
function call.
showDialog
- tells the Act! application to show a dialog based on the passed type
and payload
parameters. This function returns an object with a close
function on it to tell Act! to close the dialog. This allows your plugin control over the lifecycle of the dialog. Note: If you are going to add click handlers to the dialogs, then you will need to add the actions file to your manifest. This will allow Act! to call the correct function handler for you and resolve the action. See the Actions Extension below for steps to create that file and necessary structure. This is not the case for iframe dialogs in which you control all actions and must handle the dialog lifecycle.
The payload
object takes the following form:
interface PluginDialogPayload {
actions?: PluginDialogAction[];
content?: string;
dialog?: string;
title: string;
subTitle?: string;
size?: {
height: string;
width: string;
};
}
The currently supported dialogs types are:
- alert: a dialog with a close action used to inform the user of something without requiring input.
- confirm: a dialog requiring user input. Used for situations where the user may want to cancel the action or to continue.
- iframe: the most robust solution for a dialog. This gives you full control over the dialog content as well as the actions and life cycle. The dialog is invoked via the dialog payload property that should map to the name defined in your
plugin-manifest.json
Note: You must handle the dialog close event through your actions
"dialogs": {
"Sample_List": {
"src": "https://example-deployed-site.com/dialog.html",
"size": {
"height": "500px",
"width": "350px"
}
}
// Note: You can also declare the size of the dialog through the payload here
// ex. actPlugin.showDialog('iframe', { dialog: 'Sample_List', size: { height: '500px', width: '350px' } });
actPlugin.showDialog('iframe', { dialog: 'Sample_List' });
- toast: a non-interactive message box that shows for a pre-defined amount of time based on the Act! design guidelines. Use this for displaying information that may or may not have user input but predominantly is used as a notification style dialog.
- text: a simple text dialog. See example in below. Add the following function call where you would like to show the dialog:
There are two cases to cover. The first is calling showDialog
from the actions.js
file.
Here is an example of doing so:
dialogRef = actPlugin.showDialog('text', {
actions: [
{
name: 'close',
label: 'Close',
onClick: 'handleCloseClick',
},
],
content: 'Created a task',
title: 'Quick Task',
});
Then in your global actions file, you will need the click handler:
handleCloseClick: () => {
dialogRef.close();
};
The following case is an example of calling showDialog
from you application rather than the actions.js
file:
class TestApplication {
actPlugin;
dialogRef;
// Some initializer for your application
constructor() {
this.actPlugin = new ActPlugin();
this.actPlugin.init();
// Set the actions available for the Act! application to call here
this.actPlugin.setActions({
handleCloseClick: () => this.handleCloseClick(),
});
}
// The function to called to display the dialog and set the reference
showDialog() {
this.dialogRef = this.actPlugin.showDialog('text', {
actions: [
{
name: 'close',
label: 'Close',
onClick: 'handleCloseClick', // Note: this string should match the value set in the `setActions` call
},
],
content: 'Created a task',
title: 'Quick Task',
});
}
// The handler that `ActPlugin` will call based on the set actions
handleCloseClick() {
if (!this.dialogRef) {
return;
}
this.dialogRef.close();
this.dialogRef = null;
}
}
updateContext
- a function that takes the context object and merges the values (as overrides) to the existing context within the Act! application.
// Here is an example action that updates the current Contact context to the values in contactUpdate when the updateContext action is called
let contact = null;
const actions = {
/**
* Define plugin actions here that will be called by function name
*/
onActionClick: () => {
// An example call to update the current Contact model to the following firstName and lastName values
const contactUpdate = {
...contact,
firstName: 'Rodney',
lastName: 'McKay',
namePrefix: 'Dr.',
};
actPlugin
.requestData({
entity: 'contacts',
request: 'update',
body: contactUpdate,
})
.then((updatedContact) => {
actPlugin.updateContext(updatedContact);
});
},
};
actPlugin.context$.subscribe((context) => {
contact = context;
});
actPlugin.setActions(actions);
Actions
If your plugin requires actions used by the Actions
extensions, then you must create a new actions.js
file that should be served by your plugin.
The structure for the file should be:
const actions = {
// Name this function based on your extension function strings
testAction: () => {
// Do something here...
},
};
actPlugin.setActions(actions);
The actPlugin
object is exposed to the actions.js
file by Act! (see restrictions on importing third party libraries and DOM manipulation within WebWorkers). It contains all properties and functions as described above for the ActPlugin
API. Please note that this means all code associated with the actions file should be standard JavaScript without any third party libraries. If you would like to use third party libraries, you must bundle the whole library into your actions.js
file via a tool such as Webpack
.
Lifecycle
Next, let's talk about the lifecycle of your plugin.