I have a google spreadsheet shared with co-workers to keep track of task status. And I want to set a function through a menu to block some ranges in which the tasks were completed. The protective permissions are set to only allow me (owner) to edit it.
I've built a standalone script to run as me for this shared spreadsheet by using UrlFetch. I've tried many kinds of codes but it didn't work. The below are 3 versions I tried.
And, of course, I set "Who has access to the app: Anyone, even anonymous" in the standalone script.
Photo 1: Standalone script setting
Thanks so much!
1st version: Without using Content Service
//In standalone script
function doGet(e){
var taskRange = e.parameter.taskRange;
var ss = SpreadsheetApp.openById('SHARING_SPREADSHEET_ID');
var sh = ss.getSheetByName('Sheet1');
var protection = sh.getRange(taskRange).protect().setDescription('Completed Task')
var result = protection.removeEditors(protection.getEditors())
return result
}
//In sharing spreadsheet script
var url = "https://script.google.com/macros/s/STANDALONE_SCRIPT_ID/exec"
function onOpen(){
SpreadsheetApp.getUi().createMenu('TASK')
.addItem('Protect Task', 'protectTask')
.addToUi();
}
function protectTask(){
var taskRange = SpreadsheetApp.getActiveSheet().getActiveRange()
var response = UrlFetchApp.fetch(url+"?taskRange="+taskRange);
Logger.log(response);
}
2nd version: Using Content Service
//In standalone script
function doGet(e){
var taskRange = e.parameter.taskRange;
var ss = SpreadsheetApp.openById('SHARING_SPREADSHEET_ID');
var sh = ss.getSheetByName('Sheet1');
var protection = sh.getRange(taskRange).protect().setDescription('Completed Task')
var result = protection.removeEditors(protection.getEditors())
return ContentService.createTextOutput(result)
}
//In sharing spreadsheet script
var url = "https://script.google.com/macros/s/STANDALONE_SCRIPT_ID/exec"
function onOpen(){
SpreadsheetApp.getUi().createMenu('TASK')
.addItem('Protect Task', 'protectTask')
.addToUi();
}
function protectTask(){
var taskRange = SpreadsheetApp.getActiveSheet().getActiveRange()
var response = UrlFetchApp.fetch(url+"?taskRange="+taskRange).getContentText();
Logger.log(response);
}
3rd version: Using Content Service with JSON
//In standalone script
function doGet(e){
var taskRange = e.parameter.taskRange;
var ss = SpreadsheetApp.openById('SHARING_SPREADSHEET_ID');
var sh = ss.getSheetByName('Sheet1');
var protection = sh.getRange(taskRange).protect().setDescription('Completed Task')
var result = protection.removeEditors(protection.getEditors())
return ContentService.createTextOutput(JSON.stringify(result)).setMimeType(ContentService.MimeType.JSON)
}
//In sharing spreadsheet script
var url = "https://script.google.com/macros/s/STANDALONE_SCRIPT_ID/exec"
function onOpen(){
SpreadsheetApp.getUi().createMenu('TASK')
.addItem('Protect Task', 'protectTask')
.addToUi();
}
function protectTask(){
var taskRange = SpreadsheetApp.getActiveSheet().getActiveRange()
var response = UrlFetchApp.fetch(url+"?taskRange="+taskRange).getContentText();
var result = JSON.Parse(response)
Logger.log(result);
}
One problem is this line:
var taskRange = SpreadsheetApp.getActiveSheet().getActiveRange()
Should be:
var taskRange = SpreadsheetApp.getActiveSheet().getActiveRange().getA1Notation();
Currently, the variable taskRange is a "class" It's not a string. You need to send a string.
FINALLY IT WORKS. I think the script was not running properly because I modified many times with many kinds of code. Therefore, I've created a new one and used the "stringified" log as #SandyGood suggested.
Many thanks to you all, specially, #SandyGood.
FINAL SCRIPTS
//In standalone script
function doGet(e){
var taskRange = e.parameter.taskRange;
var ss = SpreadsheetApp.openById('SHARING_SPREADSHEET_ID');
var sh = ss.getSheetByName('Sheet1');
var protection = sh.getRange(taskRange).protect().setDescription('Completed Task');
var result = protection.removeEditors(protection.getEditors());
var resultStr = JSON.stringify(result);
Logger.log('resultStr: ' + resultStr);
}
//In sharing spreadsheet script
var url = "https://script.google.com/macros/s/STANDALONE_SCRIPT_ID/exec"
function onOpen(){
SpreadsheetApp.getUi().createMenu('TASK')
.addItem('Protect Task', 'protectTask')
.addToUi();
}
function protectTask(){
var taskRange = SpreadsheetApp.getActiveSheet().getActiveRange().getA1Notation();
var response = UrlFetchApp.fetch(url+"?taskRange="+taskRange).getContentText();
Logger.log(response);
}
Related
I'm on a project in which I get stuck just on the final step.
let me explain:
my project to filter data and move the filtered data to another spreadsheet. All work properly without issues but something happened and the issue is that I need to input dynamic data as filter and sheet name. I created 2 variables location which will determine the filter and sectionSelect which will determine the sheet name.
my goal is to send the data through the web with its tag to be filtered in the desired sheet.
FYI: the app script is bound to a gsheet.
Here is the code:
function doGet(e) {
var ss = SpreadsheetApp.openByUrl("sheetURL")
var sheet = ss.getSheetByName(Location);
return TagData(e,sheet);
}
function doPost(e) {
var ss = SpreadsheetApp.openByUrl("sheetURL")
var sheet = ss.getSheetByName(Location);
return TagData(e,sheet);
}
function TagData(e) {
var Location = e.parameter.Location; // send from app with respective tag
var sectiontSelect = e.parameter.sectiontSelect; // send from app with respective tag
sheet.append([Location, sectiontSelect])
}
function FilterOnText() {
var ss = SpreadsheetApp.getActive()
var range = ss.getDataRange();
var filter = range.getFilter() || range.createFilter()
var text = SpreadsheetApp.newFilterCriteria().whenTextContains(Location); // the location will be as per the location variable in the TagData function
filter.setColumnFilterCriteria(1, text);
}
function titleAsDate() {
const currentDate = Utilities.formatDate(new Date(), "GMT+4", "dd-MM-yyyy HH:mm:ss");
return SpreadsheetApp.create("Report of the " + currentDate);
}
function copyWithValues() {
const spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
const sourceSheet = spreadSheet.getSheetByName(sectionSelect); // fromApp variable will define which sheet the data will be copied (situated in the TagData function)
const temp_sheet = spreadSheet.insertSheet('temp_sheet');
const sourceRange = sourceSheet.getFilter().getRange();
sourceRange.copyTo(
temp_sheet.getRange('A1'),
SpreadsheetApp.CopyPasteType.PASTE_NORMAL,
false);
SpreadsheetApp.flush();
const sourceValues = temp_sheet.getDataRange().getValues();
const targetSpreadsheet = titleAsDate();
const rowCount = sourceValues.length;
const columnCount = sourceValues[0].length;
const targetSheet = targetSpreadsheet.getSheetByName('Sheet1').setName("Report"); // renamed sheet
const targetRange = targetSheet.getRange(1, 1, rowCount, columnCount);
targetRange.setValues(sourceValues);
spreadSheet.deleteSheet(temp_sheet);
}
function MoveFiles(){
var files = DriveApp.getRootFolder().getFiles();
var file = files.next();
var destination = DriveApp.getFolderById("1wan7PLhl4UFEoznmsN_BVa2y4AtFaCOr");
destination.addFile(file)
var pull = DriveApp.getRootFolder();
pull.removeFile(file);
}
function clearFilter() { // clearance of filters applied in first function
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("testo");
sheet.getFilter().remove();
}
Explanation:
Two issues:
TagData does not return anything (as Cooper pointed out)
TagData(e) has only one parameter e but you are passing two parameters TagData(e,sheet) when you call it from the doGet and doPost functions.
Not sure if this will solve all your issues, but it will solve the ones I mentioned above.
Solution:
Remove the return statements from doGet and doPost because of issue 1).
Add the sheet parameter in the TagData function.
Resulting code:
function doGet(e) {
var ss = SpreadsheetApp.openByUrl("sheetURL")
var sheet = ss.getSheetByName(Location);
TagData(e,sheet); // modified
}
function doPost(e) {
var ss = SpreadsheetApp.openByUrl("sheetURL")
var sheet = ss.getSheetByName(Location);
TagData(e,sheet); // modified
}
// added the sheet parameter
function TagData(e,sheet) {
var Location = e.parameter.Location; // send from app with respective tag
var sectiontSelect = e.parameter.sectiontSelect; // send from app with respective tag
sheet.append([Location, sectiontSelect]);
}
// rest of your code..
Or try this:
function doGet(e) {
var ss = SpreadsheetApp.openByUrl("sheetURL")
var sheet = ss.getSheetByName(Location);
var params = JSON.stringify(TagData(e,sheet)); // modified
return HtmlService.createHtmlOutput(params); // added
}
function doPost(e) {
var ss = SpreadsheetApp.openByUrl("sheetURL")
var sheet = ss.getSheetByName(Location);
ContentService.createTextOutput(JSON.stringify(e))
}
// added the sheet parameter
function TagData(e,sheet) {
var Location = e.parameter.Location; // send from app with respective tag
var sectiontSelect = e.parameter.sectiontSelect; // send from app with respective tag
sheet.append([Location, sectiontSelect]);
return { Location: Location, sectiontSelect: sectiontSelect }
}
I tried both GET and POST like the below, but Logger.log() prints only undefined.
function doGet(e){
Logger.log(JSON.stringify(e))
Logger.log(e)
var ss = SpreadsheetApp.openByUrl("MY_URL");
var sheet = ss.getSheetByName("list");
return getList(sheet);
}
function doPost(e){
Logger.log(JSON.stringify(e))
Logger.log(e)
var ss = SpreadsheetApp.openByUrl("MY_URL");
var sheet = ss.getSheetByName("list");
return getList(sheet);
}
And also tried this:
function doGet(e){
Logger.log(JSON.stringify(e.parameter))
Logger.log(e.parameter)
var ss = SpreadsheetApp.openByUrl("MY_URL");
var sheet = ss.getSheetByName("list");
return getList(sheet);
}
function doPost(e){
Logger.log(JSON.stringify(e.parameter))
Logger.log(e.parameter)
var ss = SpreadsheetApp.openByUrl("MY_URL");
var sheet = ss.getSheetByName("list");
return getList(sheet);
}
This one says it can't read parameter property because e is undefined.
I am calling APIs from Postman like this:
https://script.google.com/macros/s/MY_KEY/exec
and tried sending params with body(for POST) and Params(for GET).
And the apps script doesn't get any e.
How can I solve this problem?
I'm looking to modify this function so I don't actually need to click a Download Now link. I'd just like the script to download the sheet automatically. Appreciate any help. Thank you.
function copySheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var first = ss.getSheetByName("Road MT");
ss.rename(first.getRange(2, 14).getValue());
// this would change the name to whatever is in Row 2, col 14
var myValue =
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange("N2")
.getValue();
var copiedSpreadSheet =
SpreadsheetApp.getActiveSpreadsheet().copy(myValue);
var ssID = SpreadsheetApp.getActive().getId();
var URL = 'https://docs.google.com/spreadsheets/d/'+ssID+'/export?
format=xlsx';
// Display a modal dialog box with download link.
var htmlOutput = HtmlService
.createHtmlOutput('Download Now')
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setWidth(300)
.setHeight(40);
SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'Download XLS');
}
You can do that, but you need to invoke the download from doGet or doPost like this:
function doGet(fileId) {
var URL = 'https://docs.google.com/spreadsheets/d/'+fileId+'/export?format=xlsx';
var fileName = 'download.xlsx';
var blob = DriveApp.getFileById(fileId).getBlob();
return ContentService.createTextOutput(blob).downloadAsFile(fileName);
}
I got the problem since google isnt supporting UIapi anymore i cant use the code below. Could someone help me with it and re-edit to html service? I have no clue about any of those stuff. Code was copied from other site long time ago. Tryed to find a solution for the last 2 days and nothing. Would be really greatfull.
regards
// upload document into google spreadsheet
// and put link to it into current cell
function onOpen(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var menuEntries = [];
menuEntries.push({name: "", functionName: "doGet"});
ss.addMenu("", menuEntries);
}
function doGet(e) {
var app = UiApp.createApplication().setTitle("");
SpreadsheetApp.getActiveSpreadsheet().show(app);
var form = app.createFormPanel().setId('frm').setEncoding('multipart/form-data');
var formContent = app.createVerticalPanel();
form.add(formContent);
formContent.add(app.createFileUpload().setName('thefile'));
// these parameters need to be passed by form
// in doPost() these cannot be found out anymore
formContent.add(app.createHidden("activeCell", SpreadsheetApp.getActiveRange().getA1Notation()));
formContent.add(app.createHidden("activeSheet", SpreadsheetApp.getActiveSheet().getName()));
formContent.add(app.createHidden("activeSpreadsheet", SpreadsheetApp.getActiveSpreadsheet().getId()));
formContent.add(app.createSubmitButton(''));
app.add(form);
SpreadsheetApp.getActiveSpreadsheet().show(app);
return app;
}
function doPost(e) {
var app = UiApp.getActiveApplication();
app.createLabel('');
var fileBlob = e.parameter.thefile;
var doc = DriveApp.getFolderById('0BzI2pkyLXZ5maWo5b2Uyb3JWdzQ').createFile(fileBlob);
var label = app.createLabel('');
// write value into current cell
var value = 'hyperlink("' + doc.getUrl() + '";"' + doc.getName() + '")'
var activeSpreadsheet = e.parameter.activeSpreadsheet;
var activeSheet = e.parameter.activeSheet;
var activeCell = e.parameter.activeCell;
var label = app.createLabel('');
app.add(label);
SpreadsheetApp.openById(activeSpreadsheet).getSheetByName(activeSheet).getRange(activeCell).setFormula(value);
app.close();
return app;
}
I am trying to to create an Add On where the user has the option to either choose from the menu, or an onEdit command triggers a change to the cell in sheets. When I use the code below, attached to the sheet it works perfectly, however, when i test it as AUTH-LIMITED (Enabled or installed & enabled) the onEdit(e) functionality doesn't work. Everything about the menu button is working great, I can't figure out how to get the onEdit(e) called, what so ever.
I've tried searching all over but to no success with how to solve for this specific issue.
Thank you in advance!
function onEdit(e){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheeter = ss.getActiveSheet();
sheeter.getRange("a1").setValue("Test");
var range = e.range;
var val = range.getValues();
if(val.length+val[0].length<=2){
var val = range.getValue();
if(range.getFormula()){}
else{
var regex2 = new RegExp('[0-5]{0,1}[0-9]:[0-5]{0,1}[0-9].[0-5]{0,1}[0-9]$','g');
var docContent2 = val.replace(regex2,"00:"+val);
range.setValue(docContent2);
range.setNumberFormat("[M]:SS.0");
}
}
}
function Mass_Convert(){
var now = Date.now();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var range = sheet.getActiveRange();
var val = range.getValues();
var replaceBox = new Array(val.length);
var replaceFormat =range.getNumberFormats();
for(var i=0;i<val.length;i++){
replaceBox[i] = new Array(val[i].length);
replaceFormat[i] = new Array(val[i].length);
for(var j=0;j<val[0].length;j++){
var newRange = range.getCell(i+1,j+1);
var newVal = newRange.getValue();
var format = newRange
if(newRange.getFormula()){
replaceBox[i][j]=newRange.getFormula();
replaceFormat[i][j] = newRange.getNumberFormat();
}
else{
if(isNaN(newVal)){
var regex2 = new RegExp('[0-5]{0,1}[0-9]:[0-5]{0,1}[0-9].[0-5]{0,1}[0-9]$','g');
var docContent2 = newVal.replace(regex2,"00:"+newVal);
replaceBox[i][j] = docContent2;
replaceFormat[i][j] = "[M]:SS.0"
}else{
replaceBox[i][j] = newVal;
replaceFormat[i][j] = newRange.getNumberFormat();
}
}
}
}
range.setValues(replaceBox);
range.setNumberFormats(replaceFormat);
}
function onOpen(e) {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Split Conversion')
.addItem('Mass Convert Selected', 'Mass_Convert')
.addToUi();
}
function onInstall(e) {
onOpen(e);
}
I tested this with my add-on and .getActiveSpreadsheet() gives the error:
Execution failed: The Add-on attempted an action that is not permitted in Test as Add-on mode. To use this action, you must deploy the Add-on
Also your onEdit() might have permission issues. There should be some condition to run on specific sheets otherwise it will run on all sheets.
I also found this:
onEdit(e) not working in Add-on