Google apps script Permissions on created file in shared folder - google-apps-script

I have a script which creates a file in a shared Google Drive folder, this is the script:
var spr = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Klantenlijst');
var data = spr.getDataRange().getValues();
var klanNumbers = data; //some var declared before this piece of code
var file = DriveApp.createFile(fileName, JSON.stringify(klanNumbers));
This file needs to be updated frequently, to do so I remove the existing file and create a new one to replace it (with the new data). The problem is that when I try to perform the setTrashed action as a user other than the file owner, this error pops up:
You do not have authorization to perform that action.
Any ideas on how to fix that? :)
Thanks!
EDIT: I can delete the file in drive manually with the other users.
I have seen this article but I totally disagree with the conclusion that the problem is "too localized". Look around on Google and you will find cases with the same problem without a decent solution.
Workaround for this moment:
Rename the file
Move it to another folder
Create the new file in the old folder
I will not delete this post so people can add other ideas here.

You can only trash files that you own. When you delete the file manually (using the GUI to trash the file), it appears that you've trashed the file, but you are not actually setting the trashed flag on it. Rather, you are removing it from view in your own Google Drive, without affecting anyone else. The owner still sees it shared with you, and any other collaborators are unaffected. In fact, you can still see the file if you search for it by its full name, or use one of the alternate views such as the "Recent" file list, or use the file's URL.
To get the same effect from a script, use removeFile().
Here's a utility that will treat a file differently for owners than collaborators, to either trash or remove it.
/**
* Remove the given file from view in the user's Drive.
* If the user is the owner of the file, it will be trashed,
* otherwise it will be removed from all of the users' folders
* and their root. Refer to the comments on the removeFile()
* method:
*
* https://developers.google.com/apps-script/reference/drive/drive-app#removeFile(File)
*
* #param {File} file File object to be trashed or removed.
*/
function deleteOrRemove( file ) {
var myAccess = file.getAccess(Session.getActiveUser());
if (myAccess == DriveApp.Permission.OWNER) {
// If I own the file, trash it.
file.setTrashed(true);
}
else {
// If I don't own the file, remove it.
var parents = file.getParents();
while (parents.hasNext()) {
// Remove the file from the current folder.
parents.next().removeFile(file);
}
// Remove the given file from the root of the user's Drive.
DriveApp.removeFile(file);
}
}
Example:
function test_deleteOrRemove() {
var files = DriveApp.getFilesByName('536998589.mp3');
while (files.hasNext()) {
var file = files.next();
deleteOrRemove( file );
}
}

Related

Remove files from GDrive Trash

I have backup scripts that are copying my files into GDrive and it's also deleting files that are older than X days. But this is filling my bin with lot of these files. I want to remove them from trash bin.
I wrote this code:
function getTrashedFiles() {
//find every file in bin
var trashedSearch = DriveApp.searchFiles('trashed=true');
while (trashedSearch.hasNext()) {
var file = trashedSearch.next();
if (file.getName().indexOf("DBKLP") >= 0) {
//this is correctly printing files that I want to remove
Logger.log(file.getName());
//this should remove from bin
DriveApp.removeFile(file);
}
}
}
This code correctly finds every file, that starts at "DBKLP". But when it runs, it's not removing files from bin. It's just removing them 'once again', so their date of remove is basically "now".
I can't find a way to "remove from bin".
The AppScript DriveApp does not support it, but you can do it with the Drive API v3. As the documentation states, you'd need to use the DELETE HTTP method; alike this you could even skip trashing these files altogether. Here's an example (mine) of how to use Google API with AppScript (that's another API, but at least it's a good starting point). The documentation combined with a rough example should hopefully push you into the right direction.
From the documentation: removeFile(child) does not delete the file, but if a file is removed from all of its parents, it cannot be seen in Drive except by searching for it or using the "All items" view.
Have you tried Drive.Files.remove() instead of DriveApp.removeFile()?
To enable it, in the script editor, you have to click on the + button at "Services". Check here: imgur.com/cVFCAXQ (or, in the old editor navigate to Resources > Advanced Google Services > Drive API).
I have a similar script that seems to work. Notice that it has to find all files and folders because they are treated separately.
// Delete all files from trash older than 1 month
function cleanOldTrash() {
var files = DriveApp.getTrashedFiles();
var folders = DriveApp.getTrashedFolders();
var lastMonth = new Date();
lastMonth.setDate(lastMonth.getDate() - 30);
while (files.hasNext()) {
var file = files.next();
if (file.getLastUpdated() < lastMonth ) {
Drive.Files.remove(file.getId());
}
}
while (folders.hasNext()) {
var folder = folders.next();
if (folder.getLastUpdated() < lastMonth) {
Drive.Files.remove(folder.getId());
}
}
}
I actually do have a problem with this script when files are stored on the trash of Team Drives that I don't have permission to access, but I'll try to get more help with that in a separate question. I'll update this answer if needed.

