Google Apps Script Undo function - google-apps-script

I am setting up a spreadsheet and I need to protect cells on copy. As Google Sheets doesn't copy protection when a new sheet is copied, I came across this code. It is an undo function but it only works with text and not formula. Any ideas on how to change it?
I have changeed .getValue to .getFormula but with no luck.
var masterSheetName = "Master" // sheet where the cells are protected from updates
var helperSheetName = "Backup" // sheet where the values are copied for later checking
var ss = SpreadsheetApp.getActiveSpreadsheet();
var masterSheet = ss.getActiveSheet();
if (masterSheet.getName() != masterSheetName) return;
var masterRange = masterSheet.getActiveRange();
var helperSheet = ss.getSheetByName(helperSheetName);
var helperRange = helperSheet.getRange(masterRange.getA1Notation());
var newValue = masterRange.getValues();
var oldValue = helperRange.getValues();
Logger.log("newValue " + newValue);
Logger.log("oldValue " + oldValue);
Logger.log(typeof(oldValue));
if (oldValue == "" || isEmptyArrays(oldValue)) {
helperRange.setValues(newValue);
} else {
Logger.log(oldValue);
masterRange.setValues(oldValue);
}
}

Related

Google Sheet Appscript only collects my username and no other users

I have a changelog script that only collects my username and does not collect for any other user. I'm trying to figure out the bug but unable to find a solution.
function changelog_script(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetNameTracker = ss.getActiveSheet().getName();
if (sheetNameTracker !== "Changelog") {
let sheet = ss.getSheetByName('Changelog');
var range = e.range && e.range.getA1Notation();
var user = e.user.getUsername();
var function_source = "changelog_script";
var time = new Date();
var changeType = e.changeType || 'EDIT';
console.log(user);
var oldValue = e.oldValue;
var newValue = e.value;
var lastRow = sheet.getLastRow();
sheet.getRange(lastRow + 1, 1, 1, 8).setValues([
[time.toLocaleString(), function_source, changeType, sheetNameTracker, range, user, oldValue, newValue]]);
}
}
function onChange(e) {
if (e.changeType == "EDIT") return;
changelog_script(e);
}
Example Google Sheet with attached App script.
Thanks in advance. 🙏

Setting data protection on cell range only to be edited by specified account

I'm creating a fun little game in Google Sheets -- the part of code I'm working on now is basically locking the row after a specific cell in that row has been edited (essentially, not letting the user to go back and edit anything before that cell).
My full code is below, but I'm looking for a fix with lines specific to the protections. I want my sheet to add a data range that only allows the email amadle#strengthinsheets.com edit access. However, I'm testing with different accounts, and it seems the protection script is allowing the user to edit (not locking to the specific account).
Is something wrong with this code block? I basically just want to add a protection on the range that doesn't let anyone other than a specific user edit.
function onEdit(e) {
var oldText = "-";
var newText = "=";
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var ss = spreadsheet.getActiveSheet();
// var cell = ss.getRange('AB1').getValue();
var answer = ss.getRange("AA2").getValue();
var syntax = ss.getRange("AC2").getValue();
var description = ss.getRange("AC1").getValue();
var type = ss.getRange("AC3").getValue();
if (ss.getRange("AB1").getValue() == "MATCH") {
SpreadsheetApp.getUi().alert("🎉 Congratulations: "+ answer, "Type: "+type+"\n\nSyntax: "+syntax+"\n\n Description: "+description, SpreadsheetApp.getUi().ButtonSet.OK);
}
if (e.range.getA1Notation() === 'F5') {
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange("B6:N6").createTextFinder(oldText).matchFormulaText(true).replaceAllWith(newText);
**var ss = SpreadsheetApp.getActive();
var range = ss.getRange('B5:N5'); // Example of protecting Row 1
var protection = range.protect().setDescription('GUESS 1');
var me = "amadle#strengthinsheets.com";
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
}**
if (e.range.getA1Notation() === 'F7') {
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange("B8:N8").createTextFinder(oldText).matchFormulaText(true).replaceAllWith(newText);
}
if (e.range.getA1Notation() === 'F9') {
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange("B10:N10").createTextFinder(oldText).matchFormulaText(true).replaceAllWith(newText);
var hint = sheet.getRange('AC1').getValue();
SpreadsheetApp.getUi().alert("Function Hint", hint, SpreadsheetApp.getUi().ButtonSet.OK);
}
if (e.range.getA1Notation() === 'F11') {
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange("B12:N12").createTextFinder(oldText).matchFormulaText(true).replaceAllWith(newText);
}
if (e.range.getA1Notation() === 'F13') {
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange("B14:N14").createTextFinder(oldText).matchFormulaText(true).replaceAllWith(newText);
SpreadsheetApp.getUi().alert("Next Time! Correct Answer: "+ answer, "Type: "+type+"\n\nSyntax: "+syntax+"\n\n Description: "+description, SpreadsheetApp.getUi().ButtonSet.OK);
}
}

