我的电子表格需要由我的组织中的各个用户转换为PDF。为此,我尝试使用OAuth2(https://github.com/googlesamples/apps-script-oauth2)。
当我在主电子表格嵌入式脚本中使用它时,它可以工作。但是,我试图将OAuth2代码移动到一个单独的库,因为会有许多电子表格需要相同的功能。当我使用这个库时,我收到消息“状态令牌无效或已过期。请再试一次。”
图书馆代码:
var PROPERTY_KEY = "ExportPdfOauth2";
function initialStore() {
setAuthenticationPackage_( {
clientId : '10511......b43bu.apps.googleusercontent.com',
clientSecret : 'WYUsq...-h_',
projectKey : 'MxJ.......xOvW',
scopes : ['https://spreadsheets.google.com/feeds/']
});
}
function setAuthenticationPackage_(package) {
PropertiesService.getScriptProperties().setProperty(PROPERTY_KEY, JSON.stringify(package));
}
function getAuthenticationPackage_() {
var p = PropertiesService.getScriptProperties().getProperty(PROPERTY_KEY);
return p ? JSON.parse(p) : {};
}
function getExportPdfService() {
// Create a new service with the given name. The name will be used when
// persisting the authorized token, so ensure it is unique within the
// scope of the property store.
var package = getAuthenticationPackage_();
return OAuth2.createService('exportPdf')
// Set the endpoint URLs, which are the same for all Google services.
.setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
// Set the client ID and secret, from the Google Developers Console.
.setClientId(package.clientId)
.setClientSecret(package.clientSecret)
// Set the project key of the script using this library.
.setProjectKey(package.projectKey)
// Set the name of the callback function in the script referenced
// above that should be invoked to complete the OAuth flow.
.setCallbackFunction('authCallback')
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getUserProperties())
// Set the scopes to request (space-separated for Google services).
.setScope(package.scopes)
// Sets the login hint, which will prevent the account chooser screen
// from being shown to users logged in with multiple accounts.
.setParam('login_hint', Session.getActiveUser().getEmail())
// Requests offline access.
.setParam('access_type', 'offline')
// Forces the approval prompt every time. This is useful for testing,
// but not desirable in a production application.
.setParam('approval_prompt', 'force');
}
function authCallback(request) {
var user = Session.getActiveUser().getEmail();
var exportPdfService = getExportPdfService();
var isAuthorized = exportPdfService.handleCallback(request);
if (isAuthorized) {
return HtmlService.createHtmlOutput('Success');
} else {
return HtmlService.createHtmlOutput('Failure');
}
}
function getExportPdfRequest() {
var user = Session.getActiveUser().getEmail();
var exportPdfService = getExportPdfService();
if (!exportPdfService.hasAccess()) {
var template = HtmlService.createTemplate('Authorisation required in order to enable conversion to pdf. You will need to perform the operation again once the authorisation is complete.')+
'<br /><br />'+
'<a href="<?= authorizationUrl ?>" target="_blank">'+
'Authorise'+
'</a>');
template.authorizationUrl = exportPdfService.getAuthorizationUrl();
var page = template.evaluate();
SpreadsheetApp.getUi().showSidebar(page);
return null;
}
var request = {
headers: {
Authorization: 'Bearer ' + exportPdfService.getAccessToken()
}
};
return request;
}
嵌入式脚本调用库如下(请注意我在这里放了一个回调函数来回调库):
function test(){
var id = 'ABCD.....'; // Spreadsheet to be converted to pdf
var name = 'test.pdf';
var domain = 'XXXXX';
var pdfContent = spreadsheetToPDF(id,domain,name);
if (pdfContent) DocsList.createFile(pdfContent);
}
// Convert spreadsheet to PDF file.
function spreadsheetToPDF(id,domain,name) {
SpreadsheetApp.flush();
var request = testPdfLib.getExportPdfRequest();
if (!request) return null;
//define the params URL to fetch
var params = '?fitw=true&exportFormat=pdf&format=pdf&size=A4&portrait=true&sheetnames=false&printtitle=false&gridlines=false&pagenum=CENTER';
var url = "https://docs.google.com/a/"+domain+"/spreadsheets/d/"+id+"/export"+params;
//fetching file url
var blob = UrlFetchApp.fetch(url, request);
blob = blob.getBlob().setName(name);
//return file
return blob;
}
function authCallback(request) {
testPdfLib.authCallback(request);
}
我还查看了How to correctly construct state tokens for callback urls in Managed Libraries?的另一篇文章,但由于我在主调用脚本中提供了回调函数,但仍无法弄清楚我做错了什么。在开发人员控制台上,我的REDIRECT URIS指向图书馆。我试图将它指向主脚本,但我得到相同的结果(虽然我不想这样做,因为脚本将被许多用户复制,并且不可能为每个实例创建一个新的客户端ID调用脚本)。
感谢任何帮助!