Skip to content

Accessing the main page from a Plugin

Adam Hooper edited this page Mar 27, 2020 · 21 revisions

Sometimes, a plugin will want to reach outside of its iframe. It will want to:

  • get state
  • set state
  • listen for state changes

To do this, use window.parent.postMessage().

Example Code

Here's a plugin that documents how to set Overview's selection from within a plugin:

https://github.com/overview/overview-apps/blob/master/document-list-params/show.html

Overview's postMessage() Interface

Message-sending Concepts

Here's what you need to know:

  • This interface is made for plugins to control the Overview web client on behalf of the user.
  • window.parent.postMessage() happens from the plugin web page. If you want your plugin's HTTP backend to control Overview's web client, you'll need to do it through your plugin web page.
  • window.parent.postMessage(...) returns immediately, and it never gives a return value.
  • Your plugin web page can only postMessage to the Overview web client it appears in: it cannot message any other recipient. In particular, the Overview web client is not the Overview REST API, nor is it Overview's web server. (Your plugin may access Overview's REST API over traditional HTTP; it may not send requests to Overview's web server outside of the API.)

Calling Conventions

Think of a postMessage() call to Overview as a function call. Here's an example:

const message = {
  call: 'refineDocumentListParams', // call
  args: [ { q: 'search' } ],        // arguments
}
window.parent.postMessage(message, '*')    // postMessage() with message and origin
  • The call is one of the messages documented below.
  • The args is an Array of arguments. Most calls accept one argument, so use a single-element Array.
  • window.parent.postMessage() requires an "origin" argument, and we passed '*' here. That's a red flag, so let's go into detail. The origin security layer lets your plugin prevent itself from sending a message to a server you don't expect. That keeps your plugin's data away from an evil Overview lookalike. If your plugin derives its data from a valid apiKey (which an evil Overview lookalike can't guess), and if your plugin is accessible from multiple Overview instances (such as a development instance, a staging instance and a production instance), then it makes sense to pass '*' as the origin. Many plugins don't store any data at all: for them, an origin of '*' is fine.

Automatic "notify:" Messages from Overview

The Overview web page will keep your plugin abreast of what the user is doing. Listen for its messages like this:

window.addEventListener('message', function(ev) {
  // TODO make sure ev.origin is what we expect. This is only important if your
  // plugin opens an iframe itself -- in that case, this message may come from its
  // parent or its child iframe, and we want to adjust our actions accordingly

  console.log(`Event: ${JSON.stringify(ev.data.event)}`)    // e.g. "notify:documentListParams"
  console.log(`Arguments: ${JSON.stringify(ev.data.args)}`) // e.g. [ { q: "search" } ]
  // (Now do something with the message, if you care about it.)
})

Overview will post these messages to the plugin iframe, each containing event and args, whether or not you have added a listener.

Calls that "return" values

As mentioned above, there is no return value from window.parent.postMessage(). But you can request that Overview send a notify: message. This is useful when your plugin wants to access information during page load: first your plugin needs to start listening for messages, and then it can request that Overview notify it of what it cares about.

For instance:

window.addEventListener('message', function(ev) {
  if (ev.data.event === 'notify:documentListParams') {
    console.log("The document list is now: " + JSON.stringify(ev.data.args[0]))
  }
})

window.parent.postMessage({ call: 'notifyDocumentListParams' }, '*')
// Very soon, Overview will send a 'notify:documentListParams' message.

List of notify: Messages

The Overview web client will automatically send these messages to your plugin after their values change or your plugin requests them.

  • notify:documentListParams: args[0] contains the DocumentListParams, an Object describing the current search criteria for the documents the Overview web client is displaying. In this guide, we've used { q: "search" } as a frequent example. See https://github.com/overview/overview-server/blob/master/web/js/apps/Show/models/DocumentListParams.js for living documentation.
  • notify:documentList: args[0] contains an Object with { length: <Number> }. The document list length changes to null whenever the user tries to search for new documents; it will change to non-null when the server responds with a count.
  • notify:documentSet: args[0] contains a DocumentSet Object. This has a metadataSchema Object describing all metadata fields.
  • notify:document: args[0] contains an Object with { id, title, snippet, pageNumber, url, metadata, indexInDocumentList, isFromOcr, pdfNotes }.

List of Function Calls

Your plugin may send these messages to the Overview web client at any time. Each will make the Overview web client do something:

  • Calls that modify Overview's state
    • goToNextDocument(): navigate to the next document in the document list.
    • goToPreviousDocument(): navigate to the previous document in the document list.
    • notifyDocumentSet(): send the plugin a notify:documentSet message.
    • notifyDocument(): send the plugin a notify:document message.
    • notifyDocumentList(): send the plugin a notify:documentList message.
    • notifyDocumentListParams(): send the plugin a notify:documentListParams message.
    • refineDocumentListParams(changedParams): changes the given aspect(s) of the search parameters and searches for documents matching the new parameters. For instance, if the user has already searched for tag "A" and you send { q: "search" }, then the user will now be searching for documents matching the search phrase "search" with tag "A". You can also "un-set" a certain part of the search by passing it as null: for instance, { q: null }.
    • setDocumentListParams(newParams): changes all aspects of the search parameters and searches for documents matching what you request. For instance, if the user has already searched for tag "A" and you send { q: "search" }, then the user will now be searching for documents matching the search phrase "search" with any tag.
  • Calls that let Overview invoke your plugin
    • postMessageToPluginIframes(...): sends a window.postMessage() message to all plugin iframes. This calling convention is different: call it like { call: 'postMessageToPluginIframes', message: "here is the message" }. The recipients will see message as their ev.data. In this example, they will see "here is the message": Overview will not touch the message, and it won't wrap it with an event or args.
    • setDocumentDetailLink({ url, title, text, iconClass }): shows a "details" prompt above each document from the { url, title, text, iconClass } object you pass. When the user clicks the link, a popup will appear with url in an iframe (replacing :documentId in the URL with the current document ID). Overview will add apiToken, documentSetId and server to the URL's query string. If two Views set the same url, only the first View's link will appear.
    • setModalDialog(options): opens a modal dialog atop of Overview containing an iframe pointed at options.url. Pass { url: null } to dismiss the dialog. (The user can also dismiss the dialog by clicking an "X" in its corner.)
    • setRightPane(options): opens a right pane to the right of the document list containing an iframe pointed at options.url. Pass { url: null } to close the pane.
    • setViewFilter(options): adds another search filter that works even when the plugin is not visible. This requires several options:
      • renderOptions: see FilterView comments
      • choices: see FilterView comments
      • url: a URL -- presumably to an endpoint in your plugin -- that the Overview server will access when searching for documents. Overview will send an HTTP GET request to the given URL, adding apiToken, ids and operation to the query string.
        • Security risk: since Overview sends an API key to this URL, it grants the URL's web server access to all your documents. Use an HTTPS URL that you trust with your documents.
        • The ids are Strings the plugin specified in options.choices, comma-separated. (Do not allow commas in your IDs.)
        • The operation determines how the filter is to interpret those IDs: documents matching "any", "all" or "none" of them.
        • The server must respond with an application/octet-stream bitset.
        • The bitset must map documentId & 0xffffffff to 1 iff the document should appear in the returned document list.
        • The bitset must be encoded such that the most-significant bit of the first byte will be document 0, the second-most-significant bit will be document 1, and so on. Overview will ignore bits referring to documents that do not exist.
        • Overview will AND this bitset with all other filters': the search phrase, tags, and any other ViewFilters.
    • setViewFilterChoices(choices): overwrites setViewFilter's options.choices. Does not change the current document list.
    • setViewFilterSelection({ ids, operation }): sets ids and operation in the select box that you created with setViewFilter. ids is an Array of Strings; operation is one of "any", "all" or "none".
  • Calls that interact with the PDF view, the graphical view Overview displays for most documents:
    • beginCreatePdfNote(): If a PDF is being displayed, clicks the "Add Note" button in it. When a note is added, your plugin will receive a notify:document message.
    • goToPdfNote(pdfNote): If a PDF is being displayed, call this with one of the document.pdfNotes values from the previous notify:document message to scroll the PDF to that note for viewing or editing.
  • Calls that relate to document metadata:
    • patchDocument(options): if you pass options.title, saves a new title on the document. If you pass options.metadata, saves new metadata. (These will cause a notify:document.)
    • openMetadataSchemaEditor(): open the metadata schema editor.
Clone this wiki locally