共享表单绑定脚本,作为使用表单的用户运行

Share form bound script, have it run as the user using the form

我创建了一个表单绑定脚本,它获取表单内容,生成 PDF 和 HTML 电子邮件,并最终将所有内容发送到电子邮件中。

这将由同事使用。目前在我分享脚本后,发送的电子邮件在 Google 帐户下,因为我是脚本的所有者。

如何以使用表单的用户身份强制执行脚本 运行?

注意:由于我与多个用户共享 sheet,我计划让他们打开编辑 sheet,然后单击 "view live form" 并从那里填写。

我以前没有处理过共享脚本。我有一种感觉,我需要使脚本成为一个独立的脚本,将其作为库共享并共享表单。然后员工可以将图书馆添加到共享表单中,并 运行 它在他们自己的帐户而不是我的帐户下?希望有比这更简单的过程。

Currently after I shared the scripts, the emails sent are under (my) google account, since I am the owner of the script.

这是由于描述的行为 here可安装 [trigger] 版本 运行s 具有创建触发器的用户的授权,即使另一个用户具有编辑权限打开电子表格。

正如您所确定的那样,解决方案是让每个用户自己安装一个触发器。但是,您不必强迫他们接触 Google Apps 脚本编辑器,您可以通过三个选项来提供一个用户界面,让他们在共享电子表格 中管理自己的触发器,

表单提交触发器

这是一个简单的触发器功能,它将向编写触发器的用户发送一封电子邮件,其中包含他们的回复,并且仅针对他们自己的回复。

function mailMeMyResponse(e) {
  // This only works in forms collecting user names.
  if (!e.namedValues.hasOwnProperty("Username")) {
    //throw new Error( "Form not collecting user names." );
    return;
  }

  var me = Session.getActiveUser().getEmail();

  // If the current form response is mine, email it to me
  if (me == e.namedValues["Username"]) {
    var body = "Your responses were:\n\n";
    for (var resp in e.namedValues) {
      body += resp + ": " + e.namedValues[resp] + "\n";
    }
    MailApp.sendEmail(me, "Your form responses", body);
  }
}

UI 选项 1:自定义菜单

这组函数为电子表格提供了一个自定义菜单,标签为 "MailMe"。菜单中只有一个项目,它会根据用户通知触发器的当前状态而变化。

具有编辑权限的用户第一次打开电子表格时,菜单选项将为 "Enable response notification"。通过选择它,他们将触发脚本的授权,并设置他们的触发功能以开始监控电子表格中的响应。 (仅此而已!)之后,他们始终可以通过同一菜单禁用通知。

触发器的ID保存在用户的属性中,用于后续管理菜单和触发器状态。

function onOpen(e) {
  // Set up custom menu      
  updateMenu(e);
}

function updateMenu() {
  var menu = SpreadsheetApp.getUi().createMenu("MailMe");

  var props = PropertiesService.getUserProperties();

  // Check whether a trigger function is defined for this user
  var triggerId = props.getProperty("triggerId");
  if (triggerId) {
    menu.addItem('Disable response notification', 'deleteFormTrigger');
  } else {
    menu.addItem('Enable response notification', 'createFormTrigger');
  }
  menu.addToUi();
}

function createFormTrigger() {
  var props = PropertiesService.getUserProperties();

  // Check whether a trigger function is defined for this user
  var triggerId = props.getProperty("triggerId");
  if (triggerId == null) {  
    // Set up form submission trigger for this user
    var ss = SpreadsheetApp.getActive();
    var triggerId = ScriptApp.newTrigger("mailMeMyResponse")
                             .forSpreadsheet(ss)
                             .onFormSubmit()
                             .create()
                             .getUniqueId();
    // Remember triggerId
    props.setProperty("triggerId", triggerId);
  }

  // Update menu
  updateMenu();
}

function deleteFormTrigger() {
  var props = PropertiesService.getUserProperties();

  // Check whether a trigger function is defined for this user
  var triggerId = props.getProperty("triggerId");
  if (triggerId !== null) {
    if (deleteTriggerById( triggerId )) {
      // Trigger was deleted, delete property
      props.deleteProperty("triggerId");

      // Update menu
      updateMenu();
    }
  }
}