Google Apps Script returning all files in Drive, rather than files in a folder

I have a folder in Google Drive, where there are some files. I want to retrieve all of these file information, and that's all I want. There are many other files in other folders, but I don't need them. However, my following code is showing all files' names. How can I limit the result only to the files in the specified folder?
//Folder ID
var myFolderId = "XXXXXXXXXXXXXXXXXXXXXXXXX";
var files = DriveApp.getFolderById(myFolderId).getFiles();
while(files.hasNext())
{
var file = files.next();
Logger.log(file.getName());
}
For now, there are only 4 files in this folder, but the log shows the entire file names in my Google Drive. I don't know what I am doing wrong, because I am specifying the folder with ID. How come it is returning every file in Google Drive?
Answer:
Your code already works great!
Further information:
Google Apps Script doesn't allow the running of global code as specific functions need to be declared and run, at least, not in the same was as locally-run code. The global variables are static and can not be changed in the runtime.
Things you can do:
As Cooper mentioned in their comment - all you need to do is put your code in a function, call the function and you're set. The code works great:
function functionName(){
var myFolderId = "XXXXXXXXXXXXXXXXXXXXXXXXX";
var files = DriveApp.getFolderById(myFolderId).getFiles();
while(files.hasNext())
{
var file = files.next();
Logger.log(file.getName());
}
}
As a side-note: if you really want to use a global scope (or at least. use an emulation of it), the PropertiesService is available to you, though you'd still have to set them in a function and run a function to retrieve them:
function setVars(){
PropertiesService.getScriptProperties().setProperty('myFolderId', 'XXXXXXXXXXXXXXXXXXXXXXXXX');
}
function listFiles(){
var files = DriveApp.getFolderById(PropertiesService.getScriptProperties().getProperty('myFolderId')).getFiles();
//continue this code
}

Trying to copy files I do not own from a shared drive to personal drive

