T
his week I’m blogging from Knowledge13 in Las Vegas! So far it’s been a fantastic conference with lots of great content and it’s been fun to meet with so many people interested in improving their ServiceNow implementation. I’m really looking forward to the Fred Luddy keynote this morning. Jacob Andersen and I are here representing our ServiceNow consulting company, Crossfuze Solutions and would love to talk with any of you who could benefit from working with the most experienced ServiceNow implementation and integration consultants in the industry. Stop by the BDNA booth (Booth 1216) during any of the expo times to visit with us and see some of our world-class Incident, Problem, Knowledge, Change, Catalog, CMS, and Asset Management solutions! I’ll also be participating in a sold out panel presentation at 1:40 today where we’ll discuss the importance of data quality in a ServiceNow implementation.
Okay, enough of the high-pressure sales pitch :). This morning I saw a post on the ServiceNow community asking how to display a google map based on a ServiceNow location record. I think this is a fantastic idea and I decided to see if I could come up with a couple of solutions for it this morning. In this post I’ll show you how you can set up a UI Action or UI Macro link to pop open a Google Map window based on any location record in your ServiceNow instance!
The simplest way to set up a Google Map link in ServiceNow is to use a client-side UI Action to construct the URL and pop open a new window. Google Map URLs follow a basic convention that can include the street address or latitude/longitude coordinates of the location you want to display like this…
http://maps.google.com/?q=1200 Pennsylvania Ave SE, Washington, District of Columbia, 20003
Here’s the code and configuration for a UI Action…
Name: Open Google Map
Table: Location [cmn_location]
Action name: open_google_map
Show insert: false
Client: true
Form link/List context menu: true
OnClick: openGoogleMap()
Condition: (!current.street.nil() && !current.city.nil()) || (!current.latitude.nil() && !current.longitude.nil())
Comments: Shows a google map icon on a location field if the location has a street and city listed.
Crossfuze Solutions
www.crossfuze.com
Script:
//URL should follow this format...http://maps.google.com/?q=1200 Pennsylvania Ave SE, Washington, District of Columbia, 20003
function openGoogleMap() {
//Retrieve the 'Location' record
var sysId = typeof rowSysId == 'undefined' ? gel('sys_uniqueValue').value : rowSysId;
var gr = new GlideRecord('cmn_location');
gr.get(sysId);
//Create and display the Map URL
var mapURL = "http://maps.google.com/?q=";
//Try location and city
if(gr.street && gr.city){
mapURL = mapURL + gr.street + ',' + gr.city + ',' + gr.state + ',' + gr.zip + ',' + gr.country;
}
//Else try latitude and longitude
else if(gr.latitude && gr.longitude){
mapURL = mapURL + gr.latitude + ',' + gr.longitude;
}
//Strip '#' symbols to avoid encoding errors
mapURL = mapURL.replace(/#/g, "");
window.open(mapURL);
}
Once the UI Action is created, you should be able to right-click a location record from a list or click the ‘Open Google Map’ link as shown here…
While it’s nice to be able to open a map from a location record, it’s probably even more useful to be able to open a map directly from a referenced location record. The code for the UI Macro is a bit more complex, but once created it’s very simple to include for any reference field in your system by personalizing the dictionary entry for the location reference field (or the ‘cmn_location’ table dictionary entry if you want to include the option on every location field in your system). You can create the UI Macro using these settings.
Name: open_google_map
Description:Shows a google map icon on a location field if the location has a street and city listed.
Activate by adding the attribute: ref_contributions=open_google_map to a location reference field
Crossfuze Solutions
www.crossfuze.com
Script:
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<g:evaluate var="jvar_guid" expression="gs.generateGUID(this);" />
<j:set var="jvar_n" value="open_google_map${jvar_guid}:${ref}"/>
<g2:evaluate var="jvar_show_google_map_display" jelly="true">
var id = __ref__.getSysIdValue();
if (id == null)
"none";
else {
var loc = new GlideRecord('cmn_location');
loc.get(id);
if ((!loc.street.nil() $[AND] !loc.city.nil()) || (!loc.latitude.nil() $[AND] !loc.longitude.nil()))
"";
else
"none";
}
</g2:evaluate>
<a id="${jvar_n}"
onclick="openGoogleMap('${ref}')"
name="${jvar_n}"
style="display:$[jvar_show_google_map_display]"
title="${gs.getMessage('Open a Google Map for this location')}">
<img border="0" src="https://maps.gstatic.com/favicon2.ico" height="16" width="16"/> </a>
<script>
needsRefreshLoc = false;
function onChange_location_show_google_map(element, original, changed, loading) {
var s = '${ref}'.split('.');
var referenceField = s[1];
if (needsRefreshLoc == false) {
needsRefreshLoc = true;
return;
}
if (changed.length == 0) {
$('${jvar_n}').hide();
return;
}
var locRec = g_form.getReference(referenceField, locationGoogleMapReturn);
}
function locationGoogleMapReturn(locRec) {
var e = $('${jvar_n}');
if ((locRec.street $[AND] locRec.city) || (locRec.latitude $[AND] locRec.longitude))
e.show();
else
e.hide();
}
//Event.observe(g_form.getControl(${ref}.split('.')[1]), 'change', onChange_cmn_location_show_google_map);
var l = new GlideEventHandler('onChange_location_show_google_map', onChange_location_show_google_map, '${ref}');
g_event_handlers.push(l);
//Pop open google maps window of location specified
//URL should follow this format...http://maps.google.com/?q=1200 Pennsylvania Ave SE, Washington, District of Columbia, 20003
function openGoogleMap(reference) {
var s = reference.split('.');
var referenceField = reference.substring(s[0].length+1);
var sysId = g_form.getValue(referenceField);
//Retrieve the 'Location' record
var gr = new GlideRecord('cmn_location');
gr.get(sysId);
//Create and display the Map URL
var mapURL = "http://maps.google.com/?q=";
//Try location and city
if(gr.street $[AND] gr.city){
mapURL = mapURL + gr.street + ',' + gr.city + ',' + gr.state + ',' + gr.zip + ',' + gr.country;
}
//Else try latitude and longitude
else if(gr.latitude $[AND] gr.longitude){
mapURL = mapURL + gr.latitude + ',' + gr.longitude;
}
//Strip '#' symbols to avoid encoding errors
mapURL = mapURL.replace(/#/g, "");
window.open(mapURL);
}
</script>
</j:jelly>
Once the UI Macro has been created and the ‘ref_contributions’ attribute added to the reference field or table dictionary, you should see a google map icon displayed next to your location field whenever a location is referenced that includes address or latitude/longitude details.
I’ve made the UI Action work. Working on a modification of the UI Macro to not use reference field, but to appear at end of name field on same record which contains latitude and longitude. Does current UI Macro work as hover over? -Bill
P.S. Can’t believe I’m the first post? Awesome usefulness! Thank you.
Thanks Bill. The current macro doesn’t support the ability to hover and display the popup. Unfortunately, ServiceNow doesn’t really provide any capability to do hover popups for anything other than their own records, KB articles, and catalog items currently. I’ve looked into some CSS hacks to do it a time or two, but haven’t been able to get to a solution I was happy with.
Hi Mark,
Thanks for this script! I noticed a bug though, when using it on a field that has been dot walked to from another form. The fix is below
Change this line:
var referenceField = s[1];
To
var referenceField = reference.substring(s[0].length+1);
A dot walked field has more than 2 elements when split, so only looking at the second one won’t work. The new line just removes element 0 and the “.”
I hope that makes sense. Thank you again. I have learnt a lot from this script.
Howard
Good catch! I’ve updated the UI macro code above with your fix. Thanks!
Hi,
Thanks for the article. I’m struggling to get the UI Macro to appear on my records (such as Incident) where Location Name is pulled from the Location table (cmn_location)
The UI action works perfectly – when viewing on the location table itself however the UI macro just won’t appear for me :'(
I’m relatively new to SN but I’m confident I’ve done exactly as described above. Any guesses on why the UI Macro isn’t appearing? (FYI my locations have Street / City / Long / Latt populated)
I’m not sure what might be happening, but I’ve just added the configuration to the ‘Location’ field on the incident form at demo022.service-now.com. The only recommendation I can give is to go there and compare your UI macro and reference field attributes to what I’ve added there.
Hello Mark,
It seems that the Google Maps icon at https://plus.google.com/_/favicon?domain=maps.google.com has recently doubled size to 32x32px , probably to support Retina screens.
So the line:
should be updated to:
Alternatively the icon should be uploaded as an image to ServiceNow and linked from there (although then the icon is not updated when Google decides to change it).
Cheers,
David
Thanks for the heads up! There are probably a couple of different ways to approach this. It looks like part of your comment is missing, but I’ve updated the UI macro code above to force the width and height on the image.
Yes, sorry, I typed HTML code and I forgot to “escape” it.
I was proposing to do exactly what you did, add width=”16″ and height=”16″ 🙂
That way the image looks nice in normal screen and even nicer on retina screens 😉
Cheers,
David
Mark,
I’ve been scouring the Wiki and Forums for methods similar to this. Do you have any insight on how to implement a location map for buildings and offices? I’ve heard mention of it from Knowledge11 and Knowledge12, but don’t see any documented ways to do implement. The Google feature is great for multiple geographic locations, but would love this ability at a local site.
Thanks!
Jeremy
@Jeremy, Unfortunately I don’t have a good answer for you at this point although I may be investigating that soon for a client I’m working with. As is the case with most integrations, the challenge is really in finding another tool that can show the floorplan along with specific coordinates for offices, etc. Once you have that, it shouldn’t be that much different from what I’m doing here with google maps.
Great write-up! I have a question, sort of un-related. I see in the screenshot there is a phone icon to dial the user’s phone number directly from the incident form. This is exactly what we are looking to implement now, but I haven’t found any information about this. Where can I find information no how to accomplish this?
The icon in the screenshot is actually only used to toggle the display of an ‘Alternate contact number’ field for the user. There’s no native support for dialing a phone number in ServiceNow, but they did recently add a ‘Notify’ plugin that integrates with Twilio that should be able to dial a user. That lacks the convenient icon on the user field but it could all be built. Here’s a link to the SN ‘Notify’ plugin. Please feel free to contact Crossfuze if you need consulting assistance!
http://wiki.servicenow.com/index.php?title=Notify
This works great! Thanks for sharing!
You’re welcome!
Really nice!
And I also wanted to suggest updating the icon as it’s the blue Google icon right now (https://plus.google.com/_/favicon?domain=maps.google.com), and not the Maps icon: http://maps.gstatic.com/favicon2.ico.
I had the same problem as Mike Herron with the related link not showing up on the record page. But in the UI Action “Open Google Map” I added it to a view under “UI Action Visability”. I think I tried the Asset or Default view. Then it worked for that view. When I removed this it worked for all views. I tried this on Eureka.
Thanks Martin! Google keeps changing their favicon location, hopefully the one you gave me will last a bit longer. 🙂
Does this works on Fuji also because i tried and it is not working ???
Anyone able to do this on fuji ??
It should, but there might be some small changes to be made depending on your setup. This UI macro code works great in the Fuji instance I’ve been working in.
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<g:evaluate var="jvar_guid" expression="gs.generateGUID(this);" />
<j:set var="jvar_n" value="open_google_map${jvar_guid}:${ref}"/>
<g2:evaluate var="jvar_show_google_map_display" jelly="true">
var id = __ref__.getSysIdValue();
if (id == null)
"none";
else {
var loc = new GlideRecord('cmn_location');
loc.get(id);
if ((!loc.street.nil() $[AND] !loc.city.nil()) || (!loc.latitude.nil() $[AND] !loc.longitude.nil()))
"";
else
"none";
}
</g2:evaluate>
<a>
<span id="${jvar_n}" onclick="openGoogleMap('${ref}')"
class="btn btn-default"
title="${gs.getMessage('Open a Google Map for this location')}"
alt="${gs.getMessage('Open a Google Map for this location')}"
style="display:$[jvar_show_google_map_display]; margin-right: 5px">
<img border="0" src="https://maps.gstatic.com/favicon2.ico" height="13" width="13"/>
</span>
</a>
<script>
needsRefreshLoc = false;
function onChange_location_show_google_map(element, original, changed, loading) {
var s = '${ref}'.split('.');
var referenceField = s[1];
if (needsRefreshLoc == false) {
needsRefreshLoc = true;
return;
}
if (changed.length == 0) {
$('${jvar_n}').hide();
return;
}
var locRec = g_form.getReference(referenceField, locationGoogleMapReturn);
}
function locationGoogleMapReturn(locRec) {
var e = $('${jvar_n}');
if ((locRec.street $[AND] locRec.city) || (locRec.latitude $[AND] locRec.longitude))
e.show();
else
e.hide();
}
//Event.observe(g_form.getControl(${ref}.split('.')[1]), 'change', onChange_cmn_location_show_google_map);
var l = new GlideEventHandler('onChange_location_show_google_map', onChange_location_show_google_map, '${ref}');
g_event_handlers.push(l);
//Pop open google maps window of location specified
//URL should follow this format...http://maps.google.com/?q=1200 Pennsylvania Ave SE, Washington, District of Columbia, 20003
function openGoogleMap(reference) {
var s = reference.split('.');
var referenceField = reference.substring(s[0].length+1);
var sysId = g_form.getValue(referenceField);
//Retrieve the 'Location' record
var gr = new GlideRecord('cmn_location');
gr.get(sysId);
//Create and display the Map URL
var mapURL = "http://maps.google.com/?q=";
//Try location and city
if(gr.street $[AND] gr.city){
mapURL = mapURL + gr.street + ',' + gr.city + ',' + gr.state + ',' + gr.zip + ',' + gr.country;
}
//Else try latitude and longitude
else if(gr.latitude $[AND] gr.longitude){
mapURL = mapURL + gr.latitude + ',' + gr.longitude;
}
//Strip '#' symbols to avoid encoding errors
mapURL = mapURL.replace(/#/g, "");
window.open(mapURL);
}
</script>
</j:jelly>
Thanks again Mark for sharing this solution. I don’t think I could have solved this one alone. Going to have to read into GlideEventHandler and how that works, seems useful.
I found a community article asking a similar question so I shared your solution (with a credit/link to snguru):
https://community.servicenow.com/message/858503
We figured out how to modify the ui macro image in Fuji, but it wouldn’t go away if there was no value in the reference field. Can you explain why this extra code is necessary (in terms of hiding the icon when no value is in the field).
Thanks! The UI macro code itself is all rendered and executed at the same time the form loads. As such, it can evaluate the current value of the field when the form loads and display the macro accordingly. If you want to change the macro or take other actions as the field changes then you need to include the appropriate client-side code (included in script tags). The ‘Configuration item’ reference field macro is a good example of doing this out-of-box.
So I have a user reference field that I am applying the same solution to two different macros. It seems that the first macro in the ref_contributions list has some strange behavior. When it’s a new form, it shows. When I clear the value, it works as expected, but when I add a new value, the first macro does not reappear, though the second does. Any ideas?
It’s probably a naming conflict. Because you’ve got 2 macros using similar code on the same form, you need to make sure all of the functions and variables in your script have unique names. Otherwise they will conflict with each other and you’ll end up with unpredictable results.
It is not working on mobile app
Hi Mark,
Thanks for script!.. I want to Open Google Map from a Location Record in Mobile device. I want to implement same functionality on the mobile device. Any Help will be Appreciated.
You’re welcome. The problem here is that UI macros of any sort are not supported in the mobile app. I’m not sure what to do there other than suggest you request that ServiceNow supports UI macros (or something similar to reference field decorations) in the mobile interface.