For the past few years, I've been using Sheets as a data source for a web app by using the following code to turn the id into a direct link to a TSV file:
let id="1zD3eIL8LCTJ8F_8U3kWA6k5WPJNKr_UZ_93bnARlMxQ"
let str="https://docs.google.com/spreadsheets/d/"+id+"/export?format=tsv";
var xhr=new XMLHttpRequest();
xhr.open("GET",str);
xhr.onload=function() {/* act on data */ };
xhr.send();
xhr.onreadystatechange=function(e) {
if ((xhr.readyState === 4) && (xhr.status !== 200)) {
/* Show error */
}
It still works on old files, but new ones yield a CORS error:
Access to XMLHttpRequest at 'https://doc-00-0g-sheets.googleusercontent.com/export/l5l039s6ni5uumqbsj9o11lmdc/5filqetsf3ohbeiq2e8vbtf8ik/1593267040000/112894833168181755194/*/1zD3eIL8LCTJ8F_8U3kWA6k5WPJNKr_UZ_93bnARlMxQ?format=tsv' (redirected from 'https://docs.google.com/spreadsheets/d/1zD3eIL8LCTJ8F_8U3kWA6k5WPJNKr_UZ_93bnARlMxQ/export?format=tsv') from origin 'https://viseyes.org' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Works: www.viseyes.org/scale?1LSnAM3A62AQipZfqxDtlOjt4MWJ0fBP22cdyqJqEj5M
Error: www.viseyes.org/scale?1zD3eIL8LCTJ8F_8U3kWA6k5WPJNKr_UZ_93bnARlMxQ
I believe your goal as follows.
You want to retrieve the data with TSV format from the Google Spreadsheet using Javascript.
Your spreadsheet is publicly shared.
For this, how about this answer?
Issue and workaround:
I could confirm the same situation from your question. Unfortunately, I couldn't remove this error. So, in this case, as a workaround, I would like to propose to use Web Apps created by Google Apps Script as the wrapper. By this, the error can be removed. The flow of this workaround is as follows.
Request to Web Apps from Javascript.
At Web Apps, the data is retrieved from "https://docs.google.com/spreadsheets/d/"+id+"/export?format=tsv".
Return the data with the TSV format from Web Apps.
Usage:
Please do the following flow.
1. Create new project of Google Apps Script.
Sample script of Web Apps is a Google Apps Script. So please create a project of Google Apps Script.
If you want to directly create it, please access to https://script.new/. In this case, if you are not logged in Google, the log in screen is opened. So please log in to Google. By this, the script editor of Google Apps Script is opened.
2. Prepare script.
Please copy and paste the following script (Google Apps Script) to the script editor. This script is for the Web Apps.
function doGet() {
let id = "1zD3eIL8LCTJ8F_8U3kWA6k5WPJNKr_UZ_93bnARlMxQ"; // This is from your script.
let str = "https://docs.google.com/spreadsheets/d/"+id+"/export?format=tsv"; // This is from your script.
const value = UrlFetchApp.fetch(str);
return ContentService.createTextOutput(value.getContentText());
}
If your Google Spreadsheet is not publicly shared, please modify as follows.
function doGet() {
let id = "1zD3eIL8LCTJ8F_8U3kWA6k5WPJNKr_UZ_93bnARlMxQ"; // This is from your script.
let str = "https://docs.google.com/spreadsheets/d/"+id+"/export?format=tsv"; // This is from your script.
const value = UrlFetchApp.fetch(str, {headers: {authorization: "Bearer " + ScriptApp.getOAuthToken()}});
return ContentService.createTextOutput(value.getContentText());
// DriveApp.getFiles() // This is used for automatically detecting the scope.
}
3. Deploy Web Apps.
On the script editor, Open a dialog box by "Publish" -> "Deploy as web app".
Select "Me" for "Execute the app as:".
By this, the script is run as the owner.
Select "Anyone, even anonymous" for "Who has access to the app:".
In this case, no access token is required to be request. I think that I recommend this setting for your goal.
Of course, you can also use the access token. At that time, please set this to "Anyone". And please include the scope of https://www.googleapis.com/auth/drive.readonly and https://www.googleapis.com/auth/drive to the access token. These scopes are required to access to Web Apps.
Click "Deploy" button as new "Project version".
Automatically open a dialog box of "Authorization required".
Click "Review Permissions".
Select own account.
Click "Advanced" at "This app isn't verified".
Click "Go to ### project name ###(unsafe)"
Click "Allow" button.
Click "OK".
Copy the URL of Web Apps. It's like https://script.google.com/macros/s/###/exec.
When you modified the Google Apps Script, please redeploy as new version. By this, the modified script is reflected to Web Apps. Please be careful this.
4. Run the function using Web Apps.
When you use this, please modify your Javascript script as follows and test it.
From:
let id="1zD3eIL8LCTJ8F_8U3kWA6k5WPJNKr_UZ_93bnARlMxQ"
let str="https://docs.google.com/spreadsheets/d/"+id+"/export?format=tsv";
To:
let str = "https://script.google.com/macros/s/###/exec";
Note:
When you modified the script of Web Apps, please redeploy the Web Apps as new version. By this, the latest script is reflected to the Web Apps. Please be careful this.
In my environment, I could confirm that when above workaround is used, no error occurs and the data with the TSV format can be retrieved.
References:
Web Apps
Taking advantage of Web Apps with Google Apps Script
Related
Hello is possible twhen copying a Google Doc document to copy also the comments in the "copy doc."Because I've tried this with the TEMPLATE_DOC_ID which has many comments and I don't find the comments in the "copy".I am missing something?It's another method? Thanks!
//Make a copy of the template file
var documentId = DriveApp.getFileById(TEMPLATE_DOC_ID).makeCopy().getId();
Unfortunately, the Google Docs copied by makeCopy() don't include the comments. So the comments and replies are required to be inserted to the copied file, after the file was copied. In order to implement this, please enable Drive API at Advanced Google Services and API console.
Enable Drive API v2 at Advanced Google Services
On script editor
Resources -> Advanced Google Services
Turn on Drive API v2
Enable Drive API at API console
About Drive API, in your environment, this might have already been enabled.
On script editor
Resources -> Cloud Platform project
View API console
At Getting started, click Enable APIs and get credentials like keys.
At left side, click Library.
At Search for APIs & services, input "Drive". And click Drive API.
Click Enable button.
If API has already been enabled, please don't turn off.
Sample script :
var documentId = DriveApp.getFileById(TEMPLATE_DOC_ID).makeCopy().getId();
// Added script
var commentList = Drive.Comments.list(TEMPLATE_DOC_ID);
commentList.items.forEach(function(item) {
var replies = item.replies;
delete item.replies;
var commentId = Drive.Comments.insert(item, documentId).commentId;
replies.forEach(function(reply) {
Drive.Replies.insert(reply, documentId, commentId).replyId;
});
});
Note :
Unfortunately, the create time and modified time couldn't updated. So the date becomes the created date.
References :
Advanced Google Services
Drive API
Comments: insert
Replies: insert
If this was not what you want, I'm sorry.
Say I have a page like this:
textarea {width:300px;height:200px}
button {display:block}
<textarea value="f">id,value
2,alpha
3,beta
14,test</textarea>
<button>Open in Google Sheet</button>
I want the user to click the button "Open in Google Sheet" and open the csv as a spreadsheet.
I saw that Google Analytics and some other Google products have this button. But I didn't find 3rdparty webapps have this. Is that possible for me to use it?
I believe your goal is as follows.
From I want the user to click the button "Open in Google Sheet" and open the CSV as a spreadsheet., you want to retrieve the text value from the textarea tab and create a Google Spreadsheet using the text value, and then, want to open the Google Spreadsheet.
In order to achieve your goal, how about the following flow?
Retrieve the text value from the textarea tab.
Send the text value to Web Apps created by Google Apps Script.
At Web Apps, a new Google Spreadsheet is created and the text value is put to the sheet.
In order to open the created Spreadsheet, change the permission of the Spreadsheet. In this case, it is publicly shared as the read-only. This is the sample situation.
Return the URL of the Spreadsheet.
When this flow is reflected in the script, it becomes as follows.
Usage:
1. Create a new project of Google Apps Script.
Sample script of Web Apps is a Google Apps Script. So please create a project of Google Apps Script.
If you want to directly create it, please access https://script.new/. In this case, if you are not logged in to Google, the log-in screen is opened. So please log in to Google. By this, the script editor of Google Apps Script is opened.
2. Sample script.
Please copy and paste the following script to the created Google Apps Script project and save it. This script is used for Web Apps. In this sample, the value is sent as the POST request.
function doPost(e) {
const csv = Utilities.parseCsv(e.postData.contents);
const ss = SpreadsheetApp.create("sample");
ss.getSheets()[0].getRange(1, 1, csv.length, csv[0].length).setValues(csv);
DriveApp.getFileById(ss.getId()).setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
return ContentService.createTextOutput(ss.getUrl());
}
3. Deploy Web Apps.
The detailed information can be seen at the official document.
On the script editor, at the top right of the script editor, please click "click Deploy" -> "New deployment".
Please click "Select type" -> "Web App".
Please input the information about the Web App in the fields under "Deployment configuration".
Please select "Me" for "Execute as".
This is the importance of this workaround.
Please select "Anyone" for "Who has access".
In this case, the user is not required to use the access token. So please use this as a test case.
Of course, you can also access to your Web Apps using the access token. Please check this report.
Please click "Deploy" button.
Copy the URL of the Web App. It's like https://script.google.com/macros/s/###/exec.
When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful this.
You can see the detail of this in the report of "Redeploying Web Apps without Changing URL of Web Apps for new IDE".
4. Testing.
As the test of this Web Apps, I modified your script as follows. Before you use this script, please set the URL of your Web Apps to url. When you open this HTML and click the button, a new Spreadsheet including the text value in the textarea tab is opened with new window as the read-only.
<textarea id="sampletext" value="f">id,value
2,alpha
3,beta
14,test</textarea>
<button onclick="sample()">Open in Google Sheet</button>
<script>
function sample() {
const url = "https://script.google.com/macros/s/###/exec"; // Please set the URL of your Web Apps.
fetch(url, { method: "POST", body: document.getElementById("sampletext").value })
.then((res) => res.text())
.then((url) => window.open(url, "_blank"));
}
</script>
Note:
When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful this.
You can see the detail of this in the report of "Redeploying Web Apps without Changing URL of Web Apps for new IDE".
My proposed script is a simple script. So please modify it for your actual situation.
References:
Web Apps
Taking advantage of Web Apps with Google Apps Script
I want to prepare a Chrome Extension for my password manager program. The password manager program keeps the usernames and passwords encrypted locally and is therefore not on any server. Instead of copying and pasting every time, I send the usernames and passwords to the Google address line as follows:
https://stackoverflow.com/users/login?user_name=USERNAME&password=PASSWORD
I want the Google Extension to automatically add the username and password in the address line to the corresponding textboxes. Unfortunately, I have no idea for this. I downloaded and reviewed Google Extensions like Daslane, but they are all very complicated and I could not understand. Your ideas and examples, if possible, on how to do this very simply are very valuable for me.
First you need to find simple chrome extension sample code.
This is the simple source from chrome developer page.
After that, you have to detect the control name using chrome developer tool and use this script to auto fill and click login button.
function AutoFill() {
var html = window.location.href + "\n\n";
if (html.includes("example.com") == true)
{
document.getElementById("username").value = "username"
document.getElementById("password").value = "password"
document.getElementsByName("login")[0].click();
}
return html;
}
chrome.extension.sendRequest(AutoFill());
To fill the form on the document you need access to the document. You can use for this content scripts - https://developer.chrome.com/extensions/content_scripts
I'm not sure why you would like to pass credentials via URL. I think it is a bad practice and it can break website behavior. It is better to get credentials from background script - https://developer.chrome.com/extensions/background_pages
Communication between content script and background page can be implemented via chrome.runtime.onMessage event.
If you really need to get parameters from URL, you can do this in the content script by using URLSerachParams - https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
Example:
var url = new URL(location.href);
console.log(url.searchParams.get('user_name'));
I am sharing a set of Google Sheet documents with other users. I am the owner of all the spreadsheets and they are housed on my Google Drive. Every user has their own dedicated sheet. Users need to be able to run scripts on their sheet that collect and manipulate data from the sheet, which means the sheet needs to be unprotected while the scripts run. However, at the end end of the script, I want to protect the sheet again so that the user running the script is unable to edit the protected cells once the script is done running.
I am able to easily remove the protections from the sheet at the beginning of the script, but I am unable to protect the sheet again via the script without the user who is running the script being listed as an editor. I have tried using the "removeEditors" function to no avail (see below for basic example for one of the protected ranges).
var userEmail = Session.getActiveUser().getEmail();
var protection = newPickSheet.getRange('A1:M3').protect();
protection.removeEditor(userEmail);
For your reference, here is the code I'm using at the beginning of the script to remove all protections:
var protections = newPickSheet.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for(var i = 0; i < protections.length; i++){
protections[i].remove();
}
To summarize, I need my script to:
Remove all protections
Execute the main part of the script
Re-add all protections so that only me, the sheet owner, but NOT the user running the script, has edit ability.
I was able to accomplish this very easily using VBA when deploying Excel Macros, but it seems this is not nearly as simple using App Scripts.
If anyone can help me figure out a solution, it would be greatly appreciated.
You want to run mainly 3 functions in one function.
Remove the protected the range of newPickSheet.getRange('A1:M3').
Run a script for editing cell in the range.
Protect the range.
You want to run the script by clicking run button which is put on Spreadsheet. You don't want to use the event trigger.
If my understanding is correct, I think that the important point is who run each function. So how about this workaround? I think that there are several workarounds for your situation. So please think of this as just one of them.
Modification points:
In your situation, in order to protect a range from users, it is required to run the function for protecting the range as owner. Because the protected range is created by owner, when the protected range is removed, it is required to also run the function as owner.
Here, in this workaround, it supposes that the script of Execute the main part of the script includes the methods depending on user.
When the script is run with clicking the run button by each user, the script is run as each user. This is the important point for this situation. In order to run the part of script as owner, I used Web Apps here. By using Web Apps, the script for removing and adding the protected range can be run as owner.
By above points, the flow of this workaround is as follows.
1. Remove the protected range by owner using Web Apps.
2. Run the script of Execute the main part of the script by each user.
3. Protect the range by owner using Web Apps.
Preparation for using modified script:
Please run the following flow before you use the modified script.
Copy and paste the modified script to the script editor.
This script supposes that you are using the container-bound script of the Spreadsheet.
In this modified script, I used main() for the main function. If you are using other name, please modify it.
Please set ##### of var newPickSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("#####");.
Put the script of "Execute the main part of the script".
Deploy Web Apps.
On the script editor, open "Publish" -> "Deploy as Web Apps".
Set "Project version" as new. Please input freely to "Describe what has changed".
Set "Execute the app as:" to "Me".
Set "Who has access to the app:" to "Anyone". By this, each user can access to Web Apps using own access token.
Click "Deploy" or "Update" button. By this, Web Apps is deployed.
When you modify the script after Web Apps was deployed, please redeploy as new version. By this, the latest script is reflected to Web Apps. This is an important point for using Web Apps.
Modified script:
In this modified script, I used main() for the main function. If you are using other name, please modify it.
// This is the main function. Please set this function to the run button on Spreadsheet.
function main() {
// DriveApp.getFiles(); // This is a dummy method for detecting a scope by the script editor.
var url = ScriptApp.getService().getUrl() + "?access_token=" + ScriptApp.getOAuthToken();
UrlFetchApp.fetch(url + "&key=removeprotect"); // Remove protected range
// do Something: Please put the script of "Execute the main part of the script" here.
SpreadsheetApp.flush(); // This is required to be here.
UrlFetchApp.fetch(url + "&key=addprotect"); // Add protected range
}
function doGet(e) {
var newPickSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("#####"); // Please set here.
if (e.parameter.key == "removeprotect") {
// Remove protected range.
var protections = newPickSheet.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
protections[i].remove();
}
} else {
// Add protected range.
var ownersEmail = Session.getActiveUser().getEmail();
var protection = newPickSheet.getRange('A1:M3').protect();
var editors = protection.getEditors();
for (var i = 0; i < editors.length; i++) {
var email = editors[i].getEmail();
if (email != ownersEmail) protection.removeEditor(email);
}
}
return ContentService.createTextOutput("ok");
}
Note:
In this modified script, the range of "A1:M3" in the sheet of newPickSheet. newPickSheet is declared as var newPickSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("#####"). If you want to modify this, please modify to your situation.
In my environment, when user clicks the run button soon, after user opens the shared Spreadsheet, there was sometimes the case that the function of the button cannot be found. In this case, please wait for completely loading the Spreadsheet.
This is a simple sample script. So please modify this to your situation.
References:
Web Apps
Taking advantage of Web Apps with Google Apps Script
I'm trying to create an add-on for Google Sheets. When the sheet is opened, the script should create a menu item in Add-ons.
The item is added and works when I open the Sheet it's originally bound to. When trying to test it as an add-on with onOpen as a declared function in my script, it always results in the item not being added and an error being logged in the Google Chrome DevTools console for the Sheet opened for the test:
Google Apps Script: We're sorry, a server error occurred. Please wait a bit and
try again.
This error doesn't occur if I comment out the onOpen method.
I've tried running it as an add-on in the following ways with both Auth.none and Auth.limited:
The original copy of the script attached to its original Sheet.
The original copy of the script running with a separate Sheet.
A copy of the script in a separate Google Scripts file running with a different Sheet.
Creating a new Sheet and bounded script with just an empty onOpen function.
These all result in the error above, and when I uncomment the contents of onOpen functions that do have code, none of their code appears to run.
Here's the original copy's code but keep in mind I still get the error even if the contents are commented out and this states that even if createMenu is used for an add-on, it's handled correctly by Google App Script:
function onOpen(e){
var menu = SpreadsheetApp.getUi().createAddonMenu(); // Or DocumentApp or FormApp.
if (e && e.authMode == ScriptApp.AuthMode.NONE) {
// Add a normal menu item (works in all authorization modes).
menu.addItem("Show Sidebar", "showSidebar");
menu.addToUi();
} else {
// Add a new menu (doesn't work in AuthMode.NONE).
var topUI = SpreadsheetApp.getUi();
topUI.createMenu("Mail Merge")
.addItem("Show Sidebar", "showSidebar")
.addToUi();
}
}
The Logger object also doesn't seem to log my messages while running the script as an add-on.
Copying the Sheet over to another account and just having it be owned by that account (not in a team drive) did allow the menu to appear when testing it as an add-on with both types of authorization. The Google App Script error also stopped occurring after doing this.
The most closely related information I could find about Team Drive restrictions was this, which says it limits cloud platform interaction. But this link is Google's page on setting up and running Test As Add-on and it doesn't state anything about the Team Drive or cloud platform.