Interacting with activeSheet, activeCell in a stand-alone script - google-apps-script

I created a google sheet that stores info row by row that I send out in different emails as templates. I'd like to give a copy of the file to my colleagues, but I'm figuring out it's probably better from a version-control standpoint to deploy the script as an add-on so they can install it individually, yet I retain centralized control of the code. However, my code uses a decent amount of ActiveSpreadsheet() and ActiveSheet() and activeCell() functions that apparently only container-bound scripts can use.
Anyone have any recommendations as to how to replace the following lines in a stand-alone script? Also any commentary on my idea to change my script to a stand-alone script to deploy as an add-on would be appreciated!
var sheet = SpreadsheetApp.getActiveSheet();
var scriptRow = sheet.getActiveCell().getRow();
var tst1 = SpreadsheetApp.getActiveSpreadsheet().toast('Draft create failed');

Try this instead of the above code:
var sheet = SpreadsheetApp.openById("your-spreadsheet-id").getSheetByName("your-table-name");
var scriptRow = sheet.getActiveCell().getRow();
var tst1 = sheet.toast('Draft create failed');
It's hard to say if an add-on would be good. Depends on the exact work the script is doing: You can either publish it as Add-on, Web app or API.

Related

Web App from Standalone script to be opened in multiple identical google spreadsheets

