google script openByUrl autorization, trigger activation - google-apps-script

I'm quite new to programming and struggling with apps authorization :
I created a code that has to access other files of my Gdrive, with the command openByUrl, which sends back an error message, telling me it doesn't have the authorization to perform openByUrl.
I went through the oauth2 scopes but can't understand how to give the authorization to my script.
I already have an internet copied script that runs DriveApp.getFolderById, but it runs through a menu.
I want both getFolderById and openByUrl to run on a trigger (each day at midnight) thus, it can't run through a menu.
The goal of my script is to list all files in a specified folder, get the spreadsheet ones, and extract specific cells from these spreadsheets.
I got something running with importance, but clearly, it's too bulky to run properly on more than 3000 files.
Could anyone explain to me how to give authorizations to openByUrl please? (even better if in French ;-) )
Many thanks in advance for your help.

Answer
I would create a new project and copy the whole code.
If you want to check why in a particular project you have this error, you can open the manifest file that has the required scopes. You can find it clicking on Project Settings on the left bar and then Show "appsscript.json" manifest file in editor. The field oauthScopes has to contain all the required scopes or has to be deleted.
Methods explanation:
DriveApp.getFoldersByName(name) get all the folders with a specified name. Take the first one with .next()
Folder.getFilesByType get all the files with a specific mimetype
SpreadsheetApp.openById open a specific Spreadsheet document
Spreadsheet.getSheets() get all the sheets in an array
Sheet.getRange() get a specific range
Range.getValues() get the values of a specified range
Updated code:
function main() {
var folderName = 'folder'
var type = MimeType.GOOGLE_SHEETS
var folders = DriveApp.getFoldersByName(folderName).next().getFolders()
while (folders.hasNext()) {
var folder = folders.next()
console.log(folder.getName())
var files = folder.getFilesByType(type)
while (files.hasNext()) {
var file = files.next()
console.log(file.getName() + ': ' + file.getid())
var sheets = SpreadsheetApp.openById(file.getid()).getSheets()
var values = sheets[0].getRange('A1:B2').getValues()
console.log(values)
}
}
}

Related

Running an Apps Script stored in Google Drive from Google Sheets