How to remove limit in creating folder in Google Drive through Spreadsheet

I wanted to create a bulk of folders in my google drive using scripts in google spreadsheet. Fortunately, I have found the script I have been looking for. However, upon executing the script, it gave a notice that (I forgot the exact message) I have reached the maximum number of folders to create. So it just stops. Here is the code. I hope someone could tell me how can I or what should I change for it to create folders as required.
function onOpen() { //This is the new standard script for the onOpen trigger that creates a menu item.
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('GDrive')
.addItem('Create new Folders', 'crtGdriveFolder')
.addToUi();
}
function crtGdriveFolder() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var levelInput = Browser.inputBox('input Level', Browser.Buttons.OK_CANCEL);
var Level = levelInput * 2 + 1;
var numRows = sheet.getLastRow(); // Number of rows to process
var dataRange = sheet.getRange(3, Number(Level)-1, numRows, Number(Level)); //startRow, startCol, endRow, endCol
var data = dataRange.getValues();
var parentFolderID = new Array();
for(var i in data)
{
parentFolderID[i] = data [i][0];
if (data [i][0] == "")
{
parentFolderID[i] = parentFolderID[i-1];
}
}
for(var i in data){
if (data [i][1] !== "")
{
var theParentFolder = DriveApp.getFolderById(parentFolderID[i]);
var folderName = data[i][1];
var theChildFolder = theParentFolder.createFolder(folderName);
// Utilities.sleep(100);
var newFolderID = sheet.getRange(Number(i)+3,Number(Level)+1);
var folderIdValue = theChildFolder.getId();
newFolderID.setValue(folderIdValue);
var addLink = sheet.getRange(Number(i)+3,Number(Level));
var value = addLink.getDisplayValue();
addLink.setValue('=hyperlink("https://drive.google.com/corp/drive/folders/'+ folderIdValue +'","' + value + '")');
}
}
}```
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/QKKLr.jpg

Preserve formula references when printing a single Google Sheets sheet to PDF

I want to accomplish the following:
Make a menu with a print to PDF button.
Have that button export a PDF to with the same name and same destination as the google sheet.
This works well, but the script I found I need to change, because most people print to PDF by making a temporary copy of the sheet, printing, and finally deleting the temporary copy.
I have references from other sheets in my original document that ends up printed as #REF! values because only the one sheet gets copied and printed, not my whole document.
How can i make this process include baking the formulas as text?
CODE FOR PRINTING:
function onOpen() {
var submenu = [{name: "Save PDF", functionName: "generatePdf"}];
SpreadsheetApp.getActiveSpreadsheet().addMenu('Export', submenu);
}
function generatePdf() {
// Get active spreadsheet.
var sourceSpreadsheet = SpreadsheetApp.getActive();
// Get active sheet.
var sheets = sourceSpreadsheet.getSheets();
var sheetName = sourceSpreadsheet.getActiveSheet().getName();
var sourceSheet = sourceSpreadsheet.getSheetByName(sheetName);
// Set the output filename as sheetName.
var pdfName = sheetName;
// Get folder containing spreadsheet to save pdf in.
var parents = DriveApp.getFileById(sourceSpreadsheet.getId()).getParents();
if (parents.hasNext()) {
var folder = parents.next();
}
else {
folder = DriveApp.getRootFolder();
}
// Copy whole spreadsheet.
var destSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(sourceSpreadsheet.getId()).makeCopy("tmp_convert_to_pdf", folder))
// Delete redundant sheets.
var sheets = destSpreadsheet.getSheets();
for (i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetName() != sheetName){
destSpreadsheet.deleteSheet(sheets[i]);
}
}
var destSheet = destSpreadsheet.getSheets()[0];
// Replace cell values with text (to avoid broken references).
var sourceRange = sourceSheet.getRange(1, 1, sourceSheet.getMaxRows(), sourceSheet.getMaxColumns());
var sourcevalues = sourceRange.getValues();
var destRange = destSheet.getRange(1, 1, destSheet.getMaxRows(), destSheet.getMaxColumns());
destRange.setValues(sourcevalues);
// Save to pdf.
var theBlob = destSpreadsheet.getBlob().getAs('application/pdf').setName(pdfName);
var newFile = folder.createFile(theBlob);
// Delete the temporary sheet.
DriveApp.getFileById(destSpreadsheet.getId()).setTrashed(true);
}
CODE FOR CONVERTING FORMULAS TO TEXT:
function formulasAsText() {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for (var k = 0; k < sheets.length; k++) {
var range = sheets[k].getDataRange();
var values = range.getValues();
var formulas = range.getFormulas();
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values[0].length; j++) {
values[i][j] = formulas[i][j] ? "'" + formulas[i][j] : values[i][j];
}
}
range.setValues(values);
}
}
To maintain formula references, you need to modify the formulasAsText() function to accept an input, and also not worry about writing a formula if one is found. This input could be a spreadsheet ID - i.e. the id of the temporary copy - or it could be an array of Sheet objects.
Once you have made these two modifications, you would call the function prior to deleting the non-desired sheets in the temporary copy:
/**
* #param Sheet[] wbSheets An array of Sheets to operate on.
* #param String toPreserve The name of the sheet which should be preserved.
*/
function preserveFormulas(wbSheets, toPreserve) {
if(!wbSheets || !wbSheets.length || !toPreserve)
throw new Error("Missing arguments.");
wbSheets.forEach(function (sheet) {
if ( sheet.getName() === toPreserve ) {
var range = sheet.getDataRange();
// Serialize the cell's current value, be it static or derived from a formula.
range.setValues(range.getValues());
}
});
}
Finished my script. Here is what i ended up with.
function onOpen() {
var submenu = [{name:"Save PDF", functionName:"generatePdf"}];
SpreadsheetApp.getActiveSpreadsheet().addMenu('Export', submenu);
}
function hideSheets() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("sheet1");
var sheet2 = ss.getSheetByName("PRINT SHEET");
var sheet3 = ss.getSheetByName("sheet3");
var sheet4 = ss.getSheetByName("sheet4");
var sheet5 = ss.getSheetByName("sheet5");
var sheet6 = ss.getSheetByName("sheet6");
var sheet7 = ss.getSheetByName("sheet7");
sheet1.hideSheet();
sheet3.hideSheet();
sheet4.hideSheet();
sheet5.hideSheet();
sheet6.hideSheet();
sheet7.hideSheet();
}
function showSheets() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("sheet1");
var sheet2 = ss.getSheetByName("PRINT SHEET");
var sheet3 = ss.getSheetByName("sheet3");
var sheet4 = ss.getSheetByName("sheet4");
var sheet5 = ss.getSheetByName("sheet5");
var sheet6 = ss.getSheetByName("sheet6");
var sheet7 = ss.getSheetByName("sheet7");
sheet1.showSheet();
sheet3.showSheet();
sheet4.showSheet();
sheet5.showSheet();
sheet6.showSheet();
sheet7.showSheet();
}
function generatePdf() {
// Get active spreadsheet.
var sourceSpreadsheet = SpreadsheetApp.getActive();
// Get active sheet.
var sheets = sourceSpreadsheet.getSheets();
var sheetName = sourceSpreadsheet.getActiveSheet().getName();
var sourceSheet = sourceSpreadsheet.getSheetByName(sheetName);
// Set the output filename as BID and number (from cell C4).
var pdfName = "BID " + sheets[0].getRange("C4").getValue() + ".pdf";
var ui = SpreadsheetApp.getUi();
// Get folder containing spreadsheet to save pdf in.
var parents = DriveApp.getFileById(sourceSpreadsheet.getId()).getParents();
if (parents.hasNext()) {
var folder = parents.next();
}
else {
folder = DriveApp.getRootFolder();
}
hideSheets();
// Save to pdf.
var theBlob = sourceSpreadsheet.getBlob().getAs('application/pdf').setName(pdfName);
var newFile = folder.createFile(theBlob);
showSheets();
ui.alert('Export finished');
}
Thanks.

Google Spreadsheet unable to deploy web app script --

I've been trying to deploy this web app bound to my shared spreadsheet. Basically it's a script that locks a cell after it's been edited. It is working just fine when I access it, however when I switch to another user and try, it doesn't function. You would think it would be a sharing permission setting, but I've changed everything to public and still no luck. Can you someone please help?
Here is the script:
function doGet(e) {
var sheet = SpreadsheetApp.openById('1234567891011');
}
function onEdit(){
var masterSheetName = "PlaceYourSquare" // sheet where the cells are protected from updates
var helperSheetName = "Helper" // sheet where the values are copied for later checking
var firstDataRow = 1; // only take into account edits on or below this row
var firstDataColumn = 1; // only take into account edits on or to the right of this column
var ss = SpreadsheetApp.getActiveSpreadsheet();
var masterSheet = ss.getActiveSheet();
if (masterSheet.getName() != masterSheetName) return;
var masterCell = masterSheet.getActiveCell();
if (masterCell.getRow() < firstDataRow || masterCell.getColumn() < firstDataColumn) return;
var helperSheet = ss.getSheetByName(helperSheetName);
var helperCell = helperSheet.getRange(masterCell.getA1Notation());
var newValue = masterCell.getValue();
var oldValue = helperCell.getValue();
if (oldValue == "") {
helperCell.setValue(newValue);
} else {
masterCell.setValue(oldValue);
}
}