Help! I have one standalone script, which I want to be called in 50 separate google spreadsheets onOpen of each of these spreadsheets. I need a web app or something that will run this script as myself, not the user opening one of those 50 sheets, as the script is referring to some other files, to which the users do not have access. **My problem I do know how to make this standalone script run when one of the 50 files is opened - should I put some code (like doGet()) in my spreadsheet bound script orwhere & what???**And should I put something in my standalone script? And can I do this avoiding creating HTML pages etc.? Thanks!
My standalone script that I want to apply to my multiple spreadsheets (in short it gets a name from a cell from the opened spreadsheet, creates a copy of a separate master spreadsheet, put the saved name to the master spsheet, recalculates there, put its back to the open spreadsheet (where the name was initially):
var masterFileId = "1i-------------------DM";
var tempHolderFolder = "1D---------------ae";
function ImportIndReport2(personalFileId) {
var mf = SpreadsheetApp.openById(masterFileId);
var destFolder = DriveApp.getFolderById(tempHolderFolder);
var indCopyTest = DriveApp.getFileById(mf.getId()).makeCopy("test temp2", destFolder);
var URLCopy = indCopyTest.getUrl();
var currentIdToUse = SpreadsheetApp.openById(personalFileId).getSheetByName('Welcome').getRange('D6').getValue();
SpreadsheetApp.openById(indCopyTest.getId()).getSheetByName("Welcome").getRange("C6").setValue(currentIdToUse);
var now = new Date();
SpreadsheetApp.openById(indCopyTest.getId()).getSheetByName("jj").getRange("A1").setValue(now);
var oldSheet = SpreadsheetApp.openById(personalFileId).getSheetByName("jj");
SpreadsheetApp.openById(personalFileId).deleteSheet(oldSheet);
var CurrentIdDestination = SpreadsheetApp.openById(personalFileId);
var indReportToCopy = SpreadsheetApp.openById(indCopyTest.getId()).getSheetByName('jj');
SpreadsheetApp.openById(indCopyTest.getId()).insertSheet('Temp');
var tempDestination = SpreadsheetApp.openById(indCopyTest.getId());
indReportToCopy.getRange("A1:Z30").copyTo(tempDestination.getSheetByName('Temp').getRange("A1:Z30"),SpreadsheetApp.CopyPasteType.PASTE_VALUES,false);
indReportToCopy.getRange("A1:Z30").copyTo(tempDestination.getSheetByName('Temp').getRange("A1:Z30"),SpreadsheetApp.CopyPasteType.PASTE_FORMAT,false);
tempDestination.getSheetByName('Temp').copyTo(CurrentIdDestination).setName('jj');
var tempDestinationForDelete = DriveApp.getFileById(indCopyTest.getId());
tempDestinationForDelete.setTrashed(true);
var html = HtmlService.createHtmlOutput("<div>The report has been updated</div>").setSandboxMode(HtmlService.SandboxMode.IFRAME).setWidth(150).setHeight(100);
SpreadsheetApp.getUi().showModalDialog(html, "Message");
}
Updated - solved it with clickable url of the webapp script in the individual spreadsheet bound script, and adding simple doGet() with name of my main function (indreport..) to standalone script.
Instead of using a "web app" it might be better to use an installable on open trigger, but installable triggers are limited to 20 per script / user.
You might create three stand alone projects to hold the triggers and put the main code on a fourth stand alone project to be used as library on the three projects.
Another option is to create and add-on. This might imply extra work but it will be done only once and will be easier to maintain.
Reference
https://developers.google.com/apps-script/guides/triggers/installable

How do I execute a google script as 'owner' of a spreadsheet the user cannot view

Hopefully this is quite a simple question!
I've made a Google Script that writes to cells in a separate sheet "MasterSheet" (helped by several useful Q&As from here). This will ultimately be deployed embedded to multiple different sheets that I'm giving to individual users.
It works perfectly when the user has edit permissions on "MasterSheet", but I need that to remain private - i.e. not even viewable to anyone but me.
As background: In each 'user sheet', IMPORTRANGE is used to import the columns from 'MasterSheet' that that user is allowed to view, and then the script allows the user to add a comment to the MasterSheet.
I can view MasterSheet to see all the columns with comments from various users on one unified sheet, but the individual users shouldn't be able to view this.
The specific script for writing to the sheet is fairly generic:
function saveCommentToMasterSheet(form){
var company = form.company,
contact = form.contactselect,
comment = form.message ,
ss = SpreadsheetApp.openById("MASTERSHEET_ID").getSheetByName('MasterSheet');
if(company=="all"){
var row = findCell(contact);
} else {
var row = findCell2(contact,company);
}
//^The above finds the specific row number relating to the entry the user wants to comment on.
var cell2 = ss.getRange(row,11);
// ^In this case '11' is the column related to this sheet's specific user, I've made separate sheets for each user that are identical except this column number
cell2.setValue(comment);
}
I believe that I could make MasterSheet editable to anyone with the link, but I'd rather avoid that, particularly as the script is embedded in each spreadsheet so if the users just looked at it they'd find the MasterSheet id.
I understand that it's possible to run a script as me using the execute API, but if I'm honest, I struggled a little to figure out make that work.
Sorry if I'm asking a simple question - I've given it a good search and can't figure it out.
Many thanks!
Alex
N.B. This Running a google script from within a spreadsheet, but as a different user? looks like a similar question, but I'd really like to keep the comment system within the user's spreadsheet.
You can make a POST request to the master spreadsheet from the spreadsheets distributed to the users:
Apps Script Documentation - UrlFetchApp.fetch()
function saveCommentToMasterSheet(form) {//Function in the
//spreadsheets distributed to the users
var options,responseCode,url;
url = "https://script.google.com/macros/s/File_ID/exec";//Get from publishing
options = {};
options.method = 'post';
options.payload = form;
responseCode = UrlFetchApp.fetch(url, options).getResponseCode();
Logger.log('responseCode: ' + responseCode);//View the Logs
};
The above code will trigger the doPost(e) function in the master spreadsheet, and put the data into the event object e.
Then you can get the data out of e and write the data directly to what is the active spreadsheet, which is the master spreadsheet. Publish the Web App to run as "Me".
There are two versions of the published Web App; the "dev" version and the "exec" version. The "dev" version is always live with the latest changes, but should never be used in production. The "exec" version has a new version every time that you publish a the script again. To use the latest "exec" version in production, you must keep publishing the latest code.

How do I convert this google script to work offline?

I am trying to automate data entry while roaming offline using my Chromebook.
I know that google drive is enabled offline and a standalone script in GAS should in theory do the trick but im not sure how to put the pieces together. So far I have the below code which works perfectly online (gets stuck in "running" offline) and I've got the GAS app installed. Any guidance would be greatly appreciated!
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('Invoice/Receipt System')
// creates a menu item "Submit Order"
.addItem('Record Invoice', 'menuItem1')
.addToUi();
}
function menuItem1() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var is = ss.getSpreadsheetByName("Template_Invoice");
var lastID = is.getRange("j6");
var nextID = is.getRange("j7");
var lastIDValue = lastID.getValue();
var source = ss.getSpreadsheetByName("Key_Invoice");
// sets the 'Key_DailyInput' Sheet as source
var target = ss.geSpreadsheetByName("DataBase_Invoice");
// sets 'Key_DailyInput' sheet as the target for copying data to.
var sourceData = source.getSheetValues(5,1,source.getLastRow(),15);
// sets range to gather source 'Key_DailyInput' data by finding last row, and Line 5 to Column 15
target.getRange(target.getLastRow()+1, 1, sourceData.length,15).setValues(sourceData);
// finds last row of target 'Orders' and writes to +1 row past last row up to column 15 using setValues of sourceData
// Following simply clears DailyInput so new data can be entered
is.getRange('C5:c8').clearContent();
is.getRange('G7:G8').clearContent();
is.getRange('B12:h28').clearContent();
is.getRange('b31:b34').clearContent();
// increases value by +1 so next Sales Order ID is incremented by 1
var cell = is.getRange("j6");
var cellValue = cell.getValue();
cell.setValue(cellValue + 1);
nextID.setValue(lastIDValue + 1);
}
As stated in other responses, the answer appears to be 'No'. However, while researching I did find the Command Line Interface for Apps Script (clasp) to manage and edit your projects offline. I'll post it here hoping it will be helpful to Apps Script developers.
CLASP Features
Develop Locally. clasp lets you write code on your own computer and upload it to Apps Script via command line when you're done. You can also download existing Apps Script projects and then edit them locally. Once the code is local, you can use your favorite development tools like git to work on Apps Script projects.
* Manage Deployment Versions.
* Create, update, and view multiple deployments of your project.
* Structure Code. clasp automatically converts your flat project on script.google.com into folders.
You can find more information on clasp at https://developers.google.com/apps-script/guides/clasp. However, you'll also need to activate the Linux(beta) on your Chromebook utilizing these instructions.
Short answer
Google Apps Script can't be ran offline because they run on the server-side.
Explanation
From https://developers.google.com/apps-script/overview
Google Apps Script is a scripting language based on JavaScript that
lets you do new and cool things with Google Apps like Docs, Sheets,
and Forms. There's nothing to install — we give you a code editor
right in your browser, and your scripts run on Google's servers.