I have a google sheets workbook that builds a report based on user input, which is run by clicking a "button" (a square shape on the sheet itself). I want to share this workbook with my team, who need to create a copy of the workbook so they can generate multiple reports for themselves.
However, I also want to be able to make changes to the code at a later date, and avoid having them re-download the latest version, so I'm trying to decentralise the Apps Script file by putting it into my company's shared Google Drive, and then the workbook script replaced by a function that loads that file in the drive.
So far I have:
function getApp(){
var folderId = "<folder_id>";
var fileName = "<file_name>";
var scriptId = "<script_id>";
var folder = DriveApp.getFolderById(folderId);
var files = folder.getFilesByName(fileName);
var url = "https://script.google.com/feeds/download/export?id=" +
scriptId + "&format=json"
var options = {
"method": "GET",
"headers": {
"Authorization": "Bearer " + ScriptApp.getOAuthToken()
},
"muteHttpExceptions": true
};
var response = UrlFetchApp.fetch(url, options);
dataContentAsString = response.getContentText();
fileContents = JSON.parse(dataContentAsString);
var codeFile = fileContents.files[1];
if (codeFile){
var code = codeFile.source;
eval(code);
buildReport();
}
}
Which takes the "file" at index 1 (an object containing all functions in the script) and runs the buildReport function. When I do Logger.log(fileContents) I can see the entire script, so I know the retrieval from google drive is working.
buildReport() is the "main" function, which then calls other functions, however when running it, I get the error below, which indicates an Oauth issue:
Exception: You do not have permission to call
SpreadsheetApp.getActive. Required permissions:
(https://www.googleapis.com/auth/spreadsheets.currentonly ||
https://www.googleapis.com/auth/spreadsheets)
Does this mean that despite being able to access the file, the file itself doesn't have access to the sheets (that contain templates which the script manipulates based on the users initial inputs prior to clicking the button) where the macro is being run from?
Is this the best way to achieve what I want?
Update
I added a trigger to the workbook, which runs buildReport just fine when the spreadsheet is opened (not the desired behaviour, but still at least it's working in some way), however when clicking the "Build Report" button it shows the error still.
Why would the local script (i.e. local to the google sheet) be able to successfully import from google drive and run buildReport() when using a trigger, but not when clicking a button to do the same thing?
If you have an standalone script that will "modify" the user current Spreadsheet (or slides, or...) but the script bounded to the Spreadsheet only "calls" the external script, you can add a dummy/commented line to the bound script, so that when the user runs the bound script, permissions for the standalone script will also be asked. Sorry, for my english :D
Adding this line, anywhere, will do the trick:
//SpreadsheetApp.getActive()
BTW, I found very useful your way to share scripts!

DriveApp.getFoldersByName give different result depending on user

I have a script that copies folders and content. It gets the folder names to copy from a sheet. All this works.
When the script comes to
DriveApp.getFoldersByName("NameOfFolder");
The script finds the folder iterator when I execute the script, but not when my colleague does.
If he does a search in Drive he can see the folder, the script returns null.
We have the same access to the folder. Its a shared folder on Google Drive.
Can anyone explain?
Here is the code that reproduces the error:
NB: the sheet supplies the script with propper names of folders to look for.
function shortTest() {
var episodeFolderList = DriveApp.getFoldersByName("Folder name");
var episodeFolderByID = DriveApp.getFolderById("longFolderIDXXXXXXX");
Logger.log(episodeFolderByID+"-By ID")
var episodeFolder=episodeFolderList.next();
Logger.log (episodeFolder+ " -by name")
}
Ill ask him to log out of other accounts and try again.
Thank you for all the input.

Can I add a script to a Google Sheet using another script?

I have a script populate() to run an automation script on Google Sheets. I want this script to be run on over a hundred Google Sheets files. Is there a way to write a script to add to (or at least run) my populate() script on all those files? I prefer to be able to add the script to each file because we may need to run the scripts multiple times for each file. Otherwise, I will have to manually copy/paste the script to each sheet, which takes time.
Update: Removed the part about converting Excel files to Google Sheets because I found the answer for that on another thread here.
Solution
From what I have understood from your post, your question is about how to convert a series of Excel files inside a Drive folder into Google Sheets to then execute a function in them.
For that, you could iterate over all the files of your folder and convert them with the use of Drive.insert from the Drive API. For that, you will have to enable in your script the Advanced Google Services. If you do not know how to do so, please follow these indications.
The follow script performs what I think you are aiming for, it has self explanatory comments:
function convertExcelToGoogleSheets() {
// Id of the folder where you have all your excel files
var folderId = 'FOLDERID';
var folder = DriveApp.getFolderById(folderId);
// Get all the files in your folder and iterate over them
var files = folder.getFiles();
// For each file:
while (files.hasNext()){
var file = files.next();
// Get its blob (content)
var blob = file.getBlob();
// Create the resource to insert the Google Sheet file. Put the same name
// as the original and set the type of the file to Google sheets and its
// parent folder to where the excel files are located
var resource = {
title: file.getName(),
mimeType: MimeType.GOOGLE_SHEETS,
parents: [{id: folderId}],
};
// User Drive API to insert the actual file and get its ID
var id = Drive.Files.insert(resource, blob).getId();
// Whatever function you want to perform in the newly created Google Sheet
// pasing the parameter of the file id
myScript(id);
}
}
// Set the first cell of all the converted sheets to "IT WORKED"
function myScript(id){
SpreadsheetApp.openById(id).getActiveSheet().getRange('A1').setValue('IT WORKED!');
}
EDIT: If you want to run a script for each of this converted sheets you can simply call it in the while loop and pass the new file id as a parameter to then be able to use the script (remember to encapsulate this script in a function).
I hope this has helped you. Let me know if you need anything else or if you did not understood something. :)

Google Sheets - Script to “Replace” instead of “Create”, when file name is the same

EDIT to try and make this easier to understand:
Here is what I did:
I created a Google Spreadsheet. I created a SCRIPT that saves it to a Google Drive Folder using a File Name based on the date of service and Customer Name.
This is the Script I currently have:
// This creates a custom Menu Function so that I can save the file.
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Choice Menu')
.addItem('Submit Form','saveAsSpreadsheet')
.addToUi(); }
// Saves Spreadsheet in Google Drive Folder
function saveAsSpreadsheet() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var destFolder = DriveApp.getFolderById("0B8xnkPYxGFbUMktOWm14TVA3Yjg");
DriveApp.getFileById(sheet.getId()).makeCopy(getFilename(), destFolder);
}
//This Function uses a cell "G4" (Which is Date and Customer Name) to create a file name. This file name will be then used for the above Script.
function getFilename() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getSheetByName('Manifest');
var cell = sheet.getRange('G4');
var filename = cell.getValue();
return filename;}
So My code works GREAT except for one Problem.
Here is my Problem:
When I Save the Spreadsheet a 2nd time using the above Script, it saves a new file. I want to save it as a new file unless the Filename is the same. IF the file name is the same, I want to delete the original file, and then save the new file.
What I tried:
From what I understand reading the comments below, I need to run a code that "1. will need to run a query to see if any files exist with your chosen name, and then act accordingly. 2. If there are no files with that name, then go ahead and create one (using the function I listed above). If there is a file with the same name, 3. delete the original file and create a new one with the same name.
I tried for several days coming trying different options, and none of it worked, so I am looking to start over.
Appreciate any coding solutions or direction on where to go!
Your script is the container-bound script of Spreadsheet.
When the Spreadsheet with the searched filename is existing in a specific folder, you want to replace it to the active Spreadsheet.
In the current stage, a file cannot be replaced while the file ID is not changed. So in this modified script, the active Spreadsheet is copied and the searched file is deleted.
When the searched file is not existing in a specific folder, you want to copy the active Spreadsheet to the folder.
You want to use the value returned from getFilename() as the filename.
If my understanding is correct, how about this modification? I think that there are several solutions for your situation. So please think of this as just one of them.
Modified script:
Please modify saveAsSpreadsheet() as follows.
function saveAsSpreadsheet() {
var folderId = "0B8xnkPYxGFbUMktOWm14TVA3Yjg";
var folder = DriveApp.getFolderById(folderId);
var files = folder.getFilesByName(getFilename());
if (files.hasNext()) {
files.next().setTrashed(true);
}
var sheet = SpreadsheetApp.getActiveSpreadsheet();
DriveApp.getFileById(sheet.getId()).makeCopy(getFilename(), folder);
}
Note:
In order to search the file, I used getFilesByName() because I thought the filename seems constant.
setTrashed() for removing the file is used for this script. In this case, the file is put in the trash box. If you want to directly delete the file, you can achieve it using Drive API. At that time, please tell me.
References:
getFilesByName()
setTrashed()
Edit:
If you want to directly delete the file, please modify the script as follows.
From:
files.next().setTrashed(true);
To:
var params = {method: "delete", headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}};
UrlFetchApp.fetch("https://www.googleapis.com/drive/v3/files/" + files.next().getId(), params);
I used the method using UrlFetchApp because I thought that in your situation, Drive API has already been enabled. In this case, you can use the delete method of Drive API by only replacing the script.
Reference:
delete method of Drive API
You need to take a step back and change your thinking about filenames.
In GDrive, files are identified by their unique ID. The name is just a metadata property alongside timeModified, owner, mimeType, etc. So having multiple files with the same name is as natural as having multiple files with the same mimeType.
Your code will need to run a query to see if any files exist with your chosen name, and then act accordingly. If there are no files with that name, then go ahead and create one. If there are, then you have the choice to update the existing file, or delete the original file(s) and create a new one with the same name.

