Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: update picker example #486

Merged
merged 1 commit into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 2 additions & 9 deletions picker/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
# File Picker Sample

This sample shows how to create a "file-open" dialog in Google Sheets that
allows the user to select a file from their Drive. It does so by loading
[Google Picker](https://developers.google.com/picker/), a standard G Suite
client-side API for this purpose. More information is available in the Apps
Script guide
[Dialogs and Sidebars in Google Workspace Documents](https://developers.google.com/apps-script/guides/dialogs#file-open_dialogs).
This sample shows how to create a "file-open" dialog in Google Sheets thatallows the user to select a file from their Drive. It does so by loading [Google Picker](https://developers.google.com/picker/), for this purpose. More information is available in the Apps Script guide [Dialogs and Sidebars in Google Workspace Documents](https://developers.google.com/apps-script/guides/dialogs#file-open_dialogs).

Note that this sample expects to be
[bound](https://developers.google.com/apps-script/guides/bound)
to a spreadsheet.
Note that this sample expects to be [bound](https://developers.google.com/apps-script/guides/bound) to a spreadsheet.
18 changes: 18 additions & 0 deletions picker/appsscript.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"timeZone": "America/Los_Angeles",
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": [
"https://www.googleapis.com/auth/script.container.ui",
"https://www.googleapis.com/auth/drive.file"
],
"dependencies": {
"enabledAdvancedServices": [
{
"userSymbol": "Drive",
"version": "v3",
"serviceId": "drive"
}
]
}
}
41 changes: 16 additions & 25 deletions picker/code.gs
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,28 @@
* Creates a custom menu in Google Sheets when the spreadsheet opens.
*/
function onOpen() {
try {
SpreadsheetApp.getUi().createMenu('Picker')
.addItem('Start', 'showPicker')
.addToUi();
} catch (e) {
// TODO (Developer) - Handle exception
console.log('Failed with error: %s', e.error);
}
SpreadsheetApp.getUi()
.createMenu("Picker")
.addItem("Start", "showPicker")
.addToUi();
}

/**
* Displays an HTML-service dialog in Google Sheets that contains client-side
* JavaScript code for the Google Picker API.
*/
function showPicker() {
try {
const html = HtmlService.createHtmlOutputFromFile('dialog.html')
.setWidth(600)
.setHeight(425)
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
SpreadsheetApp.getUi().showModalDialog(html, 'Select a file');
} catch (e) {
// TODO (Developer) - Handle exception
console.log('Failed with error: %s', e.error);
}
const html = HtmlService.createHtmlOutputFromFile("dialog.html")
.setWidth(800)
.setHeight(600)
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
SpreadsheetApp.getUi().showModalDialog(html, "Select a file");
}
/**
* Checks that the file can be accessed.
*/
function getFile(fileId) {
return Drive.Files.get(fileId, { fields: "*" });
}

/**
Expand All @@ -57,12 +54,6 @@ function showPicker() {
* @return {string} The user's OAuth 2.0 access token.
*/
function getOAuthToken() {
try {
DriveApp.getRootFolder();
return ScriptApp.getOAuthToken();
} catch (e) {
// TODO (Developer) - Handle exception
console.log('Failed with error: %s', e.error);
}
return ScriptApp.getOAuthToken();
}
// [END picker_code]
237 changes: 153 additions & 84 deletions picker/dialog.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,106 +13,175 @@
<!-- [START picker_html] -->
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons.css">
<script>
// IMPORTANT: Replace the value for DEVELOPER_KEY with the API key obtained
// from the Google Developers Console.
var DEVELOPER_KEY = 'ABC123 ... ';
var DIALOG_DIMENSIONS = {width: 600, height: 425};
var pickerApiLoaded = false;
<head>
<link
rel="stylesheet"
href="https://ssl.gstatic.com/docs/script/css/add-ons.css"
/>
<style>
#result {
display: flex;
flex-direction: column;
gap: 0.25em;
}

pre {
font-size: x-small;
max-height: 25vh;
overflow-y: scroll;
background: #eeeeee;
padding: 1em;
border: 1px solid #cccccc;
}
</style>
<script>
// TODO: Replace the value for DEVELOPER_KEY with the API key obtained
// from the Google Developers Console.
const DEVELOPER_KEY = "AIza...";
// TODO: Replace the value for CLOUD_PROJECT_NUMBER with the project
// number obtained from the Google Developers Console.
const CLOUD_PROJECT_NUMBER = "1234567890";

let pickerApiLoaded = false;
let oauthToken;

/**
* Loads the Google Picker API.
*/
function onApiLoad() {
gapi.load('picker', {'callback': function() {
pickerApiLoaded = true;
}});
}
/**
* Loads the Google Picker API.
*/
function onApiLoad() {
gapi.load("picker", {
callback: function () {
pickerApiLoaded = true;
},
});
}

/**
* Gets the user's OAuth 2.0 access token from the server-side script so that
* it can be passed to Picker. This technique keeps Picker from needing to
* show its own authorization dialog, but is only possible if the OAuth scope
* that Picker needs is available in Apps Script. Otherwise, your Picker code
* will need to declare its own OAuth scopes.
*/
function getOAuthToken() {
google.script.run
.withSuccessHandler((token) => {
oauthToken = token;
createPicker(token);
})
.withFailureHandler(showError)
.getOAuthToken();
}

/**
* Gets the user's OAuth 2.0 access token from the server-side script so that
* it can be passed to Picker. This technique keeps Picker from needing to
* show its own authorization dialog, but is only possible if the OAuth scope
* that Picker needs is available in Apps Script. Otherwise, your Picker code
* will need to declare its own OAuth scopes.
*/
function getOAuthToken() {
google.script.run.withSuccessHandler(createPicker)
.withFailureHandler(showError).getOAuthToken();
}
/**
* Creates a Picker that can access the user's spreadsheets. This function
* uses advanced options to hide the Picker's left navigation panel and
* default title bar.
*
* @param {string} token An OAuth 2.0 access token that lets Picker access the
* file type specified in the addView call.
*/
function createPicker(token) {
document.getElementById("result").innerHTML = "";

/**
* Creates a Picker that can access the user's spreadsheets. This function
* uses advanced options to hide the Picker's left navigation panel and
* default title bar.
*
* @param {string} token An OAuth 2.0 access token that lets Picker access the
* file type specified in the addView call.
*/
function createPicker(token) {
if (pickerApiLoaded && token) {
var picker = new google.picker.PickerBuilder()
if (pickerApiLoaded && token) {
const picker = new google.picker.PickerBuilder()
// Instruct Picker to display only spreadsheets in Drive. For other
// views, see https://developers.google.com/picker/docs/#otherviews
.addView(google.picker.ViewId.SPREADSHEETS)
// views, see https://developers.google.com/picker/reference/picker.viewid
.addView(
new google.picker.DocsView(
google.picker.ViewId.SPREADSHEETS
).setOwnedByMe(true)
)
// Hide the navigation panel so that Picker fills more of the dialog.
.enableFeature(google.picker.Feature.NAV_HIDDEN)
// Hide the title bar since an Apps Script dialog already has a title.
.hideTitleBar()
.setOAuthToken(token)
.setDeveloperKey(DEVELOPER_KEY)
.setAppId(CLOUD_PROJECT_NUMBER)
.setCallback(pickerCallback)
.setOrigin(google.script.host.origin)
// Instruct Picker to fill the dialog, minus 2 pixels for the border.
.setSize(DIALOG_DIMENSIONS.width - 2,
DIALOG_DIMENSIONS.height - 2)
.build();
picker.setVisible(true);
} else {
showError('Unable to load the file picker.');
picker.setVisible(true);
} else {
showError("Unable to load the file picker.");
}
}

/**
* A callback function that extracts the chosen document's metadata from the
* response object. For details on the response object, see
* https://developers.google.com/picker/reference/picker.responseobject
*
* @param {object} data The response object.
*/
function pickerCallback(data) {
const action = data[google.picker.Response.ACTION];
if (action == google.picker.Action.PICKED) {
handlePicked(data);
} else if (action == google.picker.Action.CANCEL) {
document.getElementById("result").innerHTML = "Picker canceled.";
}
}

/**
* Handles `"PICKED"` responsed from the Google Picker.
*
* @param {object} data The response object.
*/
function handlePicked(data) {
const doc = data[google.picker.Response.DOCUMENTS][0];
const id = doc[google.picker.Document.ID];

google.script.run
.withSuccessHandler((driveFilesGetResponse) => {
// Render the response from Picker and the Drive.Files.Get API.
const resultElement = document.getElementById("result");
resultElement.innerHTML = "";

for (const response of [
{
title: "Picker response",
content: JSON.stringify(data, null, 2),
},
{
title: `Drive.Files.Get response`,
content: JSON.stringify(driveFilesGetResponse, null, 2),
},
]) {
const titleElement = document.createElement("h3");
titleElement.appendChild(document.createTextNode(response.title));
resultElement.appendChild(titleElement);

const contentElement = document.createElement("pre");
contentElement.appendChild(
document.createTextNode(response.content)
);
resultElement.appendChild(contentElement);
}
})
.withFailureHandler(showError)
.getFile(data[google.picker.Response.DOCUMENTS][0].id);
}
}

/**
* A callback function that extracts the chosen document's metadata from the
* response object. For details on the response object, see
* https://developers.google.com/picker/docs/result
*
* @param {object} data The response object.
*/
function pickerCallback(data) {
var action = data[google.picker.Response.ACTION];
if (action == google.picker.Action.PICKED) {
var doc = data[google.picker.Response.DOCUMENTS][0];
var id = doc[google.picker.Document.ID];
var url = doc[google.picker.Document.URL];
var title = doc[google.picker.Document.NAME];
document.getElementById('result').innerHTML =
'<b>You chose:</b><br>Name: <a href="' + url + '">' + title +
'</a><br>ID: ' + id;
} else if (action == google.picker.Action.CANCEL) {
document.getElementById('result').innerHTML = 'Picker canceled.';
/**
* Displays an error message within the #result element.
*
* @param {string} message The error message to display.
*/
function showError(message) {
document.getElementById("result").innerHTML = "Error: " + message;
}
}
</script>
</head>

/**
* Displays an error message within the #result element.
*
* @param {string} message The error message to display.
*/
function showError(message) {
document.getElementById('result').innerHTML = 'Error: ' + message;
}
</script>
</head>
<body>
<div>
<button onclick="getOAuthToken()">Select a file</button>
<p id="result"></p>
</div>
<script src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>
</body>
<body>
<div>
<button onclick="getOAuthToken()">Select a file</button>
<div id="result"></div>
</div>
<script src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>
</body>
</html>
<!-- [END picker_html] -->
Loading