I am an educator and have content I created and co-created on a school google drive. I am leaving the school and trying to take a copy of the lessons I created and co-created. The school drive has files where I am the owner and not the owner in a main folder with subfolders. Our tech person said to try the code below, but I am getting an error and no further support from my school.
I have tried sharing files within the google drive application and then have gotten as far as trying to use the code below by inputting my folder names, but it give me an error on line 22 of the code. I don't know what to do other than open each individual file and manually save it. I only have access to the shared drive until July 26. PLEASE HELP!
function duplicate() {
var sourceFolder = "5-8 Shared Humanities";
var targetFolder = "Copy of humanities curriculum from FSMN";
var source = DriveApp.getFoldersByName(sourceFolder);
var target = DriveApp.createFolder(targetFolder);
if (source.hasNext()) {
copyFolder(source.next(), target);
}
}
function copyFolder(source, target) {
var folders = source.getFolders();
var files = source.getFiles();
while(files.hasNext()) {
var file = files.next();
file.makeCopy(file.getName(), target);
}
while(folders.hasNext()) {
var subFolder = folders.next();
var folderName = subFolder.getName();
var targetFolder = target.createFolder(folderName);
copyFolder(subFolder, targetFolder);
}
}
I expected the code to copy the file structure and all files from all owners from the shared drive to the new personal drive I created. It seems like I am still running into file permissions issues for files I do not own. I am getting the following error:
Access denied: DriveApp. (line 22, file "Code")
I tried your script on stuff that was shared with me, both with modification permission and just read-only, and it worked fine.
That error is usually about drive apps being disabled (which prevents scripts too). See link
But since, it works for the files you own, I'm guessing that those you don't are either in 'read-only' or 'comment' mode. As is it wouldn't prevent you from making a copy, but when sharing the owner can check a box to disable print, download and copy options for commenters and readers.
So I think your only solution is to ask the owner to uncheck that box :/
To copy the files into your personal storage than do the following:
Make a shortcut of the folder in your personal drive.
Create a new Google Colaboratory notebook.
Mount your drive using following code:
from google.colab import drive
drive.mount("/content/drive")
Next, Enter Command:
!cp -rp '/content/drive/JAVA_ANDROID/*' '/content/drive/My Drive'
Where,
JAVA_ANDROID is name of the shortcut of the folder, put the name of the folder you desire to copy and append /* to copy all files at that location.
Next is path of the folder where copied files are to be saved, which is /content/drive/My Drive in this case.
Edit: I have noticed that in some cases adding /* doesn't work. So if it doesn't work the above specified way. Just don't append * to the shortcut name and try. So in this case the above command would be:
!cp -rp '/content/drive/JAVA_ANDROID/' '/content/drive/My Drive'

Implement a folder move function in Google Dirve

I am very new to writing any types of scripts and am trying to write a Google Apps Script to search through folders for a particular string, and if found move the folder to another folder.
I can copy a folder, but not move it since the copied folder has the same ID as the original, and deleting one deletes the other. From what I have been able to figure out I can implement this by deleting the right child from the folder. I've tried this in the APIs explorer and it does what I expect.
The problem is that I can't for the life of me figure out how to implement the example given in the Google Drive SDK page in script.google.com. If I paste in the example code I get a Missing name after . operator error. None of the scripting commands let me delete a child.
Likely this is a simple issue and arises from my trying to learn some JavaScript by doing this project, but I would appreciate any help or advice on how to implement a move function in Google Script.
The specific question is: How do I implement a move folder script? This is what I am trying, but it removes both folders:
while (contents.hasNext()) {
folder = contents.next();
name = folder.getName();
if (name.search("XYZ") != -1) {
moveFolder = folder;
targetFolder.addFolder(moveFolder) moveFolder.setName('DEL_' +
moveFolder.getName());
removeFolder(moveFolder);
}
}
First, let's clear up some confusion. Two Drive-related "Services" are provided for use within Google Apps Script, Drive Services, and Docslist Services. The documentation you've referenced is for the Google Drive SDK, which is not the same thing. If you're programming in Google Apps Script, ignore the SDK documentation.
Next, let's talk about what a folder or file is on Drive, and how that affects the concepts of "copy", "move" and "delete". A file or folder may have a folder as a parent. In fact, it may have multiple parents. If we want to have the same file or folder "copied" to another folder, we just add it as a child to the target folder. At this point, it may look like we have two items with the same ID - but what we actually have is one item with two parents. If we then remove the file or folder from the original folder (disown it, in a way), it will be left with one parent, and this will appear like a "move". The file or folder will not have been "deleted". On the other hand, if instead of removing the parent / child relationship, we DO delete the file or folder, it will appear that we have "deleted both folders", as you have described.
So, your "move" script needs to add a new parent / child relationship, and remove the other.
The Drive starter script that is presented when you open a new script in the editor contains a function called moveFileToFolder().
This function does what you are looking for, except that the thing it's moving is a file. However, the methods that are used for a file have cousins for folders, so you can just do some replacement to end up with moveFolderToFolder():
/**
* This script moves a specific folder into a given folder, and removes the folder
* from all other folders that previously contained it. For more information on
* interacting with files, see
* https://developers.google.com/apps-script/drive/file
*/
function moveFolderToFolder(sourceFolderId, targetFolderId) {
var targetFolder = DriveApp.getFolderById(targetFolderId);
var sourceFolder = DriveApp.getFolderById(sourceFolderId);
var currentFolders = sourceFolder.getParents();
while (currentFolders.hasNext()) {
var currentFolder = currentFolders.next();
currentFolder.removeFolder(sourceFolder);
}
targetFolder.addFolder(sourceFolder);
};
If you want to deal with folder Names, then you need to use getFoldersByName() to collect all matching folders, then iterate using next() into the list. Here's how that function could be modified to use folder names. To simplify things, it assumes that there is just ONE folder with the given name, and throws an exception if that's not the case. Once we have a handle on the folders we're interested in, the balance of the function remains the same as the previous example.
function moveNamedFolderToFolder(sourceFolderName, targetFolderName) {
var matchedFolders = DriveApp.getFoldersByName(sourceFolderName);
if (matchedFolders.hasNext()) {
var sourceFolder = matchedFolders.next();
if (matchedFolders.hasNext()) throw new Error( "Source Folder Name not unique" );
}
matchedFolders= DriveApp.getFoldersByName(targetFolderName);
if (matchedFolders.hasNext()) {
var targetFolder = matchedFolders.next();
if (matchedFolders.hasNext()) throw new Error( "Target Folder Name not unique" );
}
var currentFolders = sourceFolder.getParents();
while (currentFolders.hasNext()) {
var currentFolder = currentFolders.next();
currentFolder.removeFolder(sourceFolder);
}
targetFolder.addFolder(sourceFolder);
};

