Using getAuthors() for Google Apps Scripts - google-apps-script

getAuthors() in Google Apps Script returns an array, so I'm assuming it is intended to capture all people who have edited the page. However, it only returns a single value, which seems to be the person who created the page. If another person edits the page, it still returns an array containing only the first value.
Is getAuthor() intended to be limited to just the creator?
Is there any way to return an array with all of people who have worked on
a page (those who have saved edits)?
If not, is possible to change the author?
function testAuthor(){
var site = SitesApp.getSite(DOMAIN, NAME);
var decendents = site.getAllDescendants();
for (var i=0;i<decendents.length; i++){
Logger.log(decendents[i].getAuthors().join(', '));
};
}

Related

appendRow() adds blank row in google sheets (app script)

I've setup a google app script that would be triggered from an external system. This script would fetch the details from the third party system and add them to google sheet row.
function doPost(request) {
try{
var jsonString = request.postData.getDataAsString(); //get the request from KF as JSON String
setLog("\n postData*********************"+jsonString+"************************************* \n");
setLog("before the row append");
ss.appendRow([jsonString["Name"], jsonString["Age"], jsonString["Contact"]]);
setLog("After the row append");
var returnJson = '{"status": "success"}';
//used to send the return value to the calling function
setLog("/n returnJson****************************"+returnJson+"************************************* /n")
return ContentService.createTextOutput(returnJson).setMimeType(ContentService.MimeType.JSON);
}
There's absolutely no errors or warnings, but somehow it keeps adding the blank rows into the sheet.
Note: setLog() is a function where I print the values into google doc for debugging.
Maybe the reason your script is not working has to do with the value of jsonString.
I could not find any reference to request.postData.getDataAsString() inside GAS Documentation, so maybe you are trying to call a method on an object which does not support it, which would not raise an Error, but would return undefined.
One quick way to debug this would be to LOG the value (using your custom function or Logger.log(jsonString)) BEFORE you call .appendRow(). Then, you can verify if your variable has the value you expect it to have.
On the other hand, my suggestion is to use this method:
var jsonString = JSON.parse(request.postData.contents) //Gets the content of your request, then parses it
This method is present in the Documentation, and has been consistently working on all of my projects.
I think you should sort the coulmns with google app script. Write this code after ss.appendRow. The column will be sorted and all blank rows gets down.
// Sorts the sheet by the first column, ascending
ss.sort(1)
or if errors try this one also
var fl = SpreadsheetApp.getActiveSpreadsheet();
var sheet = fl.getSheets()[0];
fl.sort(1)

Google Docs Apps Script get number of columns

On a Google Doc you can set columns from Format > Columns. Now, though, I want to access those columns from Apps Script to confirm the number of columns. I don't even need to modify them, just access. I haven't found anything that jumps out at me from the documentation, so I didn't know if there's an extension of the Document service that would allow for such.
I'm sorry not to include any code, but I have no code obvious to show. I did create a document with 2 columns to see exactly what I'm talking about. https://docs.google.com/document/d/1MyttroeN4kPUm9PfYZnTe_gJqstM3Gb5q3vS3c84dNw/edit
Answer
It is possible using the get method of the Google Docs API
How to do it
In Apps Script, enable the Advanced Docs Service.
Use the method get.
Check the array called content.
Search an object called sectionBreak in each element of content.
Check that the object has the following data: sectionBreak>sectionStyle>columnProperties.
Take the length of the array columnProperties.
(keep in mind that the first occurrence of columnProperties is in the first element of content, skip it and start the loop from the second one.
Code
function myFunction() {
var id = DocumentApp.getActiveDocument().getId()
var result = Docs.Documents.get(id)
var content = result["body"]["content"]
for (var i = 1; i < 20; i++) {
try {
var cols = content[i]["sectionBreak"]["sectionStyle"]["columnProperties"]
console.log('number of columns: ' + cols.length)
} catch (error) {
}
}
}
Reference
Google Docs API
Method: documents.get
Advanced Google services
Advanced Docs Service

Return Collection of Google Drive Files Shared With Specific User

I'm trying to get a collection of files where user (let's use billyTheUser#gmail.com) is an editor.
I know this can be accomplished almost instantly on the front-end of google drive by doing a search for to:billyTheUser#gmail.com in the drive search bar.
I presume this is something that can be done in Google App Scripts, but maybe I'm wrong. I figured DriveApp.searchFiles would work, but I'm having trouble structuring the proper string syntax. I've looked at the Google SDK Documentation and am guessing I am doing something wrong with the usage of the in matched to the user string search? Below is the approaches I've taken, however if there's a different method to accomplishing the collection of files by user, I'd be happy to change my approach.
var files = DriveApp.searchFiles(
//I would expect this to work, but this doesn't return values
'writers in "billyTheUser#gmail.com"');
//Tried these just experimenting. None return values
'writers in "to:billyTheUser#gmail.com"');
'writers in "to:billyTheUser#gmail.com"');
'to:billyTheUser#gmail.com');
// this is just a test to confirm that some string searches successfully work
'modifiedDate > "2013-02-28" and title contains "untitled"');
Try flipping the operands within the in clause to read as:
var files = DriveApp.searchFiles('"billyTheUser#gmail.com" in writers');
Thanks #theAddonDepot! To illustrate specifically how the accepted answer is useful, I used it to assist in building a spreadsheet to help control files shared with various users. The source code for the full procedure is at the bottom of this post. It can be used directly within this this google sheet if you copy it.
The final result works rather nicely for listing out files by rows and properties in columns (i.e. last modified, security, descriptions... etc.).
The ultimate purpose is to be able to update large number of files without impacting other users. (use case scenario for sudden need to immediately revoke security... layoffs, acquisition, divorce, etc).
//code for looking up files by security
//Posted on stackoverlow here: https://stackoverflow.com/questions/62940196/return-collection-of-google-drive-files-shared-with-specific-user
//sample google File here: https://docs.google.com/spreadsheets/d/1jSl_ZxRVAIh9ULQLy-2e1FdnQpT6207JjFoDq60kj6Q/edit?usp=sharing
const ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("FileList");
const clearRange = true;
//const clearRange = SpreadsheetApp.getActiveSpreadsheet().getRangeByName("ClearRange").getValue();
//if you have the named range setup.
function runReport() {
//var theEmail= SpreadsheetApp.getActiveSpreadsheet().getRangeByName("emailFromExcel").getValue();
//or
var theEmail = 'billyTheUser#gmail.com';
findFilesByUser(theEmail);
}
function findFilesByUser(theUserEmail) {
if(clearRange){
ss.getDataRange().offset(1,0).deleteCells(SpreadsheetApp.Dimension.ROWS)
}
var someFiles = DriveApp.searchFiles('"' + theUserEmail + '" in writers');
var aListOfFiles = []
while(someFiles.hasNext()){
var aFile = someFiles.next();
aListOfFiles.push([aFile.getId()
,aFile.getName()
,aFile.getDescription()
,aFile.getSharingAccess()
,aFile.getSharingPermission()
,listEmails(aFile.getEditors())
,listEmails(aFile.getViewers())
,aFile.getMimeType().replace('application/','').replace('vnd.google-apps.','')
,aFile.getDateCreated()
,aFile.getLastUpdated()
,aFile.getSize()
,aFile.getUrl()
,aFile.getDownloadUrl()
])
}
if(aListOfFiles.length==0){
aListOfFiles.push("no files for " + theUserEmail);
}
ss.getRange(ss.getDataRange().getLastRow()+1,1, aListOfFiles.length, aListOfFiles[0].length).setValues(aListOfFiles);
}
function listEmails(thePeople){
var aList = thePeople;
for (var i = 0; i < aList.length;i++){
aList[i] = aList[i].getEmail();
}
return aList.toString();
}

