【问题标题】:How to run certain scripts under the authority of a specific user?如何在特定用户的权限下运行某些脚本?
【发布时间】:2022-01-13 00:27:06
【问题描述】:

感谢这里的另一个用户,我能够允许其他用户在不取消保护的情况下将新 SKU 添加到工作表。原文见此处:Restrict Editors to Specific Ranges Script

现在,我正在尝试做相反的事情,允许用户在不取消保护工作表的情况下删除 SKU。

我从以下开始,按预期工作:

function deleteEachRow(){
  const ss = SpreadsheetApp.getActive();
  var SHEET = ss.getSheetByName("Ordering");
  var RANGE = SHEET.getDataRange();
  const ui = SpreadsheetApp.getUi();
  const response = ui.prompt('WARNING: \r\n \r\n Ensure the following sheets DO NOT contain data before proceeding: \r\n \r\n Accessory INV \r\n Apparel INV \r\n Pending TOs \r\n \r\n Enter New SKU:', ui.ButtonSet.OK_CANCEL);
  if (response.getSelectedButton() === ui.Button.OK) {
    const text = response.getResponseText();
    var rangeVals = RANGE.getValues();
  
    //Reverse the 'for' loop.
    for(var i = rangeVals.length-1; i >= 0; i--){
    if(rangeVals[i][0] === text){
      
      SHEET.deleteRow(i+1);
    };
    };
  };
};

由于我是 javascript 新手,并且对 Web 应用程序非常陌生,我尝试将上述代码弗兰肯斯坦转化为我在上述 URL 中提供的答案。现在,脚本运行没有错误,但未能按预期删除输入的 SKU。这是我正在运行的脚本:

function deleteEachRow1(){
  const ss = SpreadsheetApp.getActive();
  var SHEET = ss.getSheetByName("Ordering");
  var RANGE = SHEET.getDataRange();
  const ui = SpreadsheetApp.getUi();
  const response = ui.prompt('WARNING: \r\n \r\n Ensure the following sheets DO NOT contain data before proceeding: \r\n \r\n Accessory INV \r\n Apparel INV \r\n Pending TOs \r\n \r\n Delete Which SKU?:', ui.ButtonSet.OK_CANCEL);
  if (response.getSelectedButton() === ui.Button.OK) {
    const text = response.getResponseText();

    const webAppsUrl = "WEB APP URL"; // Pleas set your Web Apps URL.

    const url = webAppsUrl + "?text=" + text;
    const res = UrlFetchApp.fetch(url, {muteHttpExceptions: true});
    // ui.alert(res.getContentText()); // You can see the response value using this line.
  }
}

function doGet(e) {
  const text = e.parameter.text;
  const sheet = SpreadsheetApp.getActive().getSheetByName('Ordering');
  var rangeVals = RANGE.getValues();
  
    //Reverse the 'for' loop.
    for(var i = rangeVals.length-1; i >= 0; i--){
    if(rangeVals[i][0] === text){
      
      SHEET.deleteRow(i+1);
    };
    };
  myFunction();
  return ContentService.createTextOutput(text);
}

// This script is from https://tanaikech.github.io/2017/07/31/converting-a1notation-to-gridrange-for-google-sheets-api/
function a1notation2gridrange1(a1notation) {
  var data = a1notation.match(/(^.+)!(.+):(.+$)/);
  var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(data[1]);
  var range = ss.getRange(data[2] + ":" + data[3]);
  var gridRange = {
    sheetId: ss.getSheetId(),
    startRowIndex: range.getRow() - 1,
    endRowIndex: range.getRow() - 1 + range.getNumRows(),
    startColumnIndex: range.getColumn() - 1,
    endColumnIndex: range.getColumn() - 1 + range.getNumColumns(),
  };
  if (!data[2].match(/[0-9]/)) delete gridRange.startRowIndex;
  if (!data[3].match(/[0-9]/)) delete gridRange.endRowIndex;
  return gridRange;
}

