Search code examples
google-apps-scriptgoogle-sheets

How to duplicate (copy) a row keeping the protected ranges


In Google sheets, I have a sheet in which there is a group of lines that duplicate a template line. Within the template line I have protected cells or ranges of cells that only I can modify.

I want the other users to be able to add copies of the template line to the group using a button that triggers a script. At the moment the script, does the copy of the template line correctly but the ranges that were protected in the template or no longer protected in the copy.

Here is the script:

function AddParticipant(){
  var spreadsheet= SpreadsheetApp.getActive();
  var thisSheet=spreadsheet.getActiveSheet()
  var sourceRange=thisSheet.getRange(7,1,1,thisSheet.getMaxColumns()).activate()

  //the ◇ char is the place before which the copy must be inserted
  var testCol= thisSheet.getRange(1,2,thisSheet.getMaxRows()).getValues()

  var pos=0
  for (var i=0;i<testCol.length;i++){
    if (testCol[i] == "◇"){
      break
    }
    pos=i+2
  }
 var destRange=thisSheet.getRange(pos,1).activate()
 thisSheet.insertRowsBefore(thisSheet.getActiveRange().getRow(),1)
 sourceRange.copyTo(destRange,SpreadsheetApp.CopyPasteType.PASTE_NORMAL,false)
}

Is it possible to keep the protected ranges with the same protection in the copy?


Solution

  • From your showing script and your question, I understood your situation as follows.

    • When you run AddParticipant, you want to copy row 7 to the destination row of the same sheet.
    • The destination rows are protected. By this, when you who is the owner of Spreadsheet run the script, no error occurs. But, when a user who is not the owner runs the script, an error occurs because of the protected range.
    • You want to make users run the script without the error even when the destination range is protected.

    In this case, how about the following modification?

    Modification points:

    In order to edit the protected range with the script by users who are not the owner of the Spreadsheet, I think that when the script is run as the owner of the Spreadsheet, no error occurs.

    In order to achieve this, I would like to propose the following 2 patterns.

    1. The installable OnEdit trigger is used. When the installable OnEdit trigger is used, the script can be run by the owner.
    2. The Web Apps is used. When Web Apps is used, the script can be run by the owner.

    Pattern 1: Use installale OnEdit trigger

    Unfortunately, in this case, the button on the Spreadsheet cannot be used. Because, when the script is run by clicking a button on Spreadsheet, the script is run by each user. So, in order to use this pattern, please replace the button with a checkbox. In this pattern, the checkbox is used as the button for executing the script.

    For example, please insert a checkbox to "A1". By this, when the checkbox is checked, the script is run. The modified script is as follows.

    And, please install the OnEdit trigger to installedOnEdit. Ref By this, when the checkbox is checked, the script is run.

    function installedOnEdit(e) {
      const { range } = e;
      if (range.getA1Notation() != "A1" || !range.isChecked()) return;
      AddParticipant();
      range.uncheck()
    }
    
    function AddParticipant() {
      var spreadsheet = SpreadsheetApp.getActive();
      var thisSheet = spreadsheet.getActiveSheet();
      var sourceRange = thisSheet.getRange(7, 1, 1, thisSheet.getMaxColumns()).activate();
      //the ◇ char is the place before wich the copy must be inserted
      var testCol = thisSheet.getRange(1, 2, thisSheet.getMaxRows()).getValues();
      var pos = 0;
      for (var i = 0; i < testCol.length; i++) {
        if (testCol[i] == "◇") {
          break;
        }
        pos = i + 2;
      }
      var destRange = thisSheet.getRange(pos, 1).activate();
      thisSheet.insertRowsBefore(thisSheet.getActiveRange().getRow(), 1);
      sourceRange.copyTo(destRange, SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
    }
    
    • When the checkbox of "A1" is checked, AddParticipant() is run.

    Pattern 2: Use Web Apps

    In this pattern, your button on the Spreadsheet can be used. The script is run through the Web Apps. In order to use this, please do the following flow.

    1. Deploy Web Apps.

    The detailed information can be seen in the official document.

    Please set this using the new IDE of the script editor.

    1. On the script editor, at the top right of the script editor, please click "click Deploy" -> "New deployment".
    2. Please click "Select type" -> "Web App".
    3. Please input the information about the Web App in the fields under "Deployment configuration".
    4. Please select "Me" for "Execute as".
    5. Please select "Anyone with Google account" for "Who has access to the app:".
    6. Please click "Deploy" button.
    7. On the script editor, at the top right of the script editor, please click "click Deploy" -> "Test deployments".
    8. Copy Web Apps URL. It's like https://script.google.com/macros/s/###/exec. This URL is used with Python script.

    2. Sample script.

    Please copy and paste the following script to the script editor. And, please set your Web Apps URL to const webAppsUrl = "https://script.google.com/macros/s/###/dev";.

    const doGet = _ => AddParticipant(true);
    
    function AddParticipant(e) {
      if (!e) {
        const webAppsUrl = "https://script.google.com/macros/s/###/dev"; // Please set your Web Apps URL.
        UrlFetchApp.fetch(webAppsUrl, { headers: { authorization: "Bearer " + ScriptApp.getOAuthToken() } });
        return;
      }
      var spreadsheet = SpreadsheetApp.getActive();
      var thisSheet = spreadsheet.getActiveSheet();
      var sourceRange = thisSheet.getRange(7, 1, 1, thisSheet.getMaxColumns()).activate();
      //the ◇ char is the place before wich the copy must be inserted
      var testCol = thisSheet.getRange(1, 2, thisSheet.getMaxRows()).getValues();
      var pos = 0;
      for (var i = 0; i < testCol.length; i++) {
        if (testCol[i] == "◇") {
          break;
        }
        pos = i + 2;
      }
      var destRange = thisSheet.getRange(pos, 1).activate();
      thisSheet.insertRowsBefore(thisSheet.getActiveRange().getRow(), 1);
      sourceRange.copyTo(destRange, SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
    }
    
    // ### Please don't remove the following comment line.
    // DriveApp.getFiles(); // This comment line is used for automatically detecting a scope of "https://www.googleapis.com/auth/drive.readonly". This scope is used for requesting Web Apps.
    // ###
    

    3. Testing.

    When you click the button assigned with AddParticipant, the script is run. And, the script is run by Web Apps. By this, when the users who are not the owner of Spreadsheet are running the script, the script is run.

    References: