Find and Replace function multiple documents in Shared Drives - google-apps-script

The issue : I have multiple documents ( specifically, Google Sheets ) in multiple Shared Drives that need the same single word replaced. Some searching around this forum found me the following code, but it doesn't seem to be working. I'm not getting any error messages, but when I go check the files nothing has been changed. I checked and made sure I put in my search string and replacement string correctly.
Do I need to do something different to get it to check files in Shared Drives? I tried turning on the Drive API in Advanced Google Services, but that just gave me an error in line 6 (
var doc = DocumentApp.openById(file.getId()); )
Here's the code I was using :
function myFunction() {
var files = DriveApp.getFiles(); // Note: this gets *every* file in your Google Drive
while (files.hasNext()) {
var file = files.next();
Logger.log(file.getName());
var doc = DocumentApp.openById(file.getId());
doc.replaceText("My search string or regex", "My replacement string");
}
Logger.log("Done")
}

DriveApp.getFiles() returns only the files on "My Drive". In order to be able to get the files in a Shared Drive (formerly Team Drive) you have to enable the Drive Advanced Service and, instead of
var files = DriveApp.getFiles();
use something like
var files = Drive.Files.list(options);
Related
Finding shared date of a file in GAS
Google Apps Script TeamDrive DriveApp.searchFolders and DriveApp.searchFiles does not return any results
"Shared Drive" support in Google Apps Script

In order to access the shared drives, you need to activate the Drive API in the Advanced Google Services, but in order to access it, you will have to use Drive and not DriveApp.
If you want to retrieve the shared drives specifically and look for files with a certain name, you can try this:
Code
// Copyright 2020 Google LLC.
// SPDX-License-Identifier: Apache-2.0
function retrieveFiles() {
var allDrives = Drive.Drives.list().items;
let fileName = 'FILE_NAME';
for (let i = 0; i < allDrives.length; i++) {
var driveId = allDrives[i].id;
console.log(id)
var optionalArgs = {
'driveId': driveId,
'includeItemsFromAllDrives': true,
'corpora': 'drive',
'supportsAllDrives': true,
'q': "title=" + "'" + fileName + "'"
};
var filesReturned = Drive.Files.list(optionalArgs).items;
for (let j = 0; j < filesReturned.length; j++) {
var fileId = filesReturned[j].id;
var sheet = SpreadsheetApp.openById(fileId);
//sheet manipulation
}
}
}
Explanation
The above code retrieves all the shared drives by making use of the Drives:list method and then for each shared drive, searches for all the files which are named FILE_NAME using Files:list. Then, it accesses each file by using SpreadsheetApp.
Note
Please bear in mind that the code can be customized in order to fit your needs and requirements specifically.
Reference
Drive API Drives:list;
Drive API Files:list;
Advanced Google Services Apps Script;
SpreadsheetApp Class Apps Script.

Related

Google Apps Script - How to change the code to create Google Sheets file in active Google Drive Shared folder rather than root (My Drive) folder?

I have some code below that takes all the tabs (excluding 2 of them) in a Google Sheets file and creates independent Google Sheets files for each of them. Right now, they are saving by default in root folder (My Drive), but I would like them to be created in the same active folder as the file from which I call my Google Apps Script. Does anyone know how I can do that with the below code? I also want to avoid scenarios where it's created first in My Drive and then moved to the Shared Drive. The reason for this is because other people may run this code and I want to avoid an instance where the folder in My Drive needs to be updated in the code based on who runs this script.
The simplified version of my above paragraph for brevity: How can I adapt the below code so that the Google Sheets files that are created show up in the active Google Shared Drive folder? I will enabling the Drive API.
My Current Code:
function copySheetsToSS(){
var ss = SpreadsheetApp.getActive();
for(var n in ss.getSheets()){
var sheet = ss.getSheets()[n];
var name = sheet.getName();
if(name != 'ControlTab' && name != 'RawData'){
var alreadyExist = DriveApp.getFilesByName(name);
while(alreadyExist.hasNext()){
alreadyExist.next().setTrashed(true);
}
var copy = SpreadsheetApp.create(name);
sheet.copyTo(copy).setName(name);
copy.deleteSheet(copy.getSheets()[0]);
}
}
}
In your situation, when your script is modified, how about the following modification?
Modified script:
In this modification, Drive API is used. So, please enable Drive API at Advanced Google services.
function copySheetsToSS() {
var ss = SpreadsheetApp.getActive();
var folderId = DriveApp.getFileById(ss.getId()).getParents().next().getId();
for (var n in ss.getSheets()) {
var sheet = ss.getSheets()[n];
var name = sheet.getName();
if (name != 'ControlTab' && name != 'RawData') {
var alreadyExist = DriveApp.getFilesByName(name);
while (alreadyExist.hasNext()) {
alreadyExist.next().setTrashed(true);
}
var newSS = Drive.Files.insert({ title: name, mimeType: MimeType.GOOGLE_SHEETS, parents: [{ id: folderId }] }, null, { supportsAllDrives: true });
var copy = SpreadsheetApp.openById(newSS.id);
sheet.copyTo(copy).setName(name);
copy.deleteSheet(copy.getSheets()[0]);
}
}
}
In this modification, first, the parent folder of Spreadsheet is retrieved. And, in the loop, the new Spreadsheet is created to the specific folder using Drive API.
Note:
In this case, it is required to have the write permission of the folder. Please be careful about this.
References:
getFileById(id)
getParents()
Files: insert

