I need to change the permission of every uploaded file. But when I try to add this code,
printPermissionIdForEmail(email) {
var request = gapi.client.drive.permissions.getIdForEmail({
'email': email,
});
request.execute(function(resp) {
return ('ID: ' + resp.id);
});
}
I got an error of getIdForEmail is not a function.
gapi.client.init, gapi.auth2.getAuthInstance(),
are working. But why gapi.client.drive.permissions.getIdForEmail is not working? There is something I need to do? in Google Developers Page? in my Code?
getIdForEmail is a method only available in Google Drive v2.
With V3 you are going to have to go after it in another manner.
Do a files.list with the q parameter. In the q parameter supply the user whos permissions you wish to change. You can see here how to use search This would find all the files where someuser is the owner.
'someuser#gmail.com' in owners
Then you will get a list of file resources you can then check the permissions on each file using permissions.list and use that to change the ones you need.
I am not a JavaScript developer but I found this in the documentation it shows how to use search to list files.
/**
* Print files.
*/
function listFiles() {
gapi.client.drive.files.list({
'q': "'someuser#gmail.com' in owners",
'fields': "*"
}).then(function(response) {
appendPre('Files:');
var files = response.result.files;
if (files && files.length > 0) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
appendPre(file.name + ' (' + file.id + ')');
}
} else {
appendPre('No files found.');
}
});
}
Update:
I just spotted this. About.get Gets information about the user, the user's Drive, and system capabilities
{
"user": {
"kind": "drive#user",
"displayName": "Linda Lawton",
"photoLink": "xxxx",
"me": true,
"permissionId": "060305882255734372",
"emailAddress": "xxxx#gmail.com"
}
}
Could that be the same permissionId you were looking for?
The method I use is based on the OAuth2 library published on script.google.com. This is written for Google Apps Script with domain-wide delegation. The key here is building a valid url and option for UrlFetchApp.fetch(url, options), then parsing the result to find the ID number.
function getIdForEmailv3(userEmail) {
var service = getService(userEmail);
if (service.hasAccess()) {
Logger.log('getIdForEmailv3(%s) has access', userEmail);
var url = 'https://www.googleapis.com/drive/v3/about' + '?fields=user/permissionId'
var options = {
'method': 'get',
'contentType': 'application/json',
'headers': { Authorization: 'Bearer ' + service.getAccessToken() },
'muteHttpExceptions': true
};
var response = UrlFetchApp.fetch(url, options);
var resultString = JSON.stringify(response.getContentText());
var regex = new RegExp(/\d+/g);
var id = regex.exec(resultString)[0];
Logger.log('getIdForEmailv3 returned %s for %s', id, userEmail);
return id
} else {
Logger.log('getIdForEmailv3 getLastError: %s', service.getLastError());
Logger.log('getIdForEmailv3 returned %s for %s', 0, userEmail);
return 0;
}
}
Regex idea from: Easiest way to get file ID from URL on Google Apps Script
Fields format from comment on solution: How to add 'field' property to a Google Drive API v3 call in JavaScript?
Related
I'm porting over some existing js code authenticating with the google cloud platform (as they are migrating to a new set of libraries).
(migration guide: https://developers.google.com/identity/oauth2/web/guides/migration-to-gis)
I'm struggling with getting a hold of the player's profile (to obtain their email).
The old approach would be along the lines of this (but as it says, it is now deprecated - I've been reading the new docs but it mostly surrounds getting authorized/authenticate and not the follow on from that):
https://developers.google.com/identity/sign-in/web/people
e.g.
var profile = auth2.currentUser.get().getBasicProfile();
var email = profile.getEmail();
In my new code I've have the access token, via the new approach:
client_id: vm.clientId,
scope: SCOPE,
callback: (tokenResponse) => {
if (tokenResponse && tokenResponse.access_token) {
access_token = tokenResponse.access_token;
// HERE??? HOW DO I GET THE PROFILE?
}
}
})
(largely taken from https://developers.google.com/identity/oauth2/web/guides/use-token-model)
I've seen this mentioned elsewhere but it doesn't work in my situation at least:
gapi.client.oauth2.userinfo.get().execute(function (resp) {
console.log(resp);
})
(How to get profile information from Google Identity Services?)
I've read via the migration guide: 'Instead, use direct references to credential sub-fields in the new JWT CredentialResponse object to work with user profile data.' but don't know how to get this Credentialresponse?
(https://developers.google.com/identity/gsi/web/guides/migration#token_response)
Adding a function like this works in my situation:
function getUserProfileData(accessToken) {
let promise = new Promise(function(resolve, reject) {
let request = new XMLHttpRequest();
const url = `https://www.googleapis.com/oauth2/v3/userinfo`;
request.addEventListener("loadend", function() {
const response = JSON.parse(this.responseText);
if (this.status === 200) {
resolve(response);
} else {
reject(this, response);
}
});
request.open("GET", url, true);
request.setRequestHeader('Authorization', `Bearer ${accessToken}`);
request.send();
});
promise.then(function(response) {
var email = response.email;
}, function(errorMessage) {
// TODO: print error
});
}
This returns a json that contains the following properties:
email
email_verified
family_name
given_name
hd
name
picture
sub
You can follow these steps that I had to undertake to achieve the entire flow using Javascript.
First create a button that will hold the HTML element:
<button id="btnGoogleSignIn" style="border:none;background: none;"> </button>
You can then use the below script and associated functions in which I am getting the JWT token from Google and then decoding it to get the required information out like email address etc. Note that I am calling the onSignInGSI as callback on the button initialization.
<script>
function decodeJwtResponseFromGoogleAPI(token) {
let base64Url = token.split('.')[1]
let base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
let jsonPayload =
decodeURIComponent(atob(base64).split('').map(function (c) {
return '%' + ('00' +
c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
return JSON.parse(jsonPayload)
}
function onSignInGSI(response) {
//console.log(response)
responsePayload = decodeJwtResponseFromGoogleAPI(response.credential);
console.log("ID: " + responsePayload.sub);
console.log('Full Name: ' + responsePayload.name);
console.log('Given Name: ' + responsePayload.given_name);
console.log('Family Name: ' + responsePayload.family_name);
console.log("Image URL: " + responsePayload.picture);
console.log("Email: " + responsePayload.email);
}
window.onload = function () {
google.accounts.id.initialize({
client_id: client_id,
context: 'signin',
callback: onSignInGSI
});
google.accounts.id.prompt();
google.accounts.id.renderButton(document.getElementById("btnGoogleSignIn"),
{
type: "standard",
text: "signin_with",
logo_alignment: "left",
width: 375
});
};
</script>
<script src="https://accounts.google.com/gsi/client" async defer></script>
I'm going to start by saying it's immensely frustrating half knowing how to do something but never quite being able to finish; this is another one of those projects for me.
Scenario: Using a Google Sheet and Apps Script I am attempting to update several User records in Zendesk using their API.
I think i probably have most if it right (i stand to be corrected of course) with the following script however I just cannot get it to update any records. I suspect it might be to do with how the array is presented (an area I sadly don't know enough about).
function updateManyUsers(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var [headers, ...rows] = sheet.getDataRange().getValues();
var data = {}
var items = []
rows.forEach(function(r) {
var obj={}
r.forEach(function (c, j) {
obj[headers[j]] = c
})
var data = {}//moved
data['users'] = obj // moved this inside your loop
items.push(data) // pushed the object into the items array
})
Logger.log("Log JSON Stringify Items: " + JSON.stringify(items))
items.forEach(function(i) { // added this to loop over objects in items
var url = 'https://itsupportdesk1611575857.zendesk.com/api/v2/users/update_many.json'; //https://developer.zendesk.com/api-reference/ticketing/users/users/#update-user
var user = 'myemailaddresshere/token';
var pwd = 'mytoken';
var options = {
'method' : 'PUT',
'headers': {
'Authorization': "Basic " + Utilities.base64Encode(user + ':' + pwd)
},
'payload' : JSON.stringify(i),
'contentType': 'application/json',
'muteHttpExceptions': true
};
UrlFetchApp.fetch(url, options);
Logger.log(i)
var response = UrlFetchApp.fetch(url, options);
Logger.log(response);
})
}
I've gone through as much as I can following the documentation, I know i had the end points incorrect and the method(?) too (set to Post instead of Push). I have gone through varying error messages that I have tried to act upon and this is my current one:
This is an image of the data in my sheet
Suplimental: In order to get better at this i would like to put myself on a learning path but am unsure what the path is; most of my automation work and scripting is done using Google Apps script so would people recommend a JavaScript course? I alter between that and Python not knowing what would suit me best to get a better understanding of this kind of issue.
Many thanks in advance.
From your endpoint in your script, I thought that you might have wanted to use "Batch update". Ref If my understanding is correct, the following sample curl in the official document can be used. Ref
curl https://{subdomain}.zendesk.com/api/v2/users/update_many.json \
-d '{"users": [{"id": 10071, "name": "New Name", "organization_id": 1}, {"external_id": "123", "verified": true}]}' \
-H "Content-Type: application/json" -X PUT \
-v -u {email_address}:{password}
If this sample curl command is converted to Google Apps Script using your script, how about the following modification?
Modified script:
function updateManyUsers2() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var [headers, ...rows] = sheet.getDataRange().getDisplayValues();
var users = rows.map(r => {
var temp = {};
headers.forEach((h, j) => {
if (r[j] != "") temp[h] = r[j];
});
return temp;
});
var url = 'https://itsupportdesk1611575857.zendesk.com/api/v2/users/update_many.json';
var user = 'myemailaddresshere/token';
var pwd = 'mytoken';
var options = {
'method': 'PUT',
'headers': {
'Authorization': "Basic " + Utilities.base64Encode(user + ':' + pwd)
},
'payload': JSON.stringify({ users }),
'contentType': 'application/json',
'muteHttpExceptions': true
};
var response = UrlFetchApp.fetch(url, options);
Logger.log(response.getContentText());
}
Note:
From the official document, it says Bulk or batch updates up to 100 users.. So, when you want to use more data, please modify the above script. Please be careful about this.
If an error occurs, please check the values of users, user and pwd, again.
Reference:
fetch(url, params)
I want to get the Google apps script library details such as version number and note programmatically in a spreadsheet cell.
You can use the projects.versions.list method.
You need to specify the script ID of the library. If you have the link of the library you can find the script id in the url or in the project settings.
If they are not automatically added, you might have to add the scopes manually in the appsscript.json file. E.g. add this inside the brackets:
"oauthScopes": [
"https://www.googleapis.com/auth/script.projects",
"https://www.googleapis.com/auth/script.projects.readonly",
"https://www.googleapis.com/auth/script.external_request"
],
Example:
function getVersion(){
const AccessToken = ScriptApp.getOAuthToken();
const scriptID = "scriptID" // put the script id of the library
const url = "https://script.googleapis.com/v1/projects/" + scriptID + "/versions";
options = {
"method" : "GET",
"muteHttpExceptions": true,
"headers": {
'Authorization': 'Bearer ' + AccessToken
}
};
response = UrlFetchApp.fetch(url,options);
response = JSON.parse(response);
allVersions = response.versions; //get all versions
highestVersion = allVersions[0].versionNumber;
console.log(response);
console.log(highestVersion);
}
I am trying to send email addresses from a google sheet to an external web page via Google Scripts, but it doesn't seem to be working.
From reading other similar issues it seems that I need the doPost() function but I'm unsure how I should incorporate it into my script.
Here's my google script
function getEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var myUrl = 'https://my.url/';
if(data) {
for(var i = 0; i < data.length; i++) {
if(i !== 0) {
postToForm(myUrl, data[i][0]);
}
}
deleteData(sheet, data);
}
}
function postToForm(url, data) {
UrlFetchApp.fetch(url + 'user/add/?email=' + data);
}
function deleteData(sheet, data){
for (var i = data.length; i>1; i--) {
sheet.deleteRow(i);
}
}
and here is the receiving script:
<script>
window.addEventListener('DOMContentLoaded', async () => {
if (location.search) {
const urlSearchParams = location.search;
try {
await DB.ready();
const email = getUrlParameter('email', urlSearchParams);
if (email) {
emailSignUp(email)
return;
}
} catch (error) {
console.log(error);
showSnackBar('Something went wrong', false);
return;
}
}
showSnackBar('Something went wrong', false);
});
function getUrlParameter(key, urlSearchParams) {
key = key.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp('[\\?&]' + key + '=([^&#]*)');
var results = regex.exec(urlSearchParams);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
}
</script>
Maybe I need to change the postToForm function to doPost and add it that way?
Thanks
Answer
On one hand there is UrlFetchApp, it is designed to fetch resources and communicate with other hosts over the Internet. On the other hand there is, doPost that runs when an HTTP POST request is sent to a web app. It is developed by the user using Google Apps Script and publishing the script as web app.
In your case, as you are calling an external web, you need to use UrlFetchApp in a proper way. In the documentation there are two examples that show how to make the HTTP request, however your case looks different. Usually, when you make an HTTP POST request to send data to a server, you put that data on the message body request but depending on the design of your API, you can send data through the query component of the URI.
In your case, the code will look like this:
function postToForm(url, data) {
var uri = url + 'user/add/?email=' + data
var options = {
'method' : 'post',
'payload' : ''
};
UrlFetchApp.fetch(uri, options);
}
I would like to create a custom function that pulls a Drive URL from a file name in Google Sheets.
So, using the code below:
If I have a valid file name in cell A1
The function =getFile(A1) would return the URL
When I run my script from within the script editor, the return value works.
When I run the function getFile() from within my sheet, I get the error below.
My code:
function getFile(cell) {
var filename = encodeURI(cell);
var url = "https://www.googleapis.com/drive/v3/files?fields=files(id,name)&q=name+contains+'" + filename + "' and trashed=false";
var params = {
method: "GET",
headers: {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
muteHttpExceptions: true
};
var res = UrlFetchApp.fetch(url, params).getContentText();
var json = JSON.parse(res);
return res; // outputs response below
if(json){
var objFiles = json.files[0];
var fileID = objFiles.id
var resURL = "https://docs.google.com/spreadsheets/d/" + fileID;
Logger.log(resURL);
//return resURL; // only works when run within script editor
}
}
Error:
"{
"error": {
"errors": [
{
"domain": "global",
"reason": "authError",
"message": "Invalid Credentials",
"locationType": "header",
"location": "Authorization"
}
],
"code": 401,
"message": "Invalid Credentials"
}
}
"
I'm guessing something's wrong with my Auth token. Can someone direct me to resolving this? Thanks in advance!
Custom functions runs as if run by a anonymous animal(user). ScriptApp.getOAuthToken will return a anonymous token without the required scopes. What you're attempting is not possible, unless the file in question is public.
References:
Custom functions permissions
Custom functions access services
This may be a solution for some needs.
My particular need was: loop through a column of file names and pull the Google Docs URL at a set interval. The code below just loops through filenames in "Column A" of "My Sheet" and returns the value into the adjacent cell of "Column B" (starting at row 2 because I had column headers). I'm not concerned about security because I'm only referencing internal organization files.
To get the code below to work you need to:
Google Sheet Doc Nav > Tools > Script Editor
Create a .gs file & input code below (Referencing The Respective Sheet
Within Script Editor > Edit > Current Project’s Triggers > Name Your Project
Within Script Editor > Edit > Current Project’s Triggers > Click on modal link “No triggers set up. Click here to add one now” > set your time-based trigger (reference replaceFileColumn in the select field within that modal)
My mistake was: thinking that I needed to use a custom function in each cell to do so. (I still don't fully understand the auth reasons why this wouldn't work, so if anyone could explain in lay-man's terms that would be fabulous; my solution is just a workaround for expediency's sake).
In my spreadsheet I have a time-driven trigger calling replaceFileColumn()
Hope this helps someone!
function getMyFile(cell) {
var filename = encodeURI(cell);
var files = DriveApp.getFilesByName(cell);
while (files.hasNext()) {
var file = files.next();
if(file){
var fileValue = file.getUrl();
return(fileValue);
};
};
}
function replaceFileColumn() {
var spreadsheet = SpreadsheetApp.getActive().getSheetByName('My Sheet');
var range = spreadsheet.getRange("A2:A");
var range_update = spreadsheet.getRange("B2:B");
var values = range.getValues();
for (var i = 0; i < values.length; i++) {
var fileName = values[i];
var getFileUrl = getMyFile(fileName);
values[i][0] = getFileUrl;
}
range_update.setValues(values);
}
#I'-'I's answer is correct. Although I'm not sure whether this is what you want, how about this workaround? I have also experienced the same issue. At that time, I had used the following workaround.
Set and get access token using PropertiesService.
The flow is as follows.
Flow:
Set access token every 1 hour by the time-driven trigger.
By this, the access token is updated every 1 hour. Because the expiration time of access token is 1 hour.
When the custom function is run, it gets the access token using PropertiesService.
By this, the access token can be used.
Modified script:
Please install this function as the time-driven trigger. Of course, you can run manually this function.
function setAccessToken() {
PropertiesService.getScriptProperties().setProperty("accessToken", ScriptApp.getOAuthToken());
}
In your script, please modify as follows.
From:
var params = {
method: "GET",
headers: {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
muteHttpExceptions: true
};
To:
var params = {
method: "GET",
headers: {"Authorization": "Bearer " + PropertiesService.getScriptProperties().getProperty("accessToken")},
muteHttpExceptions: true
};
Note:
In this case, the owner of access token is the owner of project.
I think that this can be also used by CacheService.
Reference:
PropertiesService