Google apps script 当用户选择范围内的单元格时,Google应用程序脚本执行函数
我希望在用户单击/选择给定范围内的单元格时运行一个需要授权的函数。由于授权问题,简单的onSelectionChange(e)触发器不起作用,可安装的触发器显然不包括onSelectionChangeGoogle apps script 当用户选择范围内的单元格时,Google应用程序脚本执行函数,google-apps-script,google-sheets,triggers,Google Apps Script,Google Sheets,Triggers,我希望在用户单击/选择给定范围内的单元格时运行一个需要授权的函数。由于授权问题,简单的onSelectionChange(e)触发器不起作用,可安装的触发器显然不包括onSelectionChange 请问还有别的办法吗?谢谢 因此,我最后在每行添加了一个值为“Clear to run function”的列,如果该列中的一个值与“Clear to run function”不同,则使用一个简单的onEdit(e)触发我的函数 从用户体验的角度来看,这意味着清除一个单元格来运行该函数——虽然不理
请问还有别的办法吗?谢谢 因此,我最后在每行添加了一个值为“Clear to run function”的列,如果该列中的一个值与“Clear to run function”不同,则使用一个简单的onEdit(e)触发我的函数
从用户体验的角度来看,这意味着清除一个单元格来运行该函数——虽然不理想,但它是有效的。对于不涉及
ui
/HtmlService
的函数,可以使用简单的触发器通过削弱安全性来运行一些需要授权的函数(比如特权函数):
- 流程:Trigger=>onSelectionChange(没有获取/执行特权函数的授权)=>Trigger自定义函数(获得获取特权函数的授权/没有执行特权函数的授权)=>fetch/post=>webapp(完全授权运行特权函数)
- 此解决方案的灵感来自,它直接使用可安装触发器和普通访问令牌来授权自定义函数。从安全角度来看,不建议这样做
- 虽然已采取措施确保执行以下脚本的用户的安全和隐私,但尚未考虑所有攻击向量。脚本在很多方面都可能有漏洞,特别是在平台中缺乏加密模块支持的情况下。如果替代解决方案不可行,请自行承担使用风险
- 在大多数情况下,首选使用菜单/按钮/时间触发器/可安装触发器(始终在完全授权下运行)的替代解决方案使用OneEdit可安装触发器+复选框可以实现类似的流程
- 在清单文件中。对于示例脚本
"oauthScopes": ["https://www.googleapis.com/auth/script.send_mail"],
- 用于执行需要授权的功能的明确目的
- 以“我”的身份执行
- 访问:“任何人”
- 没有用于从自定义功能授权WebApp的明确目的的角色/权限
- 并将其复制到示例脚本中的
对象creds
- 与服务帐户共享您的项目/电子表格(
)client\u email
- 安装以为服务帐户创建/签署jwt令牌
- 创建一个用于设置自定义功能的
,该自定义功能将被设置为此工作表的A1hiddenSheet
onSelectionChange
- 当有人触摸电子表格中的任何内容时,以下脚本将发送电子邮件
复选框会更理想
/**
* Gets Oauth2 service based on service account with drive scope
* Drive scope needed to access webapp with access:anyone
* This does not grant access to the user's drive but the service
* account's drive, which will only contain the file shared with it
*/
function getService_() {
const creds = {
private_key: '[PRIVATE_KEY]',
client_email: '[CLIENT_EMAIL]',
};
const PRIVATE_KEY = creds['private_key'];
const CLIENT_EMAIL = creds['client_email'];
return OAuth2.createService('GoogleDrive:')
.setTokenUrl('https://oauth2.googleapis.com/token')
.setPrivateKey(PRIVATE_KEY)
.setIssuer(CLIENT_EMAIL)
.setPropertyStore(PropertiesService.getUserProperties())
.setScope('https://www.googleapis.com/auth/drive');
}
/**
* @returns {string} base64 encoded string of SHA_512 digest of random uuidstring
*/
const getRandHashKey_ = () =>
Utilities.base64EncodeWebSafe(
Utilities.computeDigest(
Utilities.DigestAlgorithm.SHA_512,
Utilities.getUuid() //type 4 advertised crypto secure
)
);
/**
* @param {GoogleAppsScript.Events.SheetsOnSelectionChange} e
*/
const onSelectionChange = e => {
const sCache = CacheService.getScriptCache();
e.rangestr = e.range.getSheet().getName() + '!' + e.range.getA1Notation();
const hashRandom = getRandHashKey_();
sCache.put(hashRandom, JSON.stringify(e), 20);//expires in 20 seconds
e.source
.getSheetByName('hiddenSheet')
.getRange('A1')
.setValue(`=CALLWEBAPP("${hashRandom}")`);
};
/**
* Calls published webapp(Access:Anyone) with service account token
* @customfunction
* @returns void
*/
const callwebapp = randomHash => {
const webAppScriptId = '[SCRIPT_ID]';
UrlFetchApp.fetch(
`https://script.google.com/macros/s/${webAppScriptId}/exec`,
{
method: 'post',
payload: { e: randomHash },
headers: { Authorization: `Bearer ${getService_().getAccessToken()}` },
}
);
};
/**
* @param {GoogleAppsScript.Events.AppsScriptHttpRequestEvent} e
*/
const doPost = e => {
const hashRandom = e.parameter.e;
const sCache = CacheService.getScriptCache();
const encodedSelectionEvent = sCache.get(hashRandom);
if (encodedSelectionEvent) {
const selectionEvent = JSON.parse(encodedSelectionEvent);
MailApp.sendEmail(
'[EMAIL_TO_SEND_NOTIFICATION_TO]',
'Someone touched your spreadsheet',
`Wanna take a look? ${selectionEvent.rangestr} was touched without your permission`
);
}
};