How do I copy & move a file to a folder in Google Apps Script?

Is there a way to copy and rename a file and move that copy to a particular folder without having a second copy in the root folder? I have used combinations of copy, rename, move in different order, but each time, I still end up with a copy of the renamed file the root drive. Is this by default? It is annoying to say the least.
In the new version of google apps script all you need to acheive your task is the following:
file.makeCopy("new name", folder);
EDIT : I had a look at your original question (before edit) and from there I understood the confusion you made about how files and folders work in Google Drive.
When you make a copy of a file you get a new file in the root folder, when you add this new file to another folder you have to consider that as sticking a label on this file without creating any new copy of it.
You can actually see the file in both the root and the other folder but it is the very same file with 2 different labels! (and you did notice that since you tried to delete it and saw it was deleted in both places)
The only thing you have to do to get the new file in its folder and not shown in the root it to remove the "root label".
That's how Google drive works. And when you think about it and compare to a local hard disk storage it gets logical : when you move a file from one folder to another on the same logical drive you don't move data (ie the bytes on the disk) but simply tell the disk operating system that this file is somewhere else on the map.
So consider Google Drive as a (very) large disk unit with billions on files that you can't move but on which you can stick as many labels as you want ;-) (the most important label being your Google account ID !)
And to play with these labels in the case you describe just try this simple function :
function copyAndMove(file,folder){
var newfile=file.makeCopy('copy of '+file.getName());// here you can define the copy name the way you want...
newfile.addToFolder(folder);// add the copy to the folder
newfile.removeFromFolder(DocsList.getRootFolder());// and remove it from your root folder
}
to test it just use the 2 required parameters : the file object anf the folder object that you can get from many different methods, see the DocsList documentation for details on how to get it if you need to but I guess you already know that ;-)
Yeah, it is a bit odd that Google does not provide a move method. But, considering how drive works and that a file can belong to multiple folders, it makes some sense that you have write your own moves. Here is a simple move function I wrote:
// Moves a file to a new folder by removing all current
// parent folders and then adding the new one.
function moveFileTo(fileObj, folderObj) {
// Attempt the move only if folderObj has a value.
// Otherwise, the file will be left without a folder.
// (Lost files can be found in the "All items" area.)
if (folderObj) {
var folders = fileObj.getParents();
for (var i = 0; i < folders.length; i++) {
fileObj.removeFromFolder(folders[i]);
}
fileObj.addToFolder(folderObj);
return true;
}
return false;
}
Basically, use it like this:
var file = DocsList.getFileById(fileID);
var folder = DocsList.getFolder('Some folder name');
// Make a backup copy.
var file2 = file.makeCopy('BACKUP ' + Utilities.formatDate(new Date(), Session.getTimeZone(), 'yyyy-MM-dd') + '.' + file.getName());
// Move the backup file.
if (moveFileTo(file2, folder)) {
...
I should note that this simple function is exactly that... simple. It assumes you are okay with whacking all parent folders -- owned or shared. This can have unexpected consequences with shared files in various shared folders of various users if you are not careful. It does not appear, however, to remove parent folders that are not shared with the user of this script -- which is good. Anyhow, one obvious alternative to control things better would be to specify the "from" folder as well as the "to" folder.