Embedding Dashboards and Charts
- 1 Embedding A Panintelligence Chart
- 1.1 Step 1 - Ensure your dashboard is running in HTTPS
- 1.2 Step 2 - Configuring the Dashboard
- 1.3 Step 3 - Getting a token
- 1.4 Step 4 - Embedding
- 1.4.1 Chart
- 1.4.2 Category
- 1.4.2.1 categoryMode=0: Normal
- 1.4.2.2 categoryMode=1: Headless Single Category
- 1.4.2.3 categoryMode=2: Single Category
- 1.4.2.4 categoryMode=3 Headless Normal
- 1.4.3 Interacting with the left & right drawers
- 1.5 Using filters to filter a chart via JavaScript while embedded
- 1.5.1 With Category Filters (whole category)
- 1.5.2 With Temporary Filters (per chart)
- 1.5.2.1 Current URL Structure
- 1.5.3 Types of filters
- 1.5.3.1 Delimiters
- 1.6 Limiting Drill Levels On Embedded Categories
- 2 Gotchas
You can find a live code example that details how to embed the dashboard in your dashboard installation.
If your dashboard is installed locally with all the default settings, to access the example go to http://localhost:8224/pi_embed_example/.
Embedding A Panintelligence Chart
Step 1 - Ensure your dashboard is running in HTTPS
See Configuring HTTPS for more information.
Step 2 - Configuring the Dashboard
By default, embedding the dashboard in an iframe will no longer work without configuration. This is by design to provide the most secure out of the box configuration.
We have implemented click-jacking protection in the form of the recommended 'frame-ancestors'. By default, this is configured to disallow any embedding from any site. This can be changed via the Panintelligence Configuration Tool.
Using the Configuration Tool
On Windows
You can get to the Configuration Tool via the Start menu:
Or by navigating to the Dashboard installation folder. On Windows, the default location is C:\Program Files\Dashboard\Dashboard
:
Please ensure that you run configuration-tool-GUI.exe
as an administrator.
On Linux
Double click configuration-tool-GUI.AppImage
.
Editing the CORS / Frame Ancestors / Trusted Hosts and the Cookie settings
If you need embedding to work with Internet Explorer, you must set X Frame Host
option too. Keep in mind that we do not maintain support for Internet Explorer.
From the Trusted Hosts dropdown you’ll see the following values:
none
: No embedding allowedself
: Embedding allowed from the same hostcustom
: Embedding allowed from the hosts specified in the space separated list
Protocol (http/https) is required for trusted hosts / frame ancestors when using a wildcard.
Using environment variables
Settings can be applied via environment variables and this is true for these fields too:
PI_TOMCAT_FRAME_ANCESTORS="http://localhost:5000 https://my.awesome.app"
PI_TOMCAT_COOKIE_SECURE=true
PI_TOMCAT_COOKIE_SAMESITE=none
If you need embedding to work with Internet Explorer, you must set PI_TOMCAT_X_FRAME_OPTIONS
option too. Keep in mind that we do not maintain support for Internet Explorer.
Protocol (http/https) is required for trusted hosts / frame ancestors when using a wildcard.
Using the configuration file
These settings can also be applied via a configuration file to support unattended installs and GUI-less environments.
To do this, find or create a file named dashboard.json
next to the configuration tool:
NOTE: When using the configuration tool by the command line and you’re specifying a configuration file, you must specify an absolute path to it (not a relative path).
✔ Absolute path:
configuration-tool configure --json-file=/absolute/path/here.json
✘ Relative path
configuration-tool configure --json-file=relative/path/here.json
Then edit it using your favourite editor. You’ll need to change tomcat.cookies.secure
, tomcat.cookies.sameSite
and security.trustedHosts
.
If you need embedding to work with Internet Explorer, you must set xFrameOptions
option too. Keep in mind that we do not maintain support for Internet Explorer.
Protocol (http/https) is required for trusted hosts / frame ancestors when using a wildcard.
Step 3 - Getting a token
You can run a working example by following this repository:
In our examples, we’re doing this in the frontend just so you can follow a full example.
However do not do this in production, you’ll be exposing your password!
Typically your backend communicates with the dashboard in order to get the token. Once in place, your frontend then just uses it.
You can use our API to fetch a token for any user. Below we’ve got an example in javascript using jquery:
var url = "https://localhost:8224/" //Change this to your url
var username = "admin" //Change this to the user you want to login as
var password = "dashboard" //Change this to user's password
$.ajax({
// POST to the API with username and password to swap that for a session token
type: "POST",
url: url + apiToken,
contentType: "application/json",
xhrFields: {
withCredentials: true
},
beforeSend: function (xhr, settings) {
xhr.setRequestHeader("Authorization", "basic " + username + ":" + password);
},
success: function (data) {
login(data.token);
}
});
function login(token) {
// Issue an ajax request to embeddedTokenLogin with the token in the header to pick up the session cookies
$.ajax({
type: "GET",
url: url + "pi/auth/embeddedTokenLogin",
contentType: "text/HTML",
xhrFields: {
withCredentials: true
},
beforeSend: function (xhr, settings) {
// This is the header we need to set
xhr.setRequestHeader("Authorization", "bearer " + token);
},
success: function (data) {
// Once we have a succesful ajax request with authentication we can load the charts in the iframes
}
});
}
Step 4 - Embedding
Chart
To embed a chart you’ll need an iframe and the chart’s ID. You can get this by going into the dashboard, clicking chart tools at the top-right of a chart cell and selecting the i
icon:
Then copy the code snippet:
<iframe id="chart_iframe" src='https://your.dashboard.com/pi/chart#chart-filter/481__1' width="500" height="500"></iframe>
You should probably only update/show the iframes once the user has been logged in.
That would change the login code to be more like:
function login(token) {
// Issue an ajax request to embeddedTokenLogin with the token in the header to pick up the session cookies
$.ajax({
type: "GET",
url: url + "pi/auth/embeddedTokenLogin",
contentType: "text/HTML",
xhrFields: {
withCredentials: true
},
beforeSend: function (xhr, settings) {
// This is the header we need to set
xhr.setRequestHeader("Authorization", "bearer " + token);
},
success: function (data) {
document.querySelector("#chart_iframe").src = "https://your.dashboard.com/pi/chart#chart-filter/481__1"
}
});
}
Limiting drill levels on embedded charts
When embedding a chart that contains multiple drill levels, it is possible to use the query parameters “drillStart” and “drillEnd” in the URL to limit the drill levels that a user can see.
When using drillStart, the URL will also need to be configured to include the chart ID and hierarchy position of each chart that is being skipped and the chart at the drillStart level. These must be separated by the !$! delimiter when adding multiple levels. When drilling to the top, the user will return the the level defined as the drillStart parameter.
It is possible to use the parameters individually or together. The parameters are 0 indexed - with the first hierarchy in the chart corresponding to drill level 0. The functionality will also work for dynamic drill conditions.
The below url would allow the user to see drill levels 1, 2 and 3 (i.e the 2nd, 3rd and 4th hierarchy levels) of the chart:
http://localhost:1234/pi/chart/?drillStart=1&drillEnd=3#chart-filter/251__1/d-f-p=251__0/!$!251__1
*Location and syntax of the query parameters.
*Example of identifiers added to view level 1 (2nd hierarchy level ) of the given chart.
Category
We have embedded a chart but what if we want to embed a category? Embedding a category isn’t actually any different from embedding the whole dashboard (yes, you can do that too!).
Below are the different category modes that you can use when embedding the categories:
categoryMode=0
: Normal
This will show the category exactly as it appears on the dashboard.
categoryMode=1
: Headless Single Category
This will hide the category header and the category list.
categoryMode=2
: Single Category
This will show the category header options but will still hide the category list.
categoryMode=3
Headless Normal
This will remove the header but keep the category list.
Interacting with the left & right drawers
One thing to note with the different embed options is that you can actually use JavaScript’s post message API to open the category list / filter panel within the various modes if you wish to (and if they’re available in that category mode).
The left drawer tag will open the category list as long as the embed mode allows for the category list to be active (Options 0 and 3). To interact with it you can use something like below:
targetWindow.postMessage("open-drawer:left");
The right drawer tag will open the filter panel for every embed mode available. Similarly, you can open it using the same syntax as above:
targetWindow.postMessage("open-drawer:right");
The right drawer panel can be opened via a query parameter too: enableDynamicFilterPanel=true
. This lets the dashboard know that the category should load with the Category Object Filters list open
The full range of messages the dashboard interprets can be found in our API Embed Examples and they include opening
, closing
, pinning
and unpinning
drawers.
Using filters to filter a chart via JavaScript while embedded
With Category Filters (whole category)
Adding filters
The dashboard supports embedding with default category filters selected.
Refer to Embedded Categories for details. If changing the filter values outside of the dashboard is needed, we will need to implement functions to be called outside of an iframe, so that filter values are supplied to the dashboard elegantly.
The current structure of the URL for category filters
IMPORTANT: This is just for information. We don’t recommend editing the URL directly to change filter values, because it’s not a reliable approach and the implementation may break when upgrading the dashboard
Category-level filters are applied to all the charts in the category, it currently has such a pattern pattern:
scheme://host:port/pi/#PA-CA###category/id__level/c-o-p=FILTERS
For example:
This category has filters Department = Dev
:
https://localhost:8224/pi/#PA-CA###category/141__0/c-o-p=eq__[[Department]]{{dev}}
And this one has Department = Dev
and Number > 100
:
https://localhost:8224/pi/#PA-CA###category/141__0/c-o-p=eq__[[Department]]{{dev}}/*$*gt__[[Number]]{{100}}
With Temporary Filters (per chart)
Current URL Structure
IMPORTANT: This is just for information. We don’t recommend editing the URL directly to change filter values, because it’s not a reliable approach and the implementation may break when upgrading the dashboard
Temporary filters have something with the following pattern:
scheme://host:port/pi/chart#chart-filter/id/t-f-p=FILTERS
For example:
Filtering the chart with Department = Dev
:
https://localhost:8224/pi/chart#chart-filter/330__1/t-f-p=eq__[[Department]]{{dev}}
Filtering the chart with Department = Dev
and Number > 100
:
https://localhost:8224/pi/chart#chart-filter/330__1/t-f-p=eq__[[Department]]{{dev}}/*$*gt__[[Number]]{{100}}
Types of filters
IMPORTANT: This is just for information. We don’t recommend editing the URL directly to change filter values, because it’s not a reliable approach and the implementation may break when upgrading the dashboard
Filter Name | Pattern | Object Types | Examples |
---|---|---|---|
Equals |
| Dimension |
|
Measure |
| ||
Greater than |
| Dimension |
|
Measure |
| ||
Lower than |
| Dimension |
|
Measure |
| ||
Greater or equals |
| Dimension |
|
Measure |
| ||
Lower or equals |
| Dimension |
|
Measure |
| ||
Not equals |
| Dimension |
|
Measure |
| ||
Like |
| Dimension |
|
| Measure* |
| |
Not Like |
| Dimension |
|
| Measure* |
| |
Top |
| Dimension |
|
Measure |
| ||
Bottom |
| Dimension |
|
Measure |
| ||
In |
| Dimension |
|
Not In |
| Dimension |
|
Between |
| Measure |
|
Not Between |
| Measure |
|
*May not work for some databases
Upper/lower case values make a difference!
Delimiters
/*$*
- Used to separate multiple criteria items
::::
- Used to separate values within {{}}
, used for Between/Not Between and In/Not In list criteria
Limiting Drill Levels On Embedded Categories
Limiting Drill Levels for Embedded Categories (Drill Restriction) operates almost identically to using it for a single chart embed, so refer to the documentation above (link) as an initially summary of how the feature works. The only real differences are the interaction of the feature with multiple drillable charts being present on the same Category and a few necessary limitations placed upon users when in a restricted context. Details about this and a more in-depth guide to modifying your URL for use with this feature can be found below:
The interactions of multiple drillable charts within a Category
The ‘drillStart’ and ‘drillEnd’ parameters will apply to all of the drillable charts in a Category - these values can not be chart specific
The initial drill level of each chart can differ as it depends purely upon the number of placeholder filters in the URL it does not have to match the level defined by ‘drillStart’; however, it can not start at a level below that defined by 'drillStart' e.g. if 'drillStart' is set to 1 you cannot have a drillable chart that has no placeholder filters and therefore starts at drill level 0
Similarly, you cannot have a drillable chart with a number of placeholder filters that would make it start at a drill level beyond that defined by ‘drillEnd’
Necessary limitations on possible User interactions
When one or more Drill Restriction parameter is in the URL the user will be unable to do the following actions as they would either break other categories or allow them to bypass the restrictions:
Use the “Reset to the default view of the Category button”
Navigate to a different Category via the Category list
Drill to a Category
Guide to modifying your URL
In this example, I will be taking a Category with two drillable charts and adding preventing people from drilling to the first level of the chart (via ‘drillStart=1’) or from drilling down beyond the 3rd level ('drillEnd=3'). I will also be setting up the URL to make the first chart start at drill level 1 but the second chart start at drill level 2.
The best way to begin is by navigating to the Category and drilling down to the levels that you wish to be presented on initial load. In my example, this would be drilling down once on the first chart and twice on the second one. The typical use case for this feature would require the ‘skipped’ levels to not be applying any filters. However, you could leave these filters in place if that satisfies your use case and it is this ‘without placeholders’ approach we will look at first as it is the simpler approach:
Without placeholder filters i.e. you want to retain the specific filtering of the ‘skipped’ levels
If you do not need to remove the filtering done by the drill levels prior to ‘drillStart’, The modification of the URL is as simple as adding the required ‘drillStart’ or ‘drillEnd’ parameters. These are query parameters that must appear prior to the ‘#PA-CA’ part of the Category URL. Query parameters are separated by ‘&' with the entire query parameter ‘block’ being initialised with ‘?’. If your URL already contains a ‘?’ you likely already have a query parameter and you would simply need to add the ‘drillStart’ and/or ‘drillEnd’ to it - using my example and a pre-existing ?lang=en_GB
query parameter that section of the URL would look like this: ?lang=en_GB&drillStart=1&drillEnd=3
. If there wasn’t a pre-existing Query parameter it would instead look like this: ?drillStart=1&drillEnd=3
.
With those query parameters in place, both drillable charts of this Category would be unable to drill up above level 1 (this includes when using drill to top) or down beyond level 3.
With placeholder filters i.e. you do not want any filtering applied from the ‘skipped’ levels
Due to the complexity of the URL, this can seem more complicated than it actually is but in effect all you have to do is remove the condition part of any drill filters that have been applied. Here is a worked example starting from the URL of the Dashboard after I interacted with the desired Category by drilling once on the first chart and twice on the second:
http://localhost:8080/pi/?lang=en_GB#PA-CA%23%23%23category%2F27%2Fc-o-p%3D26__0%2F!%24!27__0%23CA-CH%23%23chart-filter%2F171__c7817d6a-c13c-49f5-8499-d65048295844%2Fd-f-p%3D171__0%2F*%24*in__[[Alignment]]{{Good}}%2F!%24!171__1%23CH-CH%23chart-filter%2F170__e553c878-4594-4a83-9074-671e0a36922d%2Fd-f-p%3D170__0%2F*%24*eq__[[Alignment]]{{Neutral}}%2F!%24!170__1%2F*%24*eq__[[Gender]]{{Male}}%2F!%24!170__2
I need to remove all of the specific drill criteria including a separating forward slash (%2F) in the URL (red) in the section starting with “d-f-p” (blue) of each chart. This results in the following URL:
http://localhost:8080/pi/?lang=en_GB#PA-CA%23%23%23category%2F27%2Fc-o-p%3D26__0%2F!%24!27__0%23CA-CH%23%23chart-filter%2F171__c7817d6a-c13c-49f5-8499-d65048295844%2Fd-f-p%3D171__0%2F!%24!171__1%23CH-CH%23chart-filter%2F170__e553c878-4594-4a83-9074-671e0a36922d%2Fd-f-p%3D170__0%2F!%24!170__1%2F!%24!170__2
I would then add the ‘drillStart’ and ‘drillEnd’ parameters (highlighted in green) resulting in this URL:
http://localhost:8080/pi/?lang=en_GB&drillStart=1&drillEnd=3#PA-CA%23%23%23category%2F27%2Fc-o-p%3D26__0%2F!%24!27__0%23CA-CH%23%23chart-filter%2F171__c7817d6a-c13c-49f5-8499-d65048295844%2Fd-f-p%3D171__0%2F!%24!171__1%23CH-CH%23chart-filter%2F170__e553c878-4594-4a83-9074-671e0a36922d%2Fd-f-p%3D170__0%2F!%24!170__1%2F!%24!170__2
Combining this transformation with the other applicable aspects of embedding a Category detailed in this document will result in an Embedded Category with Drill Restrictions in place.
Gotchas
CORS errors?
If you see an error in the Javascript Console such as:
Access to XMLHttpRequest at 'https://dashboard-host/' from origin 'https://your-app-host/' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Then you need to review your frame-ancestors settings (via the config tool or via the config file).
If everything still looks correct, then make sure you’re not using any browser addon that attempts to bypass CORS.
Login screen keeps showing / looping after logging in?
Check your cookie settings
This usually means your cookie settings aren’t set up correctly. Review your cookie settings (via the config tool or via the config file).
Is your dashboard behind a proxy?
If the cookie settings look correct, it might be that your dashboard is being served via http
and the proxy in front of it will be doing the ssl
communication. If that's the case, you'll need to tell the dashboard that it's behind a proxy and that the proxy is doing https
. This is done by editing your proxy settings in one of three ways.
Using the configuration tool
Editing the configuration file
Edit dashboard.json
configuration file:
{
"proxy": {
"enabled": true,
"scheme": "https",
"secure": true,
"host": "proxy-host.com",
"port": 443
}
}
Using environment variables
PI_PROXY_ENABLED=true
PI_PROXY_SCHEME=https
PI_PROXY_IS_SECURE=true
PI_PROXY_HOST="proxy-host.com"
PI_PROXY_PORT=443
Is your dashboard a try-pi or an AMI?
This is the same scenario as if having the dashboard behind a proxy.
In settings.sh
or in startup.sh
, set the following environment variables:
export PI_PROXY_ENABLED=true
export PI_PROXY_SCHEME=https
export PI_PROXY_IS_SECURE=true
export PI_PROXY_HOST="<domain url>"
export PI_PROXY_PORT=443
export PI_TOMCAT_COOKIE_SECURE= true
export PI_TOMCAT_FRAME_ANCESTORS=<URL of client site>
export PI_TOMCAT_COOKIE_SAMESITE=none
Then make sure you reboot your instance.
Check the URL being embedded
We’ve noticed the cookie doesn’t get recognised if there’s a double-slash in the URL of the iframe
.
For example: https://dashboard//pi/
or https://dashboard/pi//
instead of https://dashboard/pi/
Embedded Sub Domains
If you have subdomains, the hostname in the custom trusted hosts will need to be entered as *.hostname *.hostname
CUSTOMER NEWS - Our August 24 Release Is Now Available - Download It Now!