- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
10-28-2023 12:27 AM
Greetings, ServiceNow Community.
I am facing a challenge in GooglePickerAPI on ServiceNow UI Page. Well, the code renders but the popUP for auth., won't come. But when i save the code as html doc n run on my liveServer then it is all fine. I do not know where am wrong. Any advise?
UIPageCodeBelow
<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<html>
<head>
<title>Picker API Quickstart</title>
<meta charset="utf-8" />
</head>
<body>
<p>Picker API API Quickstart</p>
<!--Add buttons to initiate auth sequence and sign out-->
<button id="authorize_button" onclick="handleAuthClick()">Authorize</button>
<button id="signout_button" onclick="handleSignoutClick()">Sign Out</button>
<pre id="content" style="white-space: pre-wrap;"></pre>
<div id="pickerDiv" class="picker-dialog"
style="height: 500px; width:800px; border: 1px solid red; position: relative; top: 50%;"></div>
<script type="text/javascript">
const SCOPES = 'https://www.googleapis.com/auth/drive.metadata.readonly';
const CLIENT_ID = // clientID
const API_KEY = // apiKey
const APP_ID = // projectNumber
let tokenClient;
let accessToken = null;
let pickerInited = false;
let gisInited = false;
document.getElementById('authorize_button').style.visibility = 'hidden';
function gapiLoaded() {
gapi.load('picker', intializePicker);
}
function intializePicker() {
pickerInited = true;
maybeEnableButtons();
}
function gisLoaded() {
tokenClient = google.accounts.oauth2.initTokenClient({
client_id: CLIENT_ID,
scope: SCOPES,
callback: '', // defined later
});
gisInited = true;
maybeEnableButtons();
}
function maybeEnableButtons() {
if (pickerInited & gisInited) {
document.getElementById('authorize_button').style.visibility = 'visible';
}
}
function handleAuthClick() {
tokenClient.callback = async (response) => {
if (response.error !== undefined) {
throw (response);
}
accessToken = response.access_token;
document.getElementById('signout_button').style.visibility = 'visible';
document.getElementById('authorize_button').innerText = 'Refresh';
await createPicker();
};
if (accessToken === null) {
tokenClient.requestAccessToken({ prompt: 'consent' });
} else {
tokenClient.requestAccessToken({ prompt: '' });
}
}
function createPicker() {
const view = new google.picker.View(google.picker.ViewId.DOCS);
view.setMimeTypes("application/pdf,image/png,image/jpeg,image/jpg,image/gif,image/svg+xml,application/vnd.ms-excel,application/vnd.google-apps.spreadsheet,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/msword,application/vnd.google-apps.document,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation,video/x-flv,video/mp4,video/quicktime,video/mpeg,video/x-matroska,video/x-ms-asf,video/x-ms-wmv,video/avi,audio/mpeg,audio/wav,audio/ac3,audio/aac,audio/ogg,audio/oga,audio/x-m4a,application/zip")
const picker = new google.picker.PickerBuilder()
.enableFeature(google.picker.Feature.NAV_HIDDEN)
.enableFeature(google.picker.Feature.MULTISELECT_ENABLED)
.setDeveloperKey(API_KEY)
.setAppId(APP_ID)
.setOAuthToken(accessToken)
.addView(view)
.addView(new google.picker.DocsUploadView())
.setCallback(pickerCallback)
picker.build();
const myDiv = document.getElementById('pickerDiv');
const iframe = document.createElement('iframe');
iframe.setAttribute("src", picker.toUri());
iframe.style.width = "100%";
iframe.style.height = "100%";
myDiv.appendChild(iframe)
}
function pickerCallback(data) {
if (data.action === google.picker.Action.PICKED) {
document.getElementById('content').innerText = JSON.stringify(data, null, 2);
}
}
</script>
<script async='true' src="https://apis.google.com/js/api.js" onload="gapiLoaded()"></script>
<script async='true' src="https://accounts.google.com/gsi/client" onload="gisLoaded()"></script>
<style>
.picker-dialog {
border: 1px solid red;
}
</style>
</body>
</html>
</j:jelly>
Many thanks,
Kingstan.
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
10-28-2023 07:26 AM
To provide an example, this is how I would do it:
- create a record in table content_css, where field Style contains:
.x-content {
white-space: pre-wrap;
}
.x-picker-dialog {
border: 1px solid red;
height: 500px;
margin: auto;
position: relative;
top: 50%;
width: 800px;
}
- create record in table sys_ui_script (UI Script) where field API Name contains googleLibrary and field Script contains the script originally in field HTML of the UI Page:
const SCOPES = 'https://www.googleapis.com/auth/drive.metadata.readonly';
const CLIENT_ID = '1';
const API_KEY = '1';
const APP_ID = '1';
...
function pickerCallback (data) {
if (data.action === google.picker.Action.PICKED) {
document.getElementById('content').innerText = JSON.stringify(data, null, 2);
}
}
- modify the UI Page's field HTML to contain:
<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<html>
<head>
<!-- use external style sheet (sys_id e8fba397978a3150594ffbc71153af5d is that of a record in table content_css) -->
<link href="e8fba397978a3150594ffbc71153af5d.cssdbx?v=1.0" rel="stylesheet" />
<title>${HTML: gs.getMessage('Picker API Quickstart') }</title>
<meta charset="utf-8" />
</head>
<body>
<!-- use external script (moniker googleLibrary is field name of the record in table sys_ui_script that contains the script -->
<script src="googleLibrary.jsdbx?v=1.0" type="text/javascript"></script>
<p>${HTML: gs.getMessage('Picker API API Quickstart') }</p>
<!-- Add buttons to initiate auth sequence and sign out -->
<button id="authorize_button" onclick="handleAuthClick()">${HTML: gs.getMessage('Authorize') }</button>
<button id="signout_button" onclick="handleSignoutClick()">${HTML: gs.getMessage('Sign Out') }</button>
<pre id="content" class="x-content"></pre>
<div id="pickerDiv" class="x-picker-dialog"></div>
<script async="true" onload="gapiLoaded()" src="https://apis.google.com/js/api.js"></script>
<script async="true" onload="gisLoaded()" src="https://accounts.google.com/gsi/client"></script>
</body>
</html>
</j:jelly>
Note that I have updated the class names to not conflict with already defined class names (e.g picker-dialog to x-picker-dialog). I have also added Jelly expressions to translate texts. The keyword HTML in the Jelly expressions (${HTML: ...}) indicates to the (Jelly) processor that the expression's result should be escaped for HTML. The GUID e8fba397978a3150594ffbc71153af5d used in attribute href of the link element is the Sys ID of the style record in table content_css. File extensions .cssdbx and .jsdbx are the way to indicate to ServiceNow that it should look up a record by Sys ID in table content_css and by API Name in table sys_ui_script respectively. Also adding tags like html, head, title, meta and body only make sense if you mark the UI Page as a Direct one.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
10-29-2023 06:33 AM
Alright, Let me handle the redirectURL challenge.
Many thanks for your knowledge.
Can this be adjusted via CSS anyway?
Some blank line under auth and signOut button.
Also, the renderer which is hanging in the bottom.
Plus, Could you help me with any resource for the jellyScripting knowlege?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
10-30-2023 01:34 PM
For sure the look can be adjusted via CSS.
Just add the needed rules to the file I proposed above, bump the version parameter for the CSS link element and you're done.
As for Jelly scripting knowledge, I learned most of by studying the official ServiceNow Jelly documentation, reading articles here and there and applying common sense logic learned for other processors, like PHP. Also reverse engineering existing UI Pages and UI Macros.
In the end Jelly is the "basic" Apache Jelly (probably just a subset of it) augmented with Glide tags.
However, I would recommend not to lean towards Jelly - if you are not already proficient in it, better concentrate on future technologies like Next Experience - editing workspaces using UI Builder or creating applications from scratch using App Engine Studio. Those are the technologies of the future with ServiceNow. Jelly is pretty much a dead-end - as far as ServiceNow trends go.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11-01-2023 08:47 PM
Interesting, Thanks for your knowledge sharing. Much appreciated and a 90degree bow.
Many thanks,
Kingstan.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11-02-2023 12:34 AM - edited 11-02-2023 12:34 AM
You're most welcome; hope it helps.
Also 90° bow.
Regards,
János
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11-05-2024 12:25 PM
First of all, thanks for this information. I am facing below error when implementing this in our dev environment. I have tried it in my personal and company google developer consoles, but the issue is same. Can you please help on how to resolve this issue with CORS policy?
Access to XMLHttpRequest at 'https://apis.google.com/js/api.js' from origin 'https://<instancename>.service-now.com' has been blocked by CORS policy: Request header field x-transaction-source is not allowed by Access-Control-Allow-Headers in prefliight response.