removeEditor failing on some editors - google-apps-script

I have some google apps script code that adds Editors to a document with no problem. However, when I run the following code I find that some of the editors are removed, and some error with the following message: "Exception: We're sorry, a server error occurred. Please wait a bit and try again."
var file = DocsList.getFileById( fid );
var editors = file.getEditors();
for ( el = 0; el < editors.length ; el++ ) {
file.removeEditor( editors[ el ] );
}
Given that the editors are retrieved from the file itself, and then fail to be removed, I cannot see how to progress this, as the error message offers no help.
Has anyone experienced similar. I cannot see any issues raised against this.
Thanks in advance.
Chris

You could try to put your file.remove(editors[ el ]) in a try-catch structure to catch the error (and see it) and let your app finish in every case.
Example :
function myFunction() {
var file = DocsList.getFileById( '0AnqS........bW1DNnVBbVE' );
var editors = file.getEditors();
Logger.log(editors.join())
for ( el = 0; el < editors.length ; el++ ) {
try{
file.removeEditor( editors[ el ] );
Logger.log(editors[el]+' removed');// this editor is successfully removed
} catch(error)
{Logger.log('error on el = '+editors[el]+' = '+error)
}
}
}
This of course brings no answer on your issue but it could help to see what is happening in more details, which editor gets an error and which doesn't.

I have the same experience, both removeEditor and removeViewer can fail for some users, despite obtaining the user object from the getViewers or getEditors methods.
My observation is that unless the returned user object is a Google apps account holder then the remove action will fail.
So, on a google apps domain example.com a file might be shared with the following email addresses:
normal_account#example.com
group_account#example.com
third_party#some_other_example.com
removeEditor/removeViewer will only succeed for the first address on the list.
Although the call to getEditors/getViewers returns a list of user objects, some of the objects appear empty eg user.getEmail() returns a blank string, which is what the API documentation says will happen https://developers.google.com/apps-script/reference/drive/user#getEmail%28%29 if the email address is not available.

Related

Exception: Service unavailable: Documents Error in Google Doc Apps Script

I have an odd issue with a Google Doc Apps Script. It's been working for months without any issues. Then yesterday I get the Error Message: "Exception: Service unavailable: Documents" whenever I try to position an image blob (either from a URL Fetch or from getFileById). I simplified my code to this, and no matter what image I use, I always get the error.
function debugProc () {
var rLlogoborderlessId = '1ZhOX_aneaAhM0XJV4oQHGfNPUzaVETgu';
doc = DocumentApp.getActiveDocument();
curBody = doc.getBody();
var addlines = curBody.appendParagraph('');
var imgpara = curBody.appendParagraph("");
imgpara.setAlignment(DocumentApp.HorizontalAlignment.CENTER);
var logoimage = DriveApp.getFileById(rLlogoborderlessId).getBlob();
var addlineslogo = curBody.appendParagraph('');
var k2nplogo = addlineslogo.addPositionedImage(logoimage);
// Error: Exception: Service unavailable: Documents
}
I tried multiple different computers and browsers. Nothing works. No workaround that I can find. How can I resolve this?
By reading your new comments I understand that the case isn't reproducible on new documents. The issue may have originated from a problem with your project or a bad configuration. To fix it, you only need to create a new Doc and copy all the data there. Feel free to leave a comment below if you need help with the migration.

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 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

In Gmail Addons, how to get access to all threads' details returned by GmailApp.search?

With the following method, I get the error:
"Missing access token for authorization. Request: MailboxService.GetThread"
at the var button [...] line.
function createItemListCard() {
var card = CardService.newCardBuilder().setHeader(CardService.newCardHeader().setTitle('Item list'));
var section = CardService.newCardSection().setHeader("<b>Items</b>");
var threads = GmailApp.search("some search query")
for (var i = 0; i < threads.length; i++) {
var button = CardService.newTextButton().setText(threads[i].getFirstMessageSubject()).setOpenLink(CardService.newOpenLink().setUrl(threads[i].getPermalink()));
}
return card.build();
}
Example code snipplets seem to be using access tokens per message, like:
var accessToken = e.messageMetadata.accessToken;
GmailApp.setCurrentMessageAccessToken(accessToken);
But it does not help at all. Most likely due to accessing all threads not just a single message. The scope mentioned in docs for search is in place: https://mail.google.com/
There must be a way to access results of GmailApp.search in Gmail Addons. How to do it?
You will need to add
https://www.googleapis.com/auth/gmail.readonly
to the scopes in your manifest.
What might not be obvious is that you'll need to REMOVE
https://www.googleapis.com/auth/gmail.addons.current.message.readonly
from the manifest. It appears that the apps script environment chooses the most restrictive set of permissions from the scopes available: if you have both scopes in your manifest you will only get the current.message.readonly permission (and you won't be able to interact with any other messages).
I also found that I needed to completely uninstall the addon and then re-install it to get the new scopes to take effect.

e.parameter undefined

Q: why is e.parameter.wfId undefined (in the log) after running the script below (as a web-app)
I call script with this URL
https://script.google.com/a/macros/gappspro.com/exec?service=my-webapp-key
without a parameter (&wfId=somecharacters)
function doGet(e) {
var app = UiApp.createApplication().setTitle('Workflow Builder');
var mainGrid = app.createGrid(2,1).setId('FILE_doGet_mainGrid');
app.add(mainGrid);
var wfId = '1234567890' // FILE.doGet.randomString();
mainGrid.setWidget(1,0, app.createTextBox().setValue(wfId).setId('wfId').setName('wfId'));
var handler = app.createServerHandler('func');
handler.addCallbackElement(mainGrid);
Logger.log(e.parameter.wfId);
return app;
}
function func(e) {
return x;
}
I am trying to implement the workflow script from chapter 8 of james ferreira’s book Enterprise Application Essentials and in the add Docs section i ran into the problem that e.parameter.wfId in line “var wfRowArray = FILE.ssOps.getWfRowFromSS(e.parameter.wfId), “ is undefined when running the script. (on page 134 in the book, not the PDF).
In the example above i brought the code back to the essence of what is causing the error,...for me.
e.parameter.wfId is only available in your func(e) function, not in the doGet. the variable e represents the elements of the callBackElement catch by the handler function.
If I have understood your question correctly, this is behaving as expected.
You say "h-ttps://script.google.com/a/macros/gappspro.com/exec?service=my-webapp-key without a parameter (&wfId=somecharacters)"
So, I believe you are not passing any URL parameters to the script URL and therefore you get them as undefined.
If you call your script with the URL parameters, say
h-ttps://script.google.com/a/macros/gappspro.com/exec?service=my-webapp&wfId=somecharacters
then you can expect to see e.parameter.wfld