Google Apps Script Error: "You do not have permission to call openById"

I am trying to use a Google Script that I found on GitHub. (It is Posted Below.) It is supposed to merge data from Google sheets into a Google Doc. However, when I try to run the script I get an error: "You do not have permission to call openById". I have researched the problem and the information is conflicting. But, I have tried the following:
Manually run the function in script editor before adding the trigger to Google sheet.
Authorized the script to access my Google Docs data.
Set the triggers for the function to "On Form Submit"
Added the script to my Google Sheet and ran it from the menu as instructed: Merge --> Fill Template
I continue to get the error. I see that there are other people having the same problem, but the answers provided so far have not solved the issue. Any help would be greatly appreciated.
function doMerge() {
var selectedTemplateId = "1foobarfoobarfoobarfoobarfoobarfoobar";//Copy and paste the ID of the template document here (you can find this in the document's URL)
var templateFile = DriveApp.getFileById(selectedTemplateId);
var mergedFile = templateFile.makeCopy();//make a copy of the template file to use for the merged File.
mergedFile.setName("filled_"+templateFile.getName());//give a custom name to the new file (otherwise it is called "copy of ...")
var mergedDoc = DocumentApp.openById(mergedFile.getId());
var bodyElement = mergedDoc.getBody();//the body of the merged document, which is at this point the same as the template doc.
var bodyCopy = bodyElement.copy();//make a copy of the body
bodyElement.clear();//clear the body of the mergedDoc so that we can write the new data in it.