How to appendRow (or write data more generally) to Google Sheets from a custom function

I've written a custom function [=ROUTEPLAN(origin,destination,mode,departuretime)] in the Google Sheets script editor. The function assigns a unique ID to the request, calls the Google Maps Directions API, passes as params the arguments as listed in the function, parses the JSON and extracts the duration, end latitude and end longitude for each step of the journey, and then appends a row for each step, with the request ID for the whole journey, the sequential step number, the duration, end latitude and end longitude:
function ROUTEPLAN() {
//Call the google route planner api
//(variables for api declared here but removed for brevity)
var routeResponse = UrlFetchApp.fetch("https://maps.googleapis.com/maps/api/directions/json?origin=" + origin
+ "&destination=" + destination
+ "&mode=" + mode +
"&region=uk&departure-time=" + departuretime
+ "&key=MYAPIKEY")
//Assign a unique ID to this request
var requestID = Date.now() + Math.random();
//Parse JSON from routeResponse
var json = routeResponse.getContentText();
var data = JSON.parse(json);
//Insert the RequestID, step number, duration, end Latitude and end Longitude for each step of the journey into the RouteDetails sheet
var steps = data["routes"][0]["legs"][0]["steps"];
for (i = 0; i < steps.length; i++) {
var stepID = i + 1;
var duration = steps[i]["duration"]["value"];
var endLat = steps[i]["end_location"]["lat"];
var endLng = steps[i]["end_location"]["lng"];
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("RouteDetails")
sheet.appendRow([requestID,stepID,duration,endLat,endLng]);
}
}
Or at least that's what I want it to do. It worked fine until I tinkered with it, and now I'm getting an ERROR when I call the function in the spreadsheet, telling me I don't have permission to call appendRow. I know why this is happening (although I don't understand why it wasn't happening before), but I cannot work out what I'm supposed to do about it.
If appendRow exists, there must be some circumstance in which it can be used to write data the sheet, but I can't figure out the circumstances in which permission to write to the sheet would be granted.
The purpose of the sheet is to provide data to a chatbot (the chatbot app has read & write permissions to the sheet). I'm not intending to provide access beyond that (i.e. i'm not intending to publish this for wider use). I've tried going down the installable trigger route, but despite following all the instructions that made absolutely no difference to the outcome. From the limited understanding I gained from reading about API Executables, that doesn't seem to be an option either.
Can anyone tell me how to solve this? Thank you :-)
A custom function can not modify the structure of the spreadsheet, so calling appendRow() is not allowed as stated in the documentation:
A custom function cannot affect cells other than those it returns a value to. In other words, a custom function cannot edit arbitrary cells, only the cells it is called from and their adjacent cells. To edit arbitrary cells, use a custom menu to run a function instead
If you want to return multiple rows from your function, it needs to return a two dimensional array. Note however that custom functions have the same limitation as native functions of not being able to overwrite content i.e. if you try to return two rows but the row below is already filled the function will error out.

