Modify the Service Portal Report Widget to open records in the /sp/?id=ticket way
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎01-12-2018 06:47 AM
We're going to be upgrading to Jakarta in a couple weeks, which includes the new "Report" widget for the Service Portal that has people excited. However, in my sandbox instance (which is running Kingston Patch 1), when I open a record or list from a report on the Portal, it loads it in the UI16 view, rather than the Service Portal.
For example, I have a Calendar report on the Service Portal that shows a list of all Change Requests. When I select a Change record from the calendar, it loads it the record with the following URL: ...service-now.com/change_request.do?sys_id=...
What I would like it to do is load the record like it does when I select one of my tickets from the Header Menu -> Requests (URL: ...service-now.com/sp/?id=ticket&table=change_request&sys_id=...). It could even load the form view rather than the ticket view (id=form).
The reason for this is our end users are always redirected to the Service Portal and don't have access to the UI16 view. However, when they select a record from a report, they're redirected to the UI16 view of the ticket, complete with context menu and UI actions. This would provide them with unnecessary access and complication.
Any help/insight would be greatly appreciated.
- Labels:
-
Reporting
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎01-12-2018 09:01 AM
Hi David,
I've had a look at the Report widget and it doesn't look as though you can change the functionality without fully re-writing it, the functionality all comes from JS includes scripts which cannot be changed.
Kind regards
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎05-17-2018 03:17 AM
Challenge accepted...
We had the same issue - it looks really unprofessional when a user is on a nice-looking service portal page and clicks through to a legacy view of the tickets.
As previously stated, the charts are all built in JS includes, more specifically 'GlideV2ChartingIncludesSP.js' included in the 'ReportingIncludes' dependency. Initially I had a go at cloning the report widget and cloning the dependency and rewriting that but it was tough to figure out how it was all put together and I wasn't really making any useful progress.
The next question to ask was "Could we update the clickthrough functionality after the report had loaded?". The short answer is yes. You may be aware that most charts in ServiceNow are rendered using Highcharts (JavaScript library), so my next step was to look at the Highcharts documentation. If you go into your browser console (chrome is best) on a Service Portal page containing the report widget and type:
Highcharts.charts
You will see an array of the charts present on the page. If you take the first chart appearing on the page as an example, it will be the first member of that array, which you can access as follows:
Highcharts.charts[0]
This will then return an object containing all of that particular chart's properties. But where is the 'onclick' function stored? To find this you have to dig a little deeper. Each chart contains a number of series of data. In some cases this may just be one series, but for example in a stacked bar chart, there are multiple. Within each chart object, there is a property 'series' which is again, an array containing each series. So assuming we have a simple chart containing one series of data, we'd access this as follows:
Highcharts.charts[0].series[0]
For a bar chart, you can find the onclick function in the 'hcEvents' property:
Highcharts.charts[0].series[0].hcEvents.click
So to overwrite it you simply set the click function equal to your own custom function right? Not quite - for bar charts (and some other chart types), you need to set it to an array containing your function. I do not know why.
// Bar chart
Highcharts.charts[0].series[0].hcEvents.click = [
function(event) {
// Do something (Have a look at the contents of the event, it contains useful information such as the record query which will be useful in building a URL)
console.log(event)
}
];
In the case of pie charts, I found that you are expected to assign the function directly (not as an array) and also that the click function is stored somewhere else (of course).
// Pie chart
Highcharts.charts[0].series[0].options.point.events.click = function(event) {
// Do someting
}
So we have managed to overwrite the onclick function in the browser console, but how on earth do we do that in the service portal? I'm glad you asked. The first thing to do is clone the report widget. Once we have cloned the widget, we will need to add some code to the client controller to essentially do what we just did in the browser console.
Highcharts.charts.forEach(function(c) {
// Only update current report
if (c.userOptions.report_id == reportId) {
// Pie chart
if (c.types.indexOf('pie') != -1) {
c.series.forEach(function(s) {
s.options.point.events.click = function(event) {
// history.pushState stops the full browser window reloading, you could use 'window.location =' instead
history.pushState(null, null, '?id=list&table=' + event.point.table + '&filter=' + event.point.click_url_info);
};
});
// Bar chart
} else if (c.types.indexOf('column') != -1) {
c.series.forEach(function(s) {
s.hcEvents.click = [
function(event) {
history.pushState(null, null, '?id=list&table=' + event.point.table + '&filter=' + event.point.click_url_info);
}
];
});
}
}
});
I've only included bar and pie charts because that was our use case but it could be expanded to include more chart types (depending on where the click function is stored). So this works right and there's nothing else you need to do? Of course not, don't be silly. This code won't run until the charts are loaded on the form. If the charts aren't loaded and the code runs, the old onclick function still applies. I initially tried using an angular $timeout which seemed to solve the problem, but sometimes the reports can take a while to load. You can't predict how long this will take so I thought we were SOL. I ended up using an angular $interval to check every 100ms to see if the charts have loaded and only when they have loaded, update the onclick function. I was surprised at how well this works.
// Wait for report to be loaded before updating onclick
// Use $interval to search for chart of current report until it is found, then overwrite the onclick function
var inter = $interval(function() {
var chartContainers = document.getElementsByClassName('chart-container');
for (var i = 0; i < chartContainers.length; i++) {
if (chartContainers[i].id.indexOf(options.report_id) != -1) {
// When found cancel the interval
$interval.cancel(inter);
// Update the onclick link
c.updateOnClick();
}
}
}, 100);
c.updateOnClick = function() {
Highcharts.charts.forEach(function(c) {
// Only update current report
if (c.userOptions.report_id == options.report_id) {
// Pie chart
if (c.types.indexOf('pie') != -1) {
c.series.forEach(function(s) {
s.options.point.events.click = function(event) {
// history.pushState stops the full browser window reloading, you could use 'window.location =' instead
history.pushState(null, null, '?id=list&table=' + event.point.table + '&filter=' + event.point.click_url_info);
};
});
// Bar chart
} else if (c.types.indexOf('column') != -1) {
c.series.forEach(function(s) {
s.hcEvents.click = [
function(event) {
history.pushState(null, null, '?id=list&table=' + event.point.table + '&filter=' + event.point.click_url_info);
}
];
});
}
}
});
}
I hope this is clear - I will happily clarify anything but have a play around yourself, add your own logs to see what data you have available in the console but hopefully my sacrifice will save someone else the same headache

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎05-24-2018 05:05 AM
Hi Jack,
As per your code pie chart is working properly but for calender report above code is not working.
Any help would be greatly appreciated.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎05-25-2018 07:43 AM
For calendar reports the solution should be similar, only that the report calendars use the library 'FullCalendar' as opposed to Highcharts. The documentation for this library can be found here. I will have a play and see if I can figure it out when I get a chance. I've used FullCalendar before and defined custom onclick functions but that was at the time of creation, not after it has loaded which is what you need to do here.