Google Apps Script - Impossible operation with an element inside a shared drive - google-apps-script

I need your help on something.
I did a function which goal is to make a copy of a template file and put it in a folder in a shared drive. The problem is that the program returns :
"Exception: This operation cannot be performed on an item in a shared Drive"
Yet, I have all the permissions in that shared drive so I don't understand.
I did several tweaks and I've founded that removeFile and addFile are parts of the problem. If I don't run them, the folder is created and the copy is made. But I still need the file to be moved.
I hope someone can help me with this.
PS : You can fin my code below.
function makeCopyFile(folderID, fileID, newName) {
var getFolder = DriveApp.getFolderById(folderID);
var file = DriveApp.getFileById(fileID);
var copyFile = file.makeCopy();
var newFile = copyFile.setName(newName);
// Remove the file from all parent folders
var parents = newFile.getParents();
while (parents.hasNext()) {
var parent = parents.next();
parent.removeFile(newFile);
}
getFolder.addFile(newFile);
};

The problem is that you are trying to delete a file on a shared drive with DriveApp
It will work if you do it instead with the Advanced Drive service where you can specify "supportsAllDrives": true
So, after enabling the Advanced Service: replace
var parents = newFile.getParents();
while (parents.hasNext()) {
var parent = parents.next();
parent.removeFile(newFile);
}
through
var id = copyFile.getId();
// or alternatively `var id = newFile.getId();` - because it is the same
Drive.Files.remove(id,{"supportsAllDrives": true})
As for getFolder.addFile(newFile);
Once you remove a file, you cannot add it back anymore. Also, I do not understand your motivation for it - copyFile.setName(newName); is enough to rename the file - you do not need to delete the file with the old name and insert the one with the new name.
UPDATE
If your goal is to copy a file to a team drive folder, you can do it easily as following with the Drive API:
function makeCopyFile(folderID, fileID, newName){
var resource = {
"parents": [
{
"id": folderID
}
],
"title": newName
}
Drive.Files.copy(resource, fileID, {"supportsAllDrives": true})
}

Related

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.

Move Folder Using App Script

So i'm brand new to JS and trying to build a program for internal operation at the company I work for. Below is the first bit of my code. I have a created a google form, and set a trigger so that when the submit button is clicked the below code will start. The trigger works and the first two functions work, but I cannot seem to get the Karrie folder to move from the root to the sub folder id (I do have the correct ID but omitted it from this post). Maybe I'm doing this all backwards so any help would be appreciated.
// Create Karrie Folder In Root
function start() {
var sourceFolder = "Property Template";
var targetFolder = "Karrie";
var source = DriveApp.getFoldersByName(sourceFolder);
var target = DriveApp.createFolder(targetFolder);
if (source.hasNext()) {
copyFolder(source.next(), target);}}
// Adds Property data to Karrie Folder
function copyFolder(source, target) {
var folders = source.getFolders();
var files = source.getFiles();
while(files.hasNext()) {
var file = files.next();
file.makeCopy(file.getName(), target);}
// Adds Photo Folders to Karrie Folder
while(folders.hasNext()) {
var subFolder = folders.next();
var folderName = subFolder.getName();
var targetFolder = target.createFolder(folderName);
copyFolder(subFolder, targetFolder);}}
// Moves Karrie folder to propertie folder
function MoveFiles(){
var files = DriveApp.getFolderByName("Karrie");
var destination = DriveApp.getFolderById("ID NA");
destination.addFolder(files);}
Debugger
I understood that MoveFiles() in your script doesn't work. If my understanding is correct, how about this modification?
Modification points :
In order to move a folder, it required to move the parent of the folder.
But when addFolder() is used to the folder, the folder has 2 parents.
So original parent has to be removed.
There is no getFolderByName(). Please use getFoldersByName().
Modified script :
function MoveFiles(){
var folder = DriveApp.getFoldersByName("Karrie").next();
var destination = DriveApp.getFolderById("ID NA");
destination.addFolder(folder);
folder.getParents().next().removeFolder(folder);
}
Note :
In this script, The folder and parent of "Karrie" supposes only one in the Google Drive, respectively.
References :
getFoldersByName()
addFolder()
removeFolder()
If I misunderstand your question, please tell me. I would like to modify it.
Edit 1 :
When you run this function, does the ID of Logger.log(r) show the folderId of "Karrie"?
function sample() {
var r = DriveApp.getFoldersByName("Karrie").next().getId();
Logger.log(r)
}
Edit 2 :
Please confirm and consider the following points.
Confirm the folder name and the existence of the folder again.
Run sample() and confirm folder ID of "Karrie".
Confirm whether there are no functions with the same name.
Can I ask you the folder ID of "Karrie"? If you cannot retrieve the folder ID using DriveApp.getFoldersByName("Karrie").next().getId(), I propose to use directly the folder ID of "Karrie".
Updated on March 30, 2022:
In the current stage, the files and folders can be moved using moveTo. Ref This method has added on July 27, 2020. When moveTo is used, the above script can be modified as follows.
function MoveFiles(){
var folder = DriveApp.getFoldersByName("Karrie").next();
var destination = DriveApp.getFolderById("ID NA");
folder.moveTo(destination);
}
Reference:
moveTo(destination) of Class Folder
As of 2020, the easier way I found to move a file is to use this function:
function moveToFolder(file, sourceFolder, destFolder) {
var file = DriveApp.getFileById(file.getId());
destFolder.addFile(file);
sourceFolder.removeFile(file);
}
Because of the first line, you can convert your Spreadsheet, Google Form, Document or others Google objects into a File.
Then we just use methods of the Folder class : https://developers.google.com/apps-script/reference/drive/folder
Notes:
If you created your file by using .create() your sourceFolder will be DriveApp.getRootFolder().
If you have multiple files to move, just iterate over them.