Search files in shared Google Drive [duplicate]

I have some Google Apps script code that searchs for files and folders on TeamDrive.
The issue I am having is that if a file or folder is created by my colleague, when I run my script it can't find the file. If I create a file, and my colleague runs the script, the script can't find the file even though we both have access to view, edit and can see the files and folders in Drive. If one of us edits the file made by the other person, then it becomes visible from the search.
I ran into a similar problem with the Drive REST api when doing some android development. In Android when calling files().list(), It took my a while to find out that I had to set the following in order for my search to be successfull every single time.
.setSupportsTeamDrives(true)
.setIncludeTeamDriveItems(true)
.setCorpora("teamDrive")
.setTeamDriveId(myFolder.getTeamDriveId())
I assume I am running into the same issue with my apps script code.
//Create the N Google docs files
function CreateNFiles(){
var spreadsheet = SpreadsheetApp.getActive();
var Nmain = spreadsheet.getSheetByName("Nmain")
var spreadsheetId = spreadsheet.getId();
var pdfDir = "Form Data";
var TemplatesFolder = null;
//Check and see if there is a 'Form Data' folder
var NFolderId = null;
var RFolderId = DriveApp.getFileById(spreadsheetId).getParents().next().getId();
var files = DriveApp.searchFolders('parents="'+RFolderId+'" and trashed=false');
while (files.hasNext()) {
var myfile = files.next();
if(myfile.getName() == pdfDir){
NOFolderId = myfile.getId();
}
}
https://developers.google.com/apps-script/reference/drive/drive-app#searchFiles(String)
this says to refer to
https://developers.google.com/drive/api/v3/search-parameters#examples_for_teamdriveslist
so I could potentially use
corpora="teamDrive"
is there a way to setSupportsTeamDrives? and setIncludeTeamDriveItems? and setTeamDriveId? in google apps scripts
Finding Files and Folders in a Team Drive
Here's a couple of functions I've been working on for my own needs. They're still a work in progress but one can file folders within a team drive folder and another can find items within a team drive folder. The Logger.log is setup to display item number, title, id, and mimeType.
This one finds Items (either files or folders). You can tell them apart by their types.
function findItemsInTeamDriveFolder(teamDriveId,folderId){
var teamDriveId=teamDriveId || '0AFN5OZjg48ZvUk9PVA';
var folderId=folderId || '1LK76CVE71fLputdFAN-zuL-HdRFDWBGv';
var options={
"corpora":"teamDrive",
"includeTeamDriveItems":true,
"orderBy":"folder",
"q":Utilities.formatString('\'%s\' in parents',folderId),
"supportsTeamDrives":true,
"teamDriveId":teamDriveId
};
var files=Drive.Files.list(options);
var data=JSON.parse(files);
for(var i=0;i<data.items.length;i++){
Logger.log('\nItem: %s - Title: %s - Id: %s - Type:%s - Trashed: %s\n',i+1,data.items[i].title,data.items[i].id,data.items[i].mimeType,data.items[i].explicitlyTrashed?'true':'false');
}
}
This one just finds folders in a folder. It's not reentrant it's a one level deal but currently that's all I need.
function findFoldersInATeamDriveFolder(teamDriveId,folderId){
var teamDriveId=teamDriveId || '0AAc6_2qyI7C0Uk9PVA';
var folderId=folderId || '1HenWOXTSCg96iAvA0ZkgEA9EGKlch4fz';
var optionalArgs={
"corpora":"teamDrive",
"includeTeamDriveItems":true,
"orderBy":"folder",
"q":Utilities.formatString('\'%s\' in parents and mimeType = \'application/vnd.google-apps.folder\'',folderId),
"supportsTeamDrives":true,
"teamDriveId":teamDriveId
}
var list=Drive.Files.list(optionalArgs)
var data=JSON.parse(list);
for(var i=0;i<data.items.length;i++){
Logger.log('\nItem: %s - Title: %s - Id: %s - Type: %s - Trashed;%s\n',i+1,data.items[i].title,data.items[i].id,data.items[i].mimeType,data.items[i].explicitlyTrashed?'true':'false');
findItemsInTeamDriveFolder(teamDriveId,data.items[i].id)
}
}
I thought that they might be helpful.
Meta Data for a file:
Search Parameters:
Drive.Files.List Documentation:
I just used Coopers code to list files that were in a shared drive. I added the code to find the teamdriveID. Two things that cost me some time and might be helpful for others: the number of files is restricted to 100 per default. So I changed it to 200 here. Also, the options file includes trashed files (very confusing) so I filtered them out with an if statement - I am sure this can be done more elegantly but this worked :)
var resource = {
'value': 'emailstring',
'type': 'user',
'role': 'writer'
}
var teamDriveId
// If you have several Team Drives, loop through and give access
var TeamDrive = Drive.Teamdrives.list();
for(var i = 0; i < TeamDrive.items.length; i++) {
if(TeamDrive.items[i].name == "foldernamestring") {
// This ID may also be a single file inside a Team Drive
teamDriveId = TeamDrive.items[i].id;
Logger.log("found "+TeamDrive.items[i].name);
}
}
var options={
"corpora":"teamDrive",
"maxResults":200,
"includeTeamDriveItems":true,
"supportsTeamDrives":true,
"teamDriveId":teamDriveId
};
var files=Drive.Files.list(options);
var data=JSON.parse(files);
var nritems= data.items.length
Logger.log("nritems "+nritems);
for(var i=0;i<nritems;i++){
if (data.items[i].explicitlyTrashed == false){
Logger.log('\nItem: %s - Title: %s - Id: %s - Type:%s - Trashed: %s\n',i+1,data.items[i].title,data.items[i].id,data.items[i].mimeType,data.items[i].explicitlyTrashed?'true':'false');
}
}
I use simple Apps Script code to search folders in a Shared Drive:
function searchFolderByTitle(title,ShDrId){
var folders = DriveApp.searchFolders("title contains '"+title+"' and '"+ShDrId+"' in parents");
while (folders.hasNext()) {
var folder = folders.next();
Logger.log(folder.getName());
}
}
You should use the operator "in" not "=" with "parents" parameter.

