google-apps-script - 如何删除或以编程方式禁用/阻止复制的 Google Doc 上的附加脚本?
问题描述
我正在寻找模拟 Google Classroom 作业功能的行为,该功能允许您为学生列表制作文档副本。此副本保留创建者的所有权,并将学生添加为编辑。
我已经使用附加到模板文档的脚本成功地完成了这项工作。教师可以将电子邮件地址列表粘贴到自定义拉出中,并在提交时循环创建具有权限的每个文档。
尝试:
- 我可以使用
copy()
功能来复制文档,但附带的脚本会随路而行,然后学生可以访问。这不是主要的安全风险,但有可能被滥用。 - 通过使用经常提到的循环所有元素并将它们附加到新文档的方法,我可以取得适度的成功,但到目前为止,我还不能以这种方式使一切工作。教师可能创建的某些图像、表格和其他元素在新文档中的格式不正确。
有希望的解决方案:
- 有没有办法从复制的文档中删除脚本?或者
- 有没有办法使用权限只允许教职员工运行脚本?(我们确实有一个教职员工组织单元,但是一旦所有教职员工都使用该工具,我对 AdminDirectory 模块的测试让我担心权限。)或
- 知道我们的学生电子邮件地址的格式与我们的教师电子邮件地址不同,我可以根据当前用户的电子邮件地址解析以编程方式阻止脚本吗?
我一直在兜圈子,最后一直在解释如何一次将一个元素复制到新文档中的帖子。由于格式化,这似乎还不够,所以我希望涉及保留该copy()
功能的其他解决方案之一是可能的。
侧边栏代码 sidebar.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script>
</script>
<style>
textarea{
font-size: .9em;
width: 90%;
height: 300px;
}
</style>
</head>
<body>
<textarea id="addressList">
Paste class email list here with spaces between addresses.
</textarea>
<p>
<input id="submit" type="button" value="Distribute to Students" onclick="distributor();" />
<input id="reset" type="button" value="Reset" onclick="reset();" />
</p>
<script>
function reset(){
document.getElementById('addressList').value = "Paste class email list here with spaces between addresses.";
document.getElementById("submit").disabled = false;
}
var mainOut = "";
function cleanUp(output){
mainOut += output;
document.getElementById('addressList').value = mainOut;
}
function distributor(){
document.getElementById("submit").disabled = true;
var nameList = document.getElementById('addressList').value; // get email list
var list = nameList.split("\n"); // break it up
// loop through names
for(let i = 0; i < list.length; i++){
// create a doc for each student and return success to the UI
google.script.run.withSuccessHandler(cleanUp).distro(list[i],i+1,list.length);
}
}
</script>
</body>
</html>
当前脚本代码.gs
// student email addresses end in a 2 digit class year, faculty does not
var event;
var emailParts = Session.getEffectiveUser().getEmail().split('@');
var username = emailParts[0];
var classCheck = username.substring(username.length-2);
var validUser = false;
// using school email address style, determine if student or teacher to hide the UI changes
if(isNaN(classCheck)){
validUser = true;
}
// Custom Menu
function onOpen(e){
if(validUser){
event = e;
var ui = DocumentApp.getUi();
var faMenu = ui.createMenu('FA')
.addItem('Open Distribution Tool', 'openTool').addToUi();
}
}
/**
* Creates Custom Sidebar for emailing teams from spreadsheet
*/
function openTool() {
if(validUser){
var html = HtmlService.createHtmlOutputFromFile('sidebar');
html.setTitle('Share with Students');
html.setWidth(400);
html.setContent(html.getContent());
DocumentApp.getUi().showSidebar(html);
}
}
// create copy, set permissions
function distro(studentEmail,count,total){
var output = "";
if(validUser){
var teacherEmail = 'xxx@xxx.org'; // this will be replaced by email of logged in user
var thisDoc = DocumentApp.getActiveDocument();
let studentName = studentEmail.split('@')[0];
if(studentEmail != "" && studentEmail != null){
let filename = thisDoc.getName() + "- " + studentName;
let newDoc = DriveApp.getFileById(thisDoc.getId()).makeCopy(filename);
newDoc.setOwner(studentEmail);
newDoc.addEditor(teacherEmail);
output += "Created doc " + count + "/" + total + ": " + filename + "\n";
}
}
return output;
}
解决方案
问题:
我认为您不能以编程方式从复制的文档中删除绑定的脚本。
理论上,如果您使用Apps Script API,通过调用projects.updateContent并为您的Files设置和清空内容,这是可能的。
然而,这需要知道scriptId
,并且您不能以编程方式检索scriptId
不是当前脚本的绑定脚本(对于当前脚本,可以使用Session.getScriptId() )。例如,请参阅此答案以及此相关功能请求:
解决方法 - 使用库:
作为一种解决方法,我建议将至少一些脚本代码放在不同的独立脚本中,并让您的模板调用这个库。这样,绑定到复制文件的脚本将无法使用库源代码,该脚本只能运行库中定义的不同功能。
例如,您可以移动onOpen
和distro
到另一个脚本:
图书馆代码.gs:
// student email addresses end in a 2 digit class year, faculty does not
var event;
var emailParts = Session.getEffectiveUser().getEmail().split('@');
var username = emailParts[0];
var classCheck = username.substring(username.length-2);
var validUser = false;
// using school email address style, determine if student or teacher to hide the UI changes
if(isNaN(classCheck)){
validUser = true;
}
// Custom Menu
function onOpen(e){
if(validUser){
event = e;
var ui = DocumentApp.getUi();
var faMenu = ui.createMenu('FA')
.addItem('Open Distribution Tool', 'openTool').addToUi();
}
}
// create copy, set permissions
function distro(studentEmail,count,total){
var output = "";
if(validUser){
var teacherEmail = 'xxx@xxx.org'; // this will be replaced by email of logged in user
var thisDoc = DocumentApp.getActiveDocument();
let studentName = studentEmail.split('@')[0];
if(studentEmail != "" && studentEmail != null){
let filename = thisDoc.getName() + "- " + studentName;
let newDoc = DriveApp.getFileById(thisDoc.getId()).makeCopy(filename);
newDoc.setOwner(studentEmail);
newDoc.addEditor(teacherEmail);
output += "Created doc " + count + "/" + total + ": " + filename + "\n";
}
}
return output;
}
然后,将此脚本作为库共享,并在您的模板脚本中使用它,可能是这样的(LIBRARY
您之前共享的库的标识符在哪里):
模板代码.gs:
// student email addresses end in a 2 digit class year, faculty does not
var event;
var emailParts = Session.getEffectiveUser().getEmail().split('@');
var username = emailParts[0];
var classCheck = username.substring(username.length-2);
var validUser = false;
// using school email address style, determine if student or teacher to hide the UI changes
if(isNaN(classCheck)){
validUser = true;
}
// Custom Menu
function onOpen(e){
LIBRARY.onOpen(e);
}
/**
* Creates Custom Sidebar for emailing teams from spreadsheet
*/
function openTool() {
if(validUser){
var html = HtmlService.createHtmlOutputFromFile('sidebar');
html.setTitle('Share with Students');
html.setWidth(400);
html.setContent(html.getContent());
DocumentApp.getUi().showSidebar(html);
}
}
// create copy, set permissions
function distro(studentEmail,count,total){
LIBRARY.distro(studentEmail, count, total);
}
在此示例中,sidebar.html
也将包含在您的模板脚本中。
笔记:
- 这是一个基本示例,只是为了展示如何做到这一点,并且可能会得到改进。例如,即使调用via可能会变得棘手,也应该可以
openTool
将文件移动到库代码中:请参阅使用 google.script.run 从 html 调用库函数。.html
distro
google.script.run
参考:
推荐阅读
- javascript - 记录来自 API 调用的函数结果 - Javascript
- reactjs - 您可以根据路由器位置动态更新 Firestore 查询吗?
- c - 为什么 fopen() 和 open() 不能识别 char 指针
- python - 带有 DeleteView 的 Bootbox 在 Django 中找不到 url
- python - 需要帮助打印清单
- mysql - docker-compose 与 Spring Boot 和 MySQL 集成不断发生错误通信链接失败
- linux - 在 pythonanywhere 中初始化 Tor 时出错 - 卡在 5%
- excel - vba 循环运行时单元格编辑错误
- swiftui - 如何以编程方式从 SwiftUI 列表中删除行并刷新列表视图?
- javascript - 我的代码在浏览器中的工作方式与在我的 IDE 中不同