How to fill out multiple sheets everytime a Google Form is submitted

I have created a very simple Apps Script that based on the answers provided through a Google Form, makes a copy of a template (a sheet in the form answers spreadsheet) and fills it out with the entered info. This is part of the code:
function fichas() {
var formato = SpreadsheetApp.getActiveSpreadsheet();
var ficha = formato.duplicateActiveSheet();
var respuestas = SpreadsheetApp.openById('ID').getSheetByName('Form Responses');
var name = respuestas.getRange(**2**, 5);
var apellido = ficha.getRange(4, 2);
name.copyTo(apellido);
var name2 = respuestas.getRange(**2**, 6);
var apellido2 = ficha.getRange(4, 3);
name2.copyTo(apellido2);
The bold number represents the first user's information. I have set up a trigger that runs the script when the form is submitted. Now, what I don't know how to do is how to move to the next row to use the next user's info (in other words I want to automatically increase that bold number by one everytime the script runs). Is it possible? I am new to this and I am trying to learn but sometimes it is too hard!
I really appreciate your help!
Jorge
You should use a variable that is common to all users and that you increment on each form submission. There are a few possible ways to achieve that, why not try script Properties ? it is fairly simple to implement :
First initialize it with a statement like this :
ScriptProperties.setProperty('rowValue', 1);// this must happen only once, you could insert it in a small function to "initialize"
and then in your code you can retrieve it using
var rowValue = Number(ScriptProperties.getProperty('rowValue'));
increment it using rowValue++ and write it back to the storage ... remember that you can always edit this value directly in the script properties (script editor>file>project properties> project properties)
ScriptProperties.setProperty('rowValue', rowValue);
This would be perfect if you never get more than one user sending a form simultaneously.
In this case there is a risk that the script mixes values...
There is another service designed to handle this situation : the lock service
the doc gives an explicit example on how to implement it : it goes simply like this :
var lock = LockService.getPublicLock();
try {
lock.waitLock(10000);
} catch (e) {
Logger.log('Could not obtain lock after 10 seconds.');
}
do what you have to do, increment the counter... and when your done use
lock.releaseLock();
it might seem complicated but it's not ;-) and it will work flawlessly in every possible situation... just give it a try.