// Please run this function.
function myFunction() {
  const email = "MY EMAIL"; // <--- Please set your email address.

  // Please set your sheet names and unprotected ranges you want to use.
  const obj = [ 
  { sheetName: "Ordering", unprotectedRanges: ["O5:P", "C2:E2"] },  
  { sheetName: "Accessory INV", unprotectedRanges: ["E5:H"] },  
  { sheetName: "Apparel INV", unprotectedRanges: ["E5:F"] },  
  {sheetName: "Pending TOs", unprotectedRanges: ["E6:H"] }, 
  {sheetName: "INV REF", unprotectedRanges: ["C6:C"] },
];

  // 1. Retrieve sheet IDs and protected range IDs.
  const spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
  const sheets = Sheets.Spreadsheets.get(spreadsheetId, { ranges: obj.map(({ sheetName }) => sheetName), fields: "sheets(protectedRanges(protectedRangeId),properties(sheetId))" }).sheets;
  const { protectedRangeIds, sheetIds } = sheets.reduce((o, { protectedRanges, properties: { sheetId } }) => {
    if (protectedRanges && protectedRanges.length > 0) o.protectedRangeIds.push(protectedRanges.map(({ protectedRangeId }) => protectedRangeId));
    o.sheetIds.push(sheetId);
    return o;
  }, { protectedRangeIds: [], sheetIds: [] });
  
  // 2. Convert A1Notation to Gridrange.
  const gridranges = obj.map(({ sheetName, unprotectedRanges }, i) => unprotectedRanges.map(f => a1notation2gridrange1(`${sheetName}!${f}`)));

  // 3. Create request body.
  const deleteProptectedRanges = protectedRangeIds.flatMap(e => e.map(id => ({ deleteProtectedRange: { protectedRangeId: id } })));
  const protects = sheetIds.map((sheetId, i) => ({ addProtectedRange: { protectedRange: { editors: {users: [email]}, range: { sheetId }, unprotectedRanges: gridranges[i] } } }));
  
  // 4. Request to Sheets API with the created request body.
  Sheets.Spreadsheets.batchUpdate({ requests: [...deleteProptectedRanges, ...protects] }, spreadsheetId);
}

非常感谢任何见解。

【问题讨论】:

  • deleteEachRow 怎么叫?
  • 您好大师,它是使用按钮调用的。

标签: javascript google-apps-script google-sheets protection


【解决方案1】:

可能最简单的方法是避免使用按钮和using a checkbox with a installable edit trigger,这对移动支持也有很大的副作用。

建议的解决方案:

  • 使用复选框
  • 将其连接到installable edit trigger,该installable edit trigger 以安装触发器的用户身份运行。因此,如果所有者安装了触发器,那么无论谁编辑工作表,触发器都会以所有者身份运行,从而可以访问特权资源,包括受保护的范围。

可安装版本在创建触发器的用户的授权下运行,即使另一个具有编辑权限的用户打开了电子表格。

注意事项:

  • 优势:
    代码简单性和可维护性。无需 webapp 或任何复杂的设置。
  • 缺点:安全性(有可能的解决方法)
    如果代码绑定到工作表,工作表的编辑者可以直接访问工作表的脚本。因此,任何有恶意的编辑器都可以修改代码。如果带有可安装触发器的功能具有gmail 权限,则任何编辑器都可以记录所有者的所有电子邮件。因此,需要特别注意请求的权限。请注意,您的网络应用程序设置已经是这种情况。任何编辑者都可以修改doGet 以访问受保护的数据。如果 webapp 在单独的独立脚本中,这不是问题。您也可以通过将触发器设置为预定版本而不是 Head 版本来解决此问题。请参阅this answer 了解更多信息。

【讨论】:

  • 您好大师,这绝对是解决方案,虽然我仍在弄清楚如何正确实施它。我很感激!
猜你喜欢
  • 2021-10-06
  • 1970-01-01
  • 2023-04-03
  • 2010-09-28
  • 2021-09-04
  • 1970-01-01
  • 1970-01-01
  • 2012-08-05
  • 1970-01-01
相关资源
最近更新 更多