Why the variable in the Drive.App permission won't work - google-apps-script

I have a google appss script code that doesn't work. Actually it works for me but not for other users who try to use the script. I want to be able to let the user set their own access when uploading files.
This works:
create_file_from_blob.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
This doesn't:
let access= getPermission();
create_file_from_blob.setSharing(DriveApp.Access[access], DriveApp.Permission.VIEW);
The function is this:
function getPermission() {
// Retrieve the saved permission
var sharingPermission = PropertiesService.getUserProperties().getProperty('sharingPermission') || 'PRIVATE';
Logger.log(sharingPermission);
return sharingPermission
}
I know this should work as it is but it doesn't, the function gets the correct access but somehow it doesn't translate to the create_file_from_blob.setSharing(DriveApp.Access.
Is there a way to debug it?

Related

Google Apps Script: What is the correct way to check if I have access to a Google Spreadsheet by URL

I am currently writing a Google Apps Script inside a Google Sheet to read data from a list of spreadsheets (spreadsheet url is provided by the user). However, I cant seems to find a way to check if the url is valid or if user have access to the spreadsheet or not before calling SpreadsheetApp.openByUrl().
I have written the following code to "validate" the url:
for(int i = 0; i < urls.length; i++) {
let spreadsheet = null
try {
spreadsheet = SpreadsheetApp.openByUrl(urls[i]);
} catch (e) {
continue;
}
// Continue to do other stuff to read data from spreadsheet...
}
This however has an issue, it was able to catch the first few 'You do not have permission to access the requested document.' exception. But after a certain number of exception had occur, I would get a permenant error that cant be caught, stopping the script all together.
Is there a better way to do this?
Minimal reproducible example:
Create 3 google sheet using different google account
Using a different google account, create a google sheet and add the following code into Code.gs
function myFunction() {
// Put any 3 real spreadsheet url that you do not have access to
let urls = [
"https://docs.google.com/spreadsheets/d/1gOyEAz0amm4RghpE4B7f26okU3PG3vWZkrfiC-SBlbw/edit#gid=0",
"https://docs.google.com/spreadsheets/d/1Oia7ADu5BmYroUq1SLyDMHTJowrwSXOhCEyNO3nXmMA/edit#gid=0",
"https://docs.google.com/spreadsheets/d/1HE_IXURpBr_FJN--mwLo6k9gih07ZEtDGBqYSk6KgiA/edit#gid=0",
]
urls.forEach(url => {
try {
SpreadsheetApp.openByUrl(url)
} catch (e) {
console.log("Unable to open this spreadsheet")
}
})
}
function onOpen() {
SpreadsheetApp.getUi().createMenu("Test").addItem("myFunction", "myFunction").addToUi()
}
Run the function once in the apps script panel and authorize the application
Refresh this google sheet
Wait for the Custom Menus to show up and press "Menu" > "myFunction"
As you can see, the openByUrl() call is sitting inside the try catch block, however when you run the function through custom menu, you will still get "Error: You do not have permission to access the requested document.".
Executions Log:
From your question, I thought that your situation might be due to the specification or a bug of SpreadsheetApp.openByUrl. If my understanding is correct, in order to avoid this issue, how about putting the method for checking whether the file can be used before SpreadsheetApp? In your script, how about the following modification?
From :
SpreadsheetApp.openByUrl(url)
To:
var fileId = url.split("/")[5];
var file = DriveApp.getFileById(fileId);
spreadsheet = SpreadsheetApp.open(file);
In this modification, the file is retrieved with DriveApp.getFileById(fileId). When fileId cannot be used, an error occurs. But in this case, try-catch can be correctly worked. By this, the issue of SpreadsheetApp doesn't occur.

Google Apps Script trigger - run whenever a new file is added to a folder

I want to execute a google apps script whenever a new file is added to a specific folder.
Currently I'm using a run-every-x-minutes clock trigger, but I only need to run the script whenever I add a file to a folder. Is there a way to do this?
The same as this question - which is now almost 3 years old. The comment below the question states that:
There's not a trigger for that, if that's what you're hoping. How are
things getting into the folder, and do you have any control over that?
– Jesse Scherer Apr 8 '18 at 3:02
I wonder if this comment is still valid, and if it is, then if there's a workaround.
Issue:
Unfortunately, the comment you read is still true. Here is a list of all the available triggers and a trigger for a new file added to a folder is not one of them.
Workaround / Explanation:
I can offer you a workaround which is usually used by developers when they built their add-ons. You can take advantage of the PropertiesService class. The logic is quite simple.
You will store key-value pairs scoped to the script:
In your case, the key will be the folder id, and the value will be the number of files under this folder.
You will setup a time-driven trigger to execute mainFunction for example every one minute.
The script will count the current number of files within the selected folder. The function responsible for that is countFiles.
The checkProperty function is responsible for checking if the current number of files under this folder matches the old number of files. If there is a match, meaning no files were added, then checkProperty returns false, otherwise return true and update the property for the current folder ID, so when the script runs after 1 minute, it will compare with the fresh value.
If checkProperty returns true, then execute the desired code.
Code snippet:
Set up a time-driven trigger for mainFunction. Whatever code you put inside the brackets of the if(runCode) statement will be executed if the number of files under the folderID has changed.
function mainFunction(){
const folderID = 'folderID'; //provide here the ID of the folder
const newCounter = countFiles(folderID);
const runCode = checkProperty(folderID, newCounter);
if(runCode){
// here execute your main code
//
console.log("I am executed!");
//
}
}
And here are the helper functions which need to be in the same project (you can put them in the same script or different scripts but in the same "script editor").
function countFiles(folderID) {
const theFolder = DriveApp.getFolderById(folderID);
const files = theFolder.getFiles();
let count = 0;
while (files.hasNext()) {
let file = files.next();
count++;
};
return count;
}
function checkProperty(folderID, newC){
const scriptProperties = PropertiesService.getScriptProperties();
const oldCounter = scriptProperties.getProperty(folderID);
const newCounter = newC.toString();
if(oldCounter){
if(oldCounter==newCounter){
return false;
}
else{
scriptProperties.setProperty(folderID, newCounter);
return true;
}
}
else{
scriptProperties.setProperty(folderID, newCounter);
return true;
}
}

Google apps script URL changes and does not open

I have an issue where other uses of my Google apps script's url is getting changed. Due to this issue they are not able to open the html page.
Original url "https://script.google.com/a/macros/google.com/s/abcxyz-kaskasdb/exec?v=applyleave"
changed url "https://script.google.com/macros/s/abcxyz-kaskasdb/exec?v=applyleave"
I realize "/a" and "/google.com" is getting removed some how.
How can I fix this issue.
Here is my code that is rendered:-
function include(filename)
{
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function render(file,argsObject){
var tmp = HtmlService.createTemplateFromFile(file);
if(argsObject){
var keys = Object.keys(argsObject);
keys.forEach(function(key){
tmp[key] = argsObject[key];
});
}
return tmp.evaluate();
}
And here's the code for the server which should accept POST requests:
var Route = {};
Route.path = function (route,callback){
Route[route] = callback;
}
function doGet(e) {
Route.path("applyleave",leaveApply)
Route.path("leaveroster",leave_Roster)
if (Route[e.parameters.v]){
return Route[e.parameters.v]();}
else {
html = HtmlService.createTemplateFromFile('home');
return html.evaluate();
}
}
The error received from other side is this :-
Can anyone explain and provide solution?
There is nothing wrong with the deployment URL getting changed - it is common for Google to perform this redirection.
The issue is can be rather a permission issue. Make sure you deploy the WebApp as "Anyone, even anonymous".
However, currently I am experiencing the same behavior like you due to a multiply reported recent bug:
https://issuetracker.google.com/72798634
https://issuetracker.google.com/165350842
https://issuetracker.google.com/166010550
https://issuetracker.google.com/166320373
https://issuetracker.google.com/167692852
https://issuetracker.google.com/169349069
Please refer to this Github repo.
After the first deployment, you need to make further deployments on the same version by clicking on "Manage Deployment" and then selecting the version to "New version"
enter image description here

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();
}

folder.revokePermissions fails to revoke permissions

Can anyone help me figure out why this revokeAdminPermissions function is not working? It executes, and the Execution Transcript states that it's successful, but the permissions are not revoked; the user still has access to the folders in question.
AddAdminPermissions and findFolder included for reference only, those are working fine.
Thanks in advance to anyone who can help!
function findFolder(folderName){
var folders = DriveApp.getFolders();
while (folders.hasNext()) {
var folder = folders.next();
if(folder.getName()==folderName){
return folder
}
}
}
function addAdminPermissions(email){
findFolder("Admin").addEditor(email)
findFolder("Project Sheets").addEditor(email)
findFolder("Team Members").addEditor(email)
}
function revokeAdminPermissions(email){
var admin = findFolder("Admin")
admin.revokePermissions(email)
var projects = findFolder("Project Sheets")
projects.revokePermissions(email)
var tmFolder = findFolder("Team Members")
tmFolder.revokePermissions(email)
}
Looking at the documentation from revokePermissions(user), it says that:
This method does not block users from accessing the File if they
belong to a class of users who have general access — for example, if
the File is shared with the user's entire domain.
I was able to work around this by calling folder.removeEditor(email) and folder.removeViewer(email). Doesn't explain the failure, but at least it works