Using UrlFetchApp in a container-bound script to run code in a web app as effective user

In a little over my head. Trying to use UrlFetchApp.fetch(url, options) from a container-bound script to run code in a web app.
The problem: Container-bound doc script can only run as the activeUser. doc script creates a new doc by copying a template. I would like the doc newly created from the template to be stored in a centralized folder owned by the developer. I see two solutions.
I give all domain users view/edit access to the developer's folder.
I create a web app from a standalone script which runs as the effectiveUser (developer) who has access to the folder. In this case the doc script calls the web app using UrlFetchApp passing in the parameters (folder, doc). However, to quite able to figure out how to do this, if possible.
var unitId = DocumentApp.getActiveDocument().getId();
var unit = DriveApp.getFileById(unitId);
var folderId = unit.getDescription() //FoldId for unit is stored in description
var folder = DriveApp.getFolderById(folderId);
var lesson = DriveApp.getFileById(UNIT.LESSON_TEMPLATE_ID).makeCopy('Lesson - ' + result.getResponseText());
folder.addFile(lesson);//Currently I have the folder shared/edit with domain users.
//I would prefer to share/view. However, since the
//container-bound doc script runs only as active user, no
//can do. Is it possible to build a small web app which
//runs as effective user and saves lesson to folder.
showLessonSidebar(folderId);
Any hints out there?
Yes, it is possible to create a web app that runs as effective user.If the developer - effective user has access to folder and template file.
You could pass the folderId and Name of the file to be copied to the WebApp, rather than passing folder and Doc object (passing the folder Object and Doc object as URL parameter may be quite tricky and if content is too large may not be even possible as there are constraints on the possible length of URL)
1) Write a Apps Script
function doGet(e) {
var folderId = e.parameters.folderId;
var copyFileName = e.parameters.name;
var userEmail = e.parameters.email;
var folder = DriveApp.getFolderById(folderId);
var lesson = DriveApp.getFileById(UNIT.LESSON_TEMPLATE_ID).makeCopy(copyFileName);
folder.addFile(lesson);
//share file to domain or share folder with domain - folder.setSharing(....);
file.setSharing(DriveApp.Access.DOMAIN, DriveApp.Permission.VIEW);
}
2) While deploying the Web App, developer can set Execute as me(developer#...) and set "Who has access to Web App" as Anyone.
3) Make sure developer has authorized the web App.
4) Share the Web App Script file with all your domain users.
Also, if you want to pass the content of file for some reason from container-script to Web App, you may consider implementing the logic in doPost() as you would have the ability to pass large data using POST in UrlFetch.
As far as I understand, the only way to use UrlFetch in a bound script, is using an installed trigger. (UrlFetch is not authorized to run when used in a bound script's simple triggers: https://developers.google.com/apps-script/add-ons/lifecycle#authorization_modes.)
An installed trigger always "runs with the authorization of the user who created the trigger, even if another user with edit access opens the spreadsheet". Thus, simply use installed triggers:
function setupTrigger(){
var doc = DocumentApp.getActiveDocument();
ScriptApp.newTrigger('open').forDocument(doc).onOpen().create();
}
This sample code creates an installable trigger for opening the document bound to the script.

getUserLoginId() doesn't for collaborators

getUserLoginId() works for me, but not my collaborators. We all have edit permission. We all have gmail accounts and login as such. The following function is installed as an onEdit trigger. What's the problem? I took a look at this link: Google script: getUser() not working which seems to have a related problem. In there, they talk about "logging into the api". So I followed that road. Seriously? Authentication to find out who is editing a shared spreadsheet? If that is in fact the path to salvation, does anyone have the code written to perform such a task? Or am I doomed to spend another week or more banging my head to find out the name of the collaborator I gave permission to modify my spreadsheet, that is actually modifying my spreadsheet?
function happyFunTime() {
var s = SpreadsheetApp.getActiveSheet();
var r = s.getActiveCell();
var timeLastCalledColumnOffset = getTimeLastCalledColumnOffset();
timeLastCalledColumnOffset++;
var dateCell = s.getRange(r.getRow(), timeLastCalledColumnOffset);
var targetSheetName=setRowColor(i);
var user=Session.getActiveUser().getUserLoginId();
var comment="Last Edited By: " + user;
dateCell.setComment(comment);
}
Session.getActiveUser() only works for users within a particular domain. Or if your script is deployed as a web app and set to execute as the user running the app.
In your case, neither of them is true. So, I'm afraid, you're out of luck.