function deleteTriggerById(triggerId) {      
  if (triggerId !== null) {
    // Search all triggers
    var triggers = ScriptApp.getProjectTriggers();
    for (var i=0; i<triggers.length; i++) {
      if (triggers[i].getUniqueId() == triggerId) {
        // Found our trigger, delete it.
        ScriptApp.deleteTrigger(triggers[i]);
        return true;
      }
    }
    return false; // Didn't find trigger
  }
  return true;  // Nothing to do
}

UI 选项 2:附加组件

与自定义菜单脚本相比,此方法的优势在于它不依赖于任何特定的电子表格文件。它可以用于您域中的任何电子表格,而无需复制脚本。

插件的代码比自定义菜单的代码更复杂,因为它需要处理从商店安装和授权流程。但是,代码的结构在很大程度上是相同的,只是为了适应更广泛的需求而进行了细微的调整。 (例如,查找用于驱动菜单刷新的事件对象。)

创建和删除触发函数的函数与选项 1 相同。

function onInstall(e) {
  onOpen(e);
}

function onOpen(e) {  
  if (e && e.authMode == ScriptApp.AuthMode.NONE) {
    // Add a normal menu item (works in all authorization modes).
    updateMenu(e);
  } else {
    // Privileged setup, based on properties (doesn't work in AuthMode.NONE).
    createFormTrigger();
  }
}

function updateMenu(e) {
  var menu = SpreadsheetApp.getUi().createAddonMenu();
  if (e && e.authMode == ScriptApp.AuthMode.NONE) {
    // Add a normal menu item (works in all authorization modes).
    menu.addItem('Authorize & enable response notification', 'createFormTrigger');
  } else {
    // Add a menu item based on properties (doesn't work in AuthMode.NONE).
    var props = PropertiesService.getUserProperties();

    // Check whether a trigger function is defined for this user
    var triggerId = props.getProperty("triggerId");
    if (triggerId) {
      menu.addItem('Disable response notification', 'deleteFormTrigger');
    } else {
      menu.addItem('Enable response notification', 'createFormTrigger');
    }
  }
  menu.addToUi();
}

function deleteFormTrigger() {
  var props = PropertiesService.getUserProperties();

  // Check whether a trigger function is defined for this user
  var triggerId = props.getProperty("triggerId");
  if (triggerId !== null) {
    if (deleteTriggerById( triggerId )) {
      // Trigger was deleted, delete property
      props.deleteProperty("triggerId");

      // Update menu, using fake event
      updateMenu({authMode:ScriptApp.AuthMode.FULL});
    }
  }
}

function deleteTriggerById(triggerId) {      
  if (triggerId !== null) {
    // Search all triggers
    var triggers = ScriptApp.getProjectTriggers();
    for (var i=0; i<triggers.length; i++) {
      if (triggers[i].getUniqueId() == triggerId) {
        // Found our trigger, delete it.
        ScriptApp.deleteTrigger(triggers[i]);
        return true;
      }
    }
    return false; // Didn't find trigger
  }
  return true;  // Nothing to do
}

function createFormTrigger() {
  var props = PropertiesService.getUserProperties();

  // Check whether a trigger function is defined for this user
  var triggerId = props.getProperty("triggerId");
  if (triggerId == null) {  
    // Set up form submission trigger for this user
    var ss = SpreadsheetApp.getActive();
    var triggerId = ScriptApp.newTrigger("mailMeMyResponse")
                             .forSpreadsheet(ss)
                             .onFormSubmit()
                             .create()
                             .getUniqueId();
    // Remember triggerId
    props.setProperty("triggerId", triggerId);
  }

  // Update menu, using fake event
  updateMenu({authMode:ScriptApp.AuthMode.FULL});
}

UI 选项 3:启用通知的 Webapp

另一种完全不需要用户打开电子表格的可能方法是将早期的 createFormTrigger() 功能调整为独立脚本 Web 应用程序,共享并设置为 运行 作为个人用户,可能有自己的简单 UI 来打开和关闭通知。