Why Google Apps Script hangs on calling next() function - google-apps-script

Today I can't run scripts connected to a specific Google Spreadsheet which contains .next() function calls, which I use to access other Spreadsheets by name. It shows Running script Cancel Dismiss message at the top of spreadsheet until exceeding maximum execution time. The strange thing is that I have similar files for every day with exactly same scripts, and the one for yesterday works fine. I created MWE below. So, when I run it I can get the alerts for the first 2 messages, but not the 3rd one.
function test_script_hanging() {
var ui = SpreadsheetApp.getUi();
ui.alert("before getting files by name");
var getFilefile = DriveApp.getFilesByName("file");
ui.alert("after getting files by name");
var getID = getFilefile.next().getId();
ui.alert("after getting Id");
}
In some situations I notice an error message saying something like "Authorization is needed to perform this action" on the line where the .next() function is being called. I tried to revoke authorization of the file and give authorization again, but that didn't help. I tried to give full access to a given script, but couldn't google the way to do it.
Maybe I can add some more functions in my script to require full access during authorization, and maybe that will help to run my main function.
My questions are:
Why the script hangs?
How to fix this?
Can such kind of things (randomly and without any notice) happen in a free version of Google account?

This works for me:
var fldr = DriveApp.getFolderById(folderID)
var file = fldr.getFilesByName(filename);
while(file.hasNext())
{
var fi = file.next();
if(fi.getName() == filename)
{
var id=fi.getId();
}
}

Related

TypeError: Cannot read property 'getBody' of null

I'm trying to teach myself to use Google Apps Script, but somehow every function I try returns this error immediately. And since I'm a huge beginner, I don't know what I'm doing wrong.
Here's a simple example of a code I tried to run:
function myFunction(){
//application
//file
var ad = DocumentApp.getActiveDocument();
var docBody = ad.getBody () ;
var paragraphs = docBody.getParagraphs();
//paragraphs[0]. setText ("MY NEW TEXT"):
//var attr = paragraphs[0].getAttributes() ;
//Logger.log(attr);
paragraphs[0].setAttributes({FONT_SIZE:40});
}
Yet no matter what I'm running really, I get this:
TypeError: Cannot read property 'getBody' of null
myFunction # Code.gs:5
What am I doing wrong?
I have an open Google Doc, I've allowed permissions to run the script project. What else should I try? Thanks.
In order to get getActiveDocument() working you need to use it in a script that is container-bound:
Create a new Google Document
Go to Extensions > Apps Script
Run your function inside of it.
If your script is not container-bound, you need to use the openById(id) or openByUrl(url) methods, in order to retrieve a Document
Adblock software was preventing the running somehow. Disabled it and runs fine now. Disregard!

Previously working Google App Script now produces permission error