Google App Script File class method getOwner() Always returning null for Google shared Drive files

I am trying to extract file details by using the below app script. It works for all the files saved in my drive or files shared with me by someone else. For shared drives files, var owner = file.getOwner() always returns null. Other than getOwner() all the methods works well even with a shared drive folder. Please suggest if I am doing something wrong here.
Code:
function list_all_files_inside_one_folder_without_subfolders(){
var sh = SpreadsheetApp.getActiveSheet();
var folder = DriveApp.getFolderById('MyFolderID'); // I change the folder ID here
var list = [];
list.push(['Name','ID', 'URL','Size','Upload Date', 'Owner']);
list_all_folders_of_folder(folder,list)
sh.getRange(1,1,list.length,list[0].length).setValues(list);
}
function list_all_folders_of_folder(folder,list){
var subfolder = folder.getFolders();
while (subfolder.hasNext()) {
folder = subfolder.next();
list_all_files_of_folder(folder,list)
}
}
function list_all_files_of_folder(folder,list){
var files = folder.getFiles()
while (files.hasNext()){
file = files.next();
var row = []
var owner = file.getOwner()
Logger.log(file.getName())
Logger.log(owner)
row.push(file.getName(),file.getId(), file.getUrl(), file.getSize(), file.getLastUpdated(), owner.getName() )
list.push(row);
}
}
Kind of expected since using Shared Drive transfers the ownership of the file to the entire organization (Not a user or an email address) and file ownership is removed from the user.
There's a workaround I think that can be implemented but it would require a direct call to DriveAPI to check for file activity (Who created the file) but this is far beyond basic scripting or using advanced Workspace Services on GAS.

No item with the given ID could be found on makeCopy() in Google Apps Script