Moving Files In Google Drive Using Google Script

I'm trying to create documents using information posted through Google forms, then once the document is created I would like to move the document into a shared folder for people to view.
At the moment I have the script taking all of the information from the Google Forms linked spreadsheet.
Using that information I'm using the following code to create the document:
var targetFolder = DriveApp.getFolderById(TARGET_FOLDER_ID);
var newDoc = DocumentApp.create(requestID + " - " + requestSummary);
This is creating the document successfully in my Google Drive root folder, but I can't seem to move it where I want to move it to.
I've seen a lot of posts suggesting use stuff like targetFolder.addFile(newDoc) but that doesn't work, similarly I've seen examples like newDoc.addToFolder(targetFolder) but again this isn't working for me.
It seems that all the online questions people have already asked about this are using the previous API versions that are no longer applicable and these methods do not apply to the new DriveApp functionality.
What I would like, if possible, is to create the new document as above so that I can edit the contents using the script, then be able to move that file to a shared folder. (From what I understand there is no 'move' function at present, so making a copy and deleting the old one will suffice).
If we make a copy of the file and trash the original, it would change the file URL and also the file sharing settings won't be preserved.
In Drive, it is possible to add a file to multiple folders with the .addFolder() method of DriveApp service. You can add the file to the target folder and then remove the file from the immediate parent folder.
function moveFiles(sourceFileId, targetFolderId) {
var file = DriveApp.getFileById(sourceFileId);
var folder = DriveApp.getFolderById(targetFolderId);
file.moveTo(folder);
}
This is my first post! I know this has been answered a few times, but I actually came across this question while working on my project, and while reviewing the Apps Script documentation, I figured out a concise way to do it. A variation of some1's answer.
var file = DriveApp.getFileById(fileid);
DriveApp.getFolderById(folderid).addFile(file);
DriveApp.getRootFolder().removeFile(file);
Hope it helps!
There is no direct method in the File or Folder Classes to move files from one folder in Google Drive to another. As you mentioned you can copy the file to another folder with the method makeCopy() and then delete it with setTrashed(), the code should look like this:
var targetFolder = DriveApp.getFolderById(TARGET_FOLDER_ID);
var newDoc = DocumentApp.create(requestID + " - " + requestSummary); // Creates the Document in the user's Drive root folder
// Modify the new document here, example:
// var body = newDoc.getBody();
// body.appendParagraph("A paragraph.");
// newDoc.saveAndClose();
var driveFile = DriveApp.getFileById(newDoc.getId()); // Gets the drive File
driveFile.makeCopy(newDoc.getName(), targetFolder); // Create a copy of the newDoc in the shared folder
driveFile.setTrashed(true); // sets the file in the trash of the user's Drive
EDIT:
In a second thought and taking into account Ruben's comments. I agree that it is a better practice to implement Amit's answer.
It looks like there is now a moveTo() function with the Drive API (advanced services) that makes it easy to move files:
moveTo(destination)
Moves this item to the provided destination folder.
The current user must be the owner of the file or have at least edit
access to the item's current parent folder in order to move the item
to the destination folder.
Here is some code I used to move all files from the "screenshot input" folder to the "screenshot processed" folder:
var inputFolder = DriveApp.getFolderById(SCREENSHOT_INPUT_FOLDER_ID);
var processedFolder = DriveApp.getFolderById(SCREENSHOT_PROCESSED_FOLDER_ID);
var files = inputFolder.getFiles();
while (files.hasNext()) {
var file = files.next();
file.moveTo(processedFolder);
}
A bit safer approach compared to the previous ones:
If you remove link to the file first, then you will not be able to addFile.
If file is already located in the target folder, then the approach provided by Amit (https://stackoverflow.com/a/38810986/11912486) only removes file.
So, I suggest to use the following approach:
function move_file(file_id, target_folder_id) {
var source_file = DriveApp.getFileById(file_id);
var source_folder = source_file.getParents().next();
if (source_folder.getId() != target_folder_id) {
DriveApp.getFolderById(target_folder_id).addFile(source_file);
source_folder.removeFile(source_file);
}
}
can be improved by:
javascript camel style
multiple locations validation
Use File.moveTo(destination).
var newFileId = newDoc.getId();
var newFile = DriveApp.getFileById(newFileId);
newFile.moveTo(targetFolder);
Try this:
var file = DriveApp.getFileById(newDoc.getId());
targetFolder.addFile(file);
//DriveApp.getFolderById('root').removeFile(file); // remove from root
This question has been answered, but here is a slightly different configuration:
function moveFile(parameterObject) {
var currentFolderID,file,fileToMoveID,sourceFolder,targetFolder,targetFolderID;
fileToMoveID = parameterObject.fileToMoveID;
currentFolderID = parameterObject.currentFolderID;
targetFolderID = parameterObject.targetFolderID;
file = DriveApp.getFileById(fileToMoveID);//Get the file to move
if (!file) {
functionToHandleThisKindOfThing("there is no file");
return;
}
if (currentFolderID) {//The folder ID holding the current file was passed in
sourceFolder = DriveApp.getFolderById(currentFolderID);
} else {//No ID for the current folder
sourceFolder = file.getParents();
if (sourceFolder) {
if (sourceFolder.hasNext()) {
sourceFolder = sourceFolder.next();
}
}
}
targetFolder = DriveApp.getFolderById(targetFolderID);
targetFolder.addFile(file);
sourceFolder.removeFile(file);
}
function testCode() {
var o;
o = {
'fileToMoveID':"File ID of file to Move",
"targetFolderID":"ID of folder to Move to"
}
moveFile(o);
}
The script transfers all your personal files to a shared disk (Team drive). Saves the folder structure.
DRIVE_FOLDER_ID = '111aaa'; // Folder ID on the shared drive
function start() {
var files = DriveApp.searchFiles('"me" in owners');
while (files.hasNext()) {
var file = files.next();
newPath = fileMoveWithPath(file, DRIVE_FOLDER_ID);
console.info("New path: ", getFullPath(newPath));
}
}
function fileMoveWithPath(file, root) {
var folders = [],
parent = file.getParents();
// Проходим по иерархии папок текущего файла до корня
while (parent.hasNext()) {
parent = parent.next();
folders.push(parent);
parent = parent.getParents();
}
console.info("Old path: ", getFullPath(file));
if (folders.length > 0)
targetPath = makeNewPath(folders, DriveApp.getFolderById(root));
else
targetPath = DriveApp.getFolderById(root);
if (targetPath) {
targetFile = file.moveTo(targetPath);
return targetFile;
};
return;
}
function makeNewPath(folders, newroot) {
var f = folders.pop();
var query = "'" + newroot.getId() + "' in parents and title = '" + f.getName() + "' and mimeType='application/vnd.google-apps.folder' "
var targetFolder = DriveApp.searchFolders(query);
if (targetFolder.hasNext())
targetFolder = targetFolder.next()
else
targetFolder = newroot.createFolder(f.getName());
if (folders.length > 0)
return makeNewPath(folders, targetFolder)
else
return targetFolder;
}
function getFullPath(file) {
var folders = [],
parent = file.getParents();
while (parent.hasNext()) {
parent = parent.next();
folders.push(parent.getName());
parent = parent.getParents();
}
if (folders.length) {
return '> /' + folders.reverse().join("/") + '/' + file.getName();
}
return '> /' + file.getName();
}

How to Create a Spreadsheet in a particular folder via App Script

Can anybody help me out,
I want to create a Spreadsheet through App Script in a particular folder. How to do that.
Presently I am doing as follow:
var folder = DocsList.getFolder("MyFolder");
var sheet = SpreadsheetApp.create("MySheet");
var file = DocsList.getFileById(sheet.getId());
file.addToFolder(folder);
file.removeFromFolder(file.getParents()[0]);
It is not working.......
As suggested by #Joshua, it's possible to create a Spreadsheet (in a specific folder) with the Advanced Drive Service (you'll need to activate this if you haven't already, by going into Services +, find Drive API and click Add).
var name = 'your-spreadsheet-name'
var folderId = 'your-folder-id'
var resource = {
title: name,
mimeType: MimeType.GOOGLE_SHEETS,
parents: [{ id: folderId }]
}
var fileJson = Drive.Files.insert(resource)
var fileId = fileJson.id
No need to move files around with this method !
folder = DriveApp.getFolderById("FOLDER_ID")
var ss = SpreadsheetApp.create("SPREADSHEET_NAME")
DriveApp.getFileById(ss.getId()).moveTo(folder);
You may use the above code to achieve the same without using advanced drive services
Since you can no longer create Google Docs (Docs or SpreadSheets) using DriveApp, nor use addToFolder because DocList is deprecated. There is only one way to create or "move" Google Docs or Google SpreadSheets..
//"Move" file to folder-------------------------------//
var fileID = '12123123213321'
var folderID = '21321312312'
var file = DriveApp.getFileById(fileID).getName()
var folder = DriveApp.getFolderById(folderID)
var newFile = file.makeCopy(file, folder)
//Remove file from root folder--------------------------------//
DriveApp.getFileById(fileID).setTrashed(true)
As you can see this DOES NOT move the file, it makes a copy with the same name in the folder you want and then moves the original file to the trash. I'm pretty sure there is no other way to do this.
The other answer is a bit short (and not very explicit).
While your approach is logic and should work if you replace
file.removeFromFolder(file.getParents()[0]);
with
file.removeFromFolder(DocsList.getRootFolder());
there is a better way to do the same job using the new Drive app and the Folder Class, Folder has a method to create a file and you can specify the file type using the mimeType enum.
Code goes like this :
function myFunction() {
var folders = DriveApp.getFoldersByName('YOUR FOLDER NAME'); // replace by the right folder name, assuming there is only one folder with this name
while (folders.hasNext()) {
var folder = folders.next();
}
folder.createFile('new Spreadsheet', '', MimeType.GOOGLE_SHEETS); // this creates the spreadsheet directly in the chosen folder
}
In July 27, 2020 there have been these updates:
The File class now has the following methods:
file.getTargetId(): Gets a shortcut's file ID.
file.getTargetMimeType(): Returns the mime type of the item a shortcut points to.
file.moveTo(destination): Moves a file to a specified destination folder.
The Folder class now has the following methods:
folder.createShortcut(targetId): Creates a shortcut to the provided Drive item ID, and returns it.
folder.moveTo(destination): Moves an item to the provided destination folder.
The following Folder class methods have been deprecated:
addFile(File)
addFolder(Folder)
removeFile(File)
removeFolder(Folder)
https://developers.google.com/apps-script/releases/#july_27_2020
So you can create a Spreadsheet file in a folder using file.moveTo(destination) method:
function createSpreadSheetInFolder(ss_new_name, folder_dest_id) {
var ss_new = SpreadsheetApp.create(ss_new_name);
var ss_new_id = ss_new.getId();
var newfile = DriveApp.getFileById(ss_new_id);
newfile.moveTo(DriveApp.getFolderById(folder_dest_id))
return ss_new_id;
}
var file_name = 'SPREADSHEET NAME';
var folder_id = 'DESTINATION FOLDER ID';
var new_ssId = createSpreadSheetInFolder(file_name, folder_id)
You can create a spreadSheet and then add it to the folder.
function createSpreadSheetInFolder(name,folder){
var ss = SpreadsheetApp.create(name);
var id = ss.getId();
var file = DriveApp.getFileById(id);
folder.addFile(file);
return ss;
}
folderId='your_folder_id'
name='my_new_ss'
folder=DriveApp.getFolderById(folderId)
createSpreadSheetInFolder(name,folder)
By using the folder.addFile method there's no need to use a temp file (no need to duplicate and remove file). Pretty straightforward !
I finally got the answer to my question. The following works
var file = DocsList.getFileById(sheet.getId());
var folder = DocsList.getFolder("MyFolder");
file.addToFolder(folder);
// remove document from the root folder
folder = DocsList.getRootFolder();
file.removeFromFolder(folder);
What is not working? Use getRootFolder in the last line.
Creating a new spreadsheet in a file can be done using this link as a reference.
createFile(name, content, mimeType)
Therefore using the enum MimeType we can do:
var folder = DriveApp.getFolderById("your-folder-id");
folder.createFile("My File Name","",MimeType.GOOGLE_SHEETS)

create a Google spreadsheet in a particular folder

this question teaches how to adjust the folders of a file to move it into a particular folder, and out of the root.
It also suggests using folder.createFile to create it directly in the desired folder... but it appears createFile only applies to Docs... is there a way to create a spreadsheet directly in a particular folder?
Since DocsList is deprecated, currently the following solution works:
var folder=DriveApp.getFoldersByName(folderName).next();//gets first folder with the given foldername
var file=SpreadsheetApp.create(fileName);
var copyFile=DriveApp.getFileById(file.getId());
folder.addFile(copyFile);
DriveApp.getRootFolder().removeFile(copyFile);
Create spreadsheets using Apps Script does not work with regular Google accounts and Google Apps Standard Edition accounts. However, you can always make a copy of a dummy spreadsheet and then modify it accordingly using Google Apps Script. Here is a sample code.
function createSpreadsheetInfolder() {
var dummySS = DocsList.getFileById('ID of dummy SS');
var myCopy = dummySS.makeCopy('My new file');
//Get your target folder
var folder = DocsList.getFolder('ID or path of folder');
myCopy.addToFolder(folder);
}
The answers already given don't seem to work, maybe they're outdated.
Here's my answer (worked for me).
var folders=DriveApp.getFoldersByName("existingFolder");
while (folders.hasNext()) {
var folder = folders.next();
var file=SpreadsheetApp.create("the file that must be in a folder");
var copyFile=DriveApp.getFileById(file.getId());
folder.addFile(copyFile);
DriveApp.getRootFolder().removeFile(copyFile);
}
Admittedly, my answer is derived from the previous ones, none of which did exactly what I needed. This function has been tested with current Google Apps Script and spreadsheet documents. With duplicate folder names it will work, but move the file to the first folder returned by the API.
// Arguments are a file object and a folder name.
function moveFileToFolder(file, folderName)
{
var folders = DriveApp.getFoldersByName(folderName);
var copyFile = DriveApp.getFileById(file.getId());
if (folders.hasNext()) {
var folder = folders.next();
folder.addFile(copyFile);
DriveApp.getRootFolder().removeFile(copyFile);
}
}
Now it is possible to create Google spreadsheet in a specific folder.
For that you need to use advanced drive service v2. Enable it from Advanced Services menu in google app script editor.
var oFile = placeGoogleSpreadsheetInSpecificFolder("my file", "target folder id");
var oSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(oFile.getId()));
function placeGoogleSpreadsheetInSpecificFolder(sSpreadsheetName, sTargetFolderId) {
var oGeneratedFile = Drive.Files.insert({
"mimeType": "application/vnd.google-apps.spreadsheet",
"parents": [{
id: sTargetFolderId
}],
"title": sSpreadsheetName
});
return oGeneratedFile;
}
This solution worked perfectly fine for me.
This code works for me, I think there is no way to avoid the "remove from root" step, at least not that I know.
function createSpreadsheet() {
var folder = DocsList.getFolder('GAS');// this value as test for me
var root = DocsList.getRootFolder()
var newFileId = SpreadsheetApp.create('testNewSS').getId();// this defines the name of the new SS
var newFile = DocsList.getFileById(newFileId); // begin the 'move to folder process'
newFile.addToFolder(folder);
newFile.removeFromFolder(root);// had to do it in 2 separate steps
}
This question has beeen answerd in.
https://stackoverflow.com/a/19610700
It is a nice solustion.
folder.createFile("name", '',MimeType.GOOGLE_SHEETS);
using anything but a empty string leaves me with a error.
Just a 'heads up' for people who are reading this old answers like me. The most solutions are already deprecated. You have to use "moveTo" and you will not need to remove from root-directions or something else.
Have a look at this link for more details.
My solution to do so:
/**
* function createSS
* #description: Creates a new SpreadSheet with imported values and directly in provided folder
* #param {string} ssName name of SpreadSheet
* #param {object} sheetData values as range
* #param {object} ssFolder reference to a folder
*/
function createSS(ssName, sheetData, ssFolder = false){
var ss = SpreadsheetApp.create(ssName);
ss.setSpreadsheetLocale("de");
if(ssFolder){
// Move to provided folder
var file = DriveApp.getFileById(ss.getId());
file.moveTo(ssFolder);
}
var sheet = ss.getActiveSheet();
sheet.getRange(1, 1, sheetData.length, sheetData[0].length).setValues(sheetData);
return sheet;
}
Example with folder assignment:
var folders = DriveApp.getFileById(ssFileId).getParents();
var folder = folders.next();
// add new file (directly in correct folder)
var sourceSheet = createSS(newFileName, relevantValues, folder);
( This is only working this way, if you're sure your file is in 1 folder only. Otherwise, do it with .hasNext() and a loop )