I cannot find anything about Google having updated policies on April 2022. This script was working without any issues before, then in the middle of the day, permissions needed to be re-added. Even with said permissions granted, Google still says access is denied. It was later discovered that the macro has not ran successfully since March 31st.
var newJobFile = DriveApp.getFileById(newJobID);
newJobFile.AddEditors(['group#gmail.com','admin#gmail.com');
newJobFile.setOwner('admin#gmail.com');
The last line is what generates the error. When debugging, I can see as the variable gets created, I cannot see any information about it. For example, if I add a "Filecreated.getName();" before the line with the error, the variable menu remains blank. I am not sure if this is normal. The "newid" variable is confirmed to have the spreadsheet ID. I can copy it into the URL and it takes me to the page. Why is this suddenly an issue and how can I fix it? I did not have a appscripts.json file before with OAuthScopes and it worked fine then. I've added it with the proper permissions, but it does not change anything. I've added several permission scopes to try to resolve this but none of them do. Any advice?
"oauthScopes": [
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/spreadsheets"
]
EDIT: I am still having this issue. Even with everything controlled under one account, I cannot transfer ownership to 'Central_account#gmail.com' due to 'Exception : Access Denied : DriveApp'. I have found just one other person experiencing this same issue on the Google App Script group page. Is anyone else not having issues with transferring ownership starting in April on a regular, non-work account?
EDIT 2: Sorry for not uploading my code earlier, I had to clean it up first.
function onOpen(e) {
SpreadsheetApp.getUi()
.createMenu('Add') //creates toolbar entry to the right of 'Help'
.addItem('Capital project','newProject')
.addToUi();
}
function newProject(){
var dashboard = SpreadsheetApp.getActiveSpreadsheet(); //Stores dashboard into a variable for later
var template = SpreadsheetApp.openById('xxxxxx') //Opens the project sheet template in the backend
var nameEntry = getName(); //Prompts user for the name of the new job file
if (nameEntry == null){
return;
}
SpreadsheetApp.setActiveSpreadsheet(template); //Template is now the active sheet
var newJobID = copySheet(nameEntry); //Creates a copy of the New Project template
var newJobFile = DriveApp.getFileById(newJobID);
newJobFile.AddEditors(['group#gmail.com','admin#gmail.com');
newJobFile.setOwner('admin#gmail.com');
}
function getName(){
var ui = SpreadsheetApp.getUi();
var name = ui.prompt( //Prompts the user for an input name
'',
'?????-? Project Description',
ui.ButtonSet.OK_CANCEL);
var cancelCheck = name.getSelectedButton();
if (cancelCheck == ui.Button.CANCEL || cancelCheck == ui.Button.CLOSE) {
return null;
}
var sheetName = name.getResponseText();
return sheetName;
}
function copySheet(name) {
var activeSS = SpreadsheetApp.getActive();
var newFile = activeSS.copy(name) //Creates a copy of the New Project template
SpreadsheetApp.setActiveSpreadsheet(newFile); //Resets active spreadsheet to the recent copy
activeSS = SpreadsheetApp.getActive();
activeSS.getRange('A1').activateAsCurrentCell();
activeSS.getCurrentCell().setValue(name); //Set cell A1 to the name of the file
var newHyperlink = '=HYPERLINK("' + activeSS.getUrl() + '#gid=15580246",A1)';
activeSS.getRange('A2').activateAsCurrentCell();
activeSS.getCurrentCell().setValue(newHyperlink);
return activeSS.getId();
}
With logs, I can see that everything works as intended except for the .setOwner() method. It returns the Access Denied error. I have checked all google accounts and each has enabled CustomScripts to access their drive. Has .setOwner() been deprecated for non workspace accounts?
I've updated my original snippets to match my code.
Apparently the process to transfer file ownership between consumer accounts has changed. According to this guide, the prospective new owner needs to accept the transfer request. See example below:
And I found this article where you can see that the process was different a few months ago, you could set a new owner immediately without sending invitation.
I tested the setOwner() method with a Google Workspace account and it works as within Google Workspace you can directly transfer file ownership between users within the same organization, then I tested the same script with a Gmail account and tried to set another Gmail account as the new owner and I got the same error message: "Exception: Access denied: DriveApp".
Based on all the information, it seems that this behavior is expected as the process to change a file ownership for Gmail accounts is different now, you can’t set a new owner directly, the person you invite to own the file must accept your request to complete the transfer.
I don't have enough reputation to comment, but I'd second what Lorena Gomez said that it's likely an issue with your OAuthScopes. According to the Apps Script documentation, the setOwner() method on the File class requires the /auth/drive authorization scope.
It looks like you have both the /auth/drive and the /auth/drive.file scope configured, and I think the /auth/drive.file scope, which is narrower, is overriding the broader /auth/drive scope, which is required to call setOwner().
Have you tried removing the /auth/drive.file scope and running the script only with /auth/drive?

How to archive data with google scripts

I'm new to writing scripts and am attempting to get started on archiving my data. Right now I have this script, which I thought was a good start, but am getting an error when I attempt to run it.
On the sheet named "Allergen Planner" I have a live CSS feed in cell N17 which updates every hour. I am attempting to archive that data once daily and place it in a new sheet called "Archived data". I've tried looking into researching on my own for some time now, but am struggling to find something that can help. Everything I've found is way above my capabilities. Is there anything I can edit in my script which would achieve what I'm looking for? Any help would be greatly appreciated.
{
var ssA = SpreadsheetApp.openById('1ji3Q02uOyrDXyrl7LcghWLcLjz-CeSUgSCU7QD0BVEk');
var sheetA = ssA.getSheetByName('Allergen Planner');
var dataToMove = sheetA.getRange(14,17,sheetA.getLastRow(),sheetA.getLastColumn()).getValues();
var ssB = SpreadsheetApp.openById('1ji3Q02uOyrDXyrl7LcghWLcLjz-CeSUgSCU7QD0BVEk');
var sheetB = ssB.getSheetByName('Archived Data');
dataToMove.forEach(function(elts){
sheetB.appendRow(elts)
});
}
The code pasted above worked. If you receive a red "Unkown error occurred" message in the execution log, reboot the script app and try again.

Published google script link with Google Sheet returning printing empty variable

Since I tend to ramble, first a short version and if you need more information read the long one.
TL;DR
Why is this:
function doGet(e) {
var sheet = SpreadsheetApp.getActiveSheet();
var jobsCreated = sheet.getRange(12,2).getValue();
Browser.msgBox(jobsCreated);
var params = JSON.stringify({number:jobsCreated});
return ContentService.createTextOutput(params);
}
returning this when I published as website and then open:
{"number":""}
when it should look more like this {"number":2451}
Full Version:
First of all, I learned to program back in uni for my Computer science degree (10 years ago) but since then I haven't done much programming so I am basically a newbie.
Now to the question. I have a very simple script that a created with the script editor from Google Sheets
function doGet(e) {
var sheet = SpreadsheetApp.getActiveSheet();
var jobsCreated = sheet.getRange(12,2).getValue();
Browser.msgBox(jobsCreated);
var params = JSON.stringify({number:jobsCreated});
return ContentService.createTextOutput(params);
}
First I get the sheet I am working on
Then I select a cell from that sheet
now if I use a msgBox to make sure that I have the right number and run the script, it works and it shows the message.
next, I format the variable as JSON and finally I just create a text output.
Now I deploy as Web app
Execute as ME
Anyone, even anonymous
And when I access the website I can only see this:
{"number":""}
If I change the code and give jobsCreated and static value it works fine
var jobsCreated = 100;
{"number":100}
So my conclusion is that the problem is with accessing the value of the cell when running the script from the published link compare to running it directly from the editor, but I have no idea how to fix this.
A little bit more information, i am trying to use this for a counter called Smiirl, i got most of the information from here
https://medium.com/#m_nebra/bootstrapping-your-company-counter-22f5d4bc7dd4
try this:
function doGet(e) {
return ContentService.createTextOutput(Utilities.formatString('number: %s',SpreadsheetApp.getActiveSheet().getRange(12,2).getValue());
}
Ok as I replied to #Aerials, thank you again for your help btw. Seeing other codes that should work not working with my script, I decided to create a new sheet and script as a test and with the exact same code it works.
But now checking on it a little bit more, something that I didn't think it was a problem since it was getting the number without any problems. The cell it's being populated by a GoogleAnalytics add-on. Now when setting up the add-on again to get the information the script from the website returns an empty value again. SO it seems the issue is with the script getting the information from the sheet (only the published version) when its being populated by the add on
Your issue is in the use of JSON.stringify
In JSON, functions are not allowed as object values.
The JSON.stringify() function will omit or change to null any functions from a JavaScript object.
That said, you can do the following:
function doGet(e){
// Get the value of the range
var sheet = SpreadsheetApp.getActiveSheet();
var jobsCreated = sheet.getRange(12,2).getValue();
// JSON Stringify it
var params = JSON.stringify({"number" : number=jobsCreated});
// Return JSON string
return ContentService.createTextOutput(params);
}
Returns:
{"number":123} if jobsCreated is the number 123, or
{"number":"Mangos"} if jobsCreated is the string "Mangos".
See it the script deployed here, and the sheet to play with.
Note:
You should avoid using functions in JSON, the functions will lose their scope, and you would have to use eval() to convert them back into functions.

Inserting Image into Google Sheets from Google Drive using Apps Script

I have been trying for 9 days to add an image that is uploaded to my drive (via the use of a google form) into my Google sheet using Apps Script, but it isn't working and I have no idea why, this is my code below:
function getImage(){
var folderImage = DriveApp.getFolderById("0B7gxdApLS0TYfm1pRHpHSG4yTm96bm1PbTZQc1VmdGpxajY4N1J4M1gtR1BiZ0lOSl9NMjQ");
Logger.log(folderImage.getFiles().next().setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW));
Logger.log(folderImage.getFiles().next().getSharingAccess());
Logger.log(folderImage.getFiles().next().getSharingPermission());
var imageFile = folderImage.getFiles().next().getBlob();
detailSheet.insertImage(imageFile, 1, 13);
}
I have even tried making the sharing and access permissions of the to be as open as possible but I keep getting this error message:
"We're sorry, a server error occurred. Please wait a bit and try again"
I find the error message ambiguous which leads me at a dead end. Usually the message gives me a good idea of where I have gone wrong.
I believe my code is correct, and during my research I have found no definitive reason this shouldn't work. Does anybody know where I am going wrong?
A solution would be great but preferably a critique on my code so I can learn :)
Couple of issues with your script:
You never bind to a specific file, so to work with the same file you have to reinitialize the iterator each time.
You don't verify its mimetype prior to using it as an image
An example that resolves those issues:
function addFolderPNGs_(sheet, folderId) {
const folder = folderId ? DriveApp.getFolderById(folderId) : DriveApp.getRootFolder(); // scope only to the root or given folder.
const imgs = folder.getFilesByType(MimeType.PNG);
var targetRow = sheet.getLastRow();
while (imgs.hasNext()) {
var img = imgs.next();
Logger.log(img.getName())
sheet.insertImage(img.getBlob(), 1, ++targetRow);
}
}
References
getFilesByType is either folder-specific (above) or operates on all of Google Drive
DriveApp
Folder
MimeTypes
Sheet#insertImage