I have an add-on that lets the user download my templates from my drive to theirs provided the add-on is installed in their sheet or doc.
The function that should make a copy is as follows
function createFileCopy(id){
var file = id.split('.');
var docName = DriveApp.getFilesByName(file[0]);
while (docName.hasNext()) {
var file = docName.next();
var fileId = file.getId();
var fileName = file.getName();
}
Logger.log(fileId);
var sheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName(TEMPLATES_DATA);
var data = sheet.getRange(1, 9, sheet.getLastRow()-1, 1).getValues();
var pos = data.map(function (obj) { return obj[0]; }).indexOf(id);
if(pos > -1){
// var val = sheet.getRange("J" + (pos + 1)).getValue() + 1;
var title = sheet.getRange("A" + (pos + 1)).getValue();
// sheet.getRange("J" + (pos + 1)).setValue(val);
}
var newFile = DriveApp.getFileById(fileId).makeCopy('Copy of '+ title);
return {
title: newFile.getName(),
url: newFile.getUrl()
}
The problem is that when a user tries to make a copy he/she is getting an error `No item with the given ID could be found, or you do not have permission to access it.'
I have commented 2 lines, I thought that the issue was due to posting back the download increment to the original spreadsheet, but as it turns out this was not the only issue there.
It works fine inside the origin account BTW.
I have asked Add-on Advisor for help but was readdressed here instead.
Please help
When a user installs an add-on it executes under that user's account. As such they cannot access your template spreadsheets unless they are granted permission to do so.
If you are comfortable sharing files with your end-users, you can programmatically grant/revoke permission to your templates by using an Advanced Service. These advanced services are merely wrappers for their corresponding APIs so in this case you need to leverage the Drive API V2 documentation to figure out how to add/remove permissions. The following guides and reference should help:
Manage Sharing
Drive API V2 Permissions
If sharing files is undesirable, you can opt to use a Service Account. Service Accounts are a special kind of account that you can create from the GCP API console. You can grant the service account access to your templates and then use the service account to retrieve the spreadsheet template as a Spreadsheet resource object in JSON format using the spreadsheet API. You can then use this resource object to create a user-owned copy of the spreadsheet without explicitly sharing your template. You can find more info on service accounts by checking other threads here on stackoverflow.

DriveApp conversion from DocX to PDF fails

I'm trying to convert an existing .DOCX file on my Google Drive.
It works up until the new (PDF) file should be created, then I get this error message:
"Conversion from application/vnd.openxmlformats-officedocument.wordprocessingml.document to application/pdf failed".
The relevant DOCX file should not be corrupted as it can be opened by Google Document and manually convertred to PDF from there.
I have enabled Drive API (both in Drive and in API Console)
Anyone else seen this problem?
Code:
function convertPDF(fileid) {
var docId = fileid;
var f=DriveApp.getFileById(docId);
var n=f.getName();
var docFolder = f.getParents().next().getId();
var docblob = f.getAs('application/pdf');
n=n.replace(".docx",".pdf");
docblob.setName(n);
var bn=docblob.getName();
var t=docblob.getContentType();
Logger.log(bn+"-->"+t); // <-- works
var file = DriveApp.createFile(docblob); // <<- error msg here
var fileId = file.getId();
moveFileId(fileId, docFolder);
return (fileId);
}
At Google, it cannot be directly converted from docx to pdf. But, after it converted docx to Google document, it can convert to pdf. In order to convert docx to Google document, you can use Drive API using Advanced Google services. For this, please enabling Drive API of Advanced Google services as follows.
In the script editor, select Resources > Advanced Google services
In the dialog that appears, click the on/off switch for Drive API v2.
Click OK button.
The detail information of Advanced Google services is here.
I think that enabling Drive API at API console has already been done, because you have already used DriveApp on your script. Google automatically enables Drive API on API console when DriveApp is used in the script.
The modified script using Drive API is as follows.
Modified script :
function convertPDF(docx_id) {
var docx = DriveApp.getFileById(docx_id);
var docFolder = docx.getParents().next().getId();
var n = docx.getName();
var res = Drive.Files.insert({ // Here, Drive API of Advanced Google services is used.
"mimeType": "application/vnd.google-apps.document",
"title": n
}, docx.getBlob());
var f = DriveApp.getFileById(res.id).getBlob();
var docblob = f.getAs('application/pdf');
n=n.replace(".docx",".pdf");
docblob.setName(n);
var bn=docblob.getName();
var t=docblob.getContentType();
Logger.log(bn+"-->"+t);
var file = DriveApp.createFile(docblob);
var fileId = file.getId();
Logger.log(fileId)
moveFileId(fileId, docFolder);
return (fileId);
}
I don't know about moveFileId(). So if an error occurs at this line, please check the function.
Note :
In this case, Google document is created as a temporally file for converting to PDF. The filename is the same to the docx file. So if it is not required for you, please delete it.