Restore trashed google drive files to a target folder - scripting

I need to move all files presently in my google drive's trash to a certain folder or, if that can't be done, restore them to their original location. Some mess happened and I have valuable files in the trash, some 6 gig of them, so I'd prefer to move them to a separate directory, back it up or sync locally and see those files later.
This is the script I have so far:
function moveFilesFromTrash() {
var pageSize = 200;
var files = null;
var token = null;
var cestisti = DocsList.getFolder('cestisti');
do {
var result = DocsList.getAllFilesForPaging(pageSize, token);
files = result.getFiles();
token = result.getToken();
for (var i = 0; i < files.length; i++) {
if (files[i].isTrashed == true) {
Logger.log(files[i].getName());
// files[i].setTrashed(false)
files[i].addToFolder(cestisti);
}
} while (files.length == pageSize);
}
The matter is it just does not work.
The part of the code to page through files items works, I got it from other working scripts of mine. I just don't know if it parses the trash folder or label to. I do not know if setTrashed() or addtofolder() works - I have no idea. Now the former is commented out because I would prefer to copy items instead of restoring them, but if that's not possible I can restore.

This answer has been updated due to the deprecation of DocsList, and now uses DriveApp methods throughout.
You must un-trash a file before you can move it, but doing so will accomplish what you're after. Here's the algorithm:
Get a list of files (or file ids) for all trashed files
For each file,
Undelete / un-trash the file
Move file to target folder (detail: remove file from original folders, add to target recovery folder)
This answer provides snippets of script to accomplish this; The full script is available as a Google Sheets Add-on in a gist, complete with menu and UI elements.
Recover all trashed files
This function uses DriveApp.search() to get the list of all trashed files, and then recovers them into a specified folder, rescueFldr, which will be created if not already there.
/**
* Untrash then move all trashed files to rescue folder.
* From: http://stackoverflow.com/a/14541247/1677912
*
* #returns {Number} Count of Rescued files.
*/
function rescueAllFiles() {
// Find or create target folder
var rescueFldrIterator = DriveApp.getFoldersByName(rescueFldrName);
var rescueFldr = rescueFldrIterator.hasNext() ? rescueFldrIterator.next() : DriveApp.createFolder(rescueFldrName);
// Get file iterator with all trashed files
var trashed = DriveApp.searchFiles('trashed=true');
var count = 0;
while (trashed.hasNext()) {
var file = trashed.next();
// Untrash the file
if (rescueFile( file, rescueFldr )) count++;
}
return(count);
};
Recover specific Trashed Files
If you want to have more control over the files that will be recovered, and you can't simply modify the search parameters used above, an alternative might be to list the file IDs in a spreadsheet, and have a script use that as its input.
Fill spreadsheet with trashed files
/**
* Get array of files in user's Google Drive trash, and write
* into currently active sheet.
* From: http://stackoverflow.com/a/14541247/1677912
*
* #returns {Object[]} Array of DriveApp file objects
*/
function getTrashedFiles() {
var trashedSearch = DriveApp.searchFiles('trashed=true');
var files = [];
files.push(["ID","File Name","Type","URL"]);
while (trashedSearch.hasNext()) {
var file = trashedSearch.next();
files.push([file.getId(),file.getName(),docTypeToText_(file.getMimeType()),file.getUrl()]);
}
var sheet = SpreadsheetApp.getActiveSheet();
sheet.clear();
sheet.getRange(1, 1, files.length, files[0].length)
.setValues(files);
return files;
}
Manual file ID collection
(Alternative to automatic retrieval, for reference only.) Since you're just doing this once, though, let's cheat. At the bottom of the Files-list page, use the API Explorer to retrieve the IDs of all your deleted files!
The output is shown just as you'd receive it if you were calling the API from a script.
Cut & paste the output into a text file, and strip it down to just the file ids. (I did that with gvim, it takes just a few commands.) Save the result as a csv file, and pull it into Drive as a spreadsheet. Here's what that will look like. (Do include a header line; the script assumes there is one.)
Process Trashed Files
Now that the list of trashed files is in a spreadsheet, scripting the recovery & move is easy.
/**
* Untrash then move files listed in spreadsheet to rescue folder.
* From: http://stackoverflow.com/a/14541247/1677912
*
* #returns {Number} Count of Rescued files.
*/
function rescueListedFiles() {
var fileList = SpreadsheetApp.getActiveSheet()
.getDataRange().getValues()
.splice(1); // Skip header line
// Find or create target folder
var rescueFldrIterator = DriveApp.getFoldersByName(rescueFldrName);
var rescueFldr = rescueFldrIterator.hasNext() ? rescueFldrIterator.next() : DriveApp.createFolder(rescueFldrName);
var count = 0;
for (var i=0; i<fileList.length; i++) {
var fileId = fileList[i][0];
var file = DriveApp.getFileById(fileId);
// Untrash the file
if (rescueFile( file, rescueFldr )) count++;
}
return( count );
};

try
if (files[i].isTrashed() == true)
I suggest that you put in some diagnostics (Logger.log) to narrow down problems

Remember, files are not in folders. Files have labels that are called folders and there can be more than one folder(label). Even 'trashed' is a label. Setting to trash just creates a trashed label that overrides the other labels(folders). The code below does not move anything. It just resets the labels(folders)
Edit: Trashed is not a label but a condition: everything else holds
for (var i = 0; i < files.length; i++) {
if (files[i].isTrashed()) { // don't forget ()
Logger.log(files[i].getName());
var folders = files[i].getParents();
// remove existing folders(labels)
for(var f = 0; f < folders.length; f++ ) {
files[i].removeFromFolder( folders[f] );
};
// remove trashed label
files[i].setTrashed(false);
// add cestisti label(add to cestisti folder)
files[i].addToFolder(cestisti);
}
}

Related

Exception when listing created sub-directory in documents directory

I am using the official API to list a directory I had created for some purpose and have pushed few files into it. Now I want to list them out.
Here's how I am creating first
final Directory extDir = await getApplicationDocumentsDirectory();
// final String dirPath = '${extDir.path}/Movies/flutter_test';
final String dirPath = '${extDir.path}/mydir';
await new Directory(dirPath).create(recursive: true);
Here's what I am doing to read in a different page.
final Directory extDir = await getApplicationDocumentsDirectory();
// final String dirPath = '${extDir.path}/Pictures/flutter_test';
final String dirPath = '${extDir.path}/mydir';
final String thumbDirPath = '$dirPath/thumbs';
final Directory imgDir = Directory.fromUri(Uri.file(dirPath));
dirExists = imgDir.existsSync();
fileCount = 0;
if(dirExists) {
print("my dir exists");
// thumbDir.list(recursive: false, followLinks: false)
fileCount = await imgDir.list(recursive: false).length;
print('mydir images count $fileCount');
if(fileCount > 1) { // we think one is always the directory itself?
try {
imgDir.list(recursive: false, followLinks: false)
.listen((FileSystemEntity entity) {
The code breaks here giving the following exception message
type '() => Null' is not a subtype of type '(Object) => FutureOr<dynamic>'
I have to also tell you this doesn't happen when I am listing the external storage directory. Am I doing something wrong while creating my directory?
EDIT
I have now upgraded flutter to 0.7.3 on my Mac, and have a new problem altogether. The application won't run at all.
Okay, I found a solution myself. Apparently tinkering with the large provider API has given me different way to list the items, which actually told me I was trying to run listeners twice.
fileCount = await imgDir.list(recursive: false).length;
and
imgDir.list(recursive: false, followLinks: false)
.listen((FileSystemEntity entity) {
from original code cannot be written one after the other, as the first one finishes the listener. This I found out by altering my own code and listing directories this way instead.
Stream<FileSystemEntity> files = imgDir.list(recursive: false, followLinks: false);
will add as many events as many files according to API.. and then create a regular generic list
List<FileSystemEntity> entities = await files.toList();
and then you perform usual iteration using iterator or for() loop like
for(FileSystemEntity entity in entities) {
... // every file now is available

where are app scripts stored on Google Drive?

I found a useful script long ago and installed it. Now I want to delete it so it's no longer running, but I have no idea where it is stored so I can delete it. And I don't know it's exact filename to search for it. How do I find the script?? it watches a shared folder on Google Drive and emails me when the contents of the folder are changed.
I need to find this script and delete it.... suggestions????
How about something like this? It uses Mime types to find the scripts.
function findGoogleScripts() {
var ssNew = SpreadsheetApp.create("GoogleScriptsOnMyDrive");
SpreadsheetApp.setActiveSpreadsheet(ssNew);
var sheet = ssNew.getSheets()[0];
var files = DriveApp.getFiles();
sheet.appendRow(["File Name", "Mime Type"]);
while (files.hasNext()) {
var mType, theFile;
var tempArray = [];
theFile = files.next();
mType = theFile.getMimeType();
if (mType.equals("application/vnd.google-apps.script")) {
tempArray.push(theFile.getName());
tempArray.push(mType);
sheet.appendRow(tempArray);
}
}
}

Google Drive Change File Permissions within a folder

I wondered if someone could help. We use Google Apps for Education. Within the system we have a shared folder where teachers can place files for students to access. E.g. The entire team of 8 science teachers all add files to "Science Shared."
Science Shared folder is on a separate google account "science-shared#domain.com"
Over time, files will take up quota of individual users and if they leave and their account is deleted, all these files will go. We obviously do not want to transfer their entire data to science-shared using the transfer facility.
Ideally, I am looking for some sort of script which can traverse through the shared folder and change the permissions of each file and folder so that science-shared is the owner and the individual teacher has edit access.
Is this possible and if so, can anyone provide some help on how/where to start...clueless at the moment.
Thanks in advance.
Edit:
Refer to issue 2756, noting that an administrator cannot change the ownership of files via Google Apps Script:
...It's related to the fact that you can't change the own for files
you don't own. This error occurs for any illegal ACL change, such as
trying to call addEditor() when you are a viewer.
To change the ownership of files not owned by the administrator, they must use the Google Drive SDK, authenticated via OAuth.
This is certainly possible, although only for files owned by the user running the script.
Here's a script that will find all the files owned by the current user in the Science Shared folder, and transfer ownership to user science-shared. It's designed as a spreadsheet-contained script, which creates a custom menu and uses the spreadsheet Browser UI. Put the spreadsheet into your shared directory, and any teacher should be able to use it to transfer their own files, wholesale.
An admin should be able to use the script to change ANY teacher's files - just collect the id of the origOwner, and pass it to chownFilesInFolder.
Caveat: It only deals with files, not sub-directories - you could extend it if needed.
/**
* Find all files owned by current user in given folder,
* and change their ownership to newOwner.
* Note: sub-folders are untouched
*/
function chownFilesInFolder(folderId,origOwner,newOwner) {
var folder = DriveApp.getFolderById(folderId);
var contents = folder.getFiles();
while(contents.hasNext()) {
var file = contents.next();
var name = file.getName();
var owner = file.getOwner().getEmail();
// Note: domain security policies may block access to user's emails
// If so, this will return a blank string - good enough for our purposes.
if (owner == origOwner) {
// Found a file owned by current user - change ownership
Logger.log(name);
//file.setOwner(newOwner);
}
}
};
/**
* Spreadsheet browser-based UI driver for chownFilesInFolder()
*/
function changeOwnership() {
var resp = Browser.msgBox("Transfer 'Science Shared' files",
"Are you sure you want to transfer ownership of all your shared files?",
Browser.Buttons.YES_NO);
if (resp == "yes") {
var folderName = "Science Shared";
// Assume there is just one "Science Shared" folder.
var folder = DriveApp.getFoldersByName(folderName);
if (!folder.hasNext()) {
throw new Error("Folder not found ("+folderName+")");
}
else {
var folderId = folder.next().getId();
var origOwner = Session.getActiveUser().getEmail(); // Operate on own files
var newOwner = "science_shared#example.com";
chownFilesInFolder(folderId,origOwner,newOwner);
Browser.msgBox("Operation completed", Browser.Buttons.OK);
}
}
else Browser.msgBox("Operation aborted", Browser.Buttons.OK);
}
/**
* Adds a custom menu to the active spreadsheet, containing a single menu item
*/
function onOpen() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Transfer 'Science Shared' files",
functionName : "changeOwnership"
}];
spreadsheet.addMenu("Science-Shared", entries);
};
I hope that helps get you started.

Google Drive: Move file to folder

I want to understand the moveFileToFolder script that is listed as an example when you select google drive scripts http://www.google.com/script/start/ (click "Start Scripting" then select "Drive" option in "Create script for" menu)
The code is below. My question is how would I alter this code so that it could be used to move the file "Austin" into the folder "Texas"? This is presuming that the file "Austin" is a google doc which is currently sitting in my main google drive file list and the folder "Texas" is also currently sitting in my main google drive list.
There are a few other posts regarding moving files in drive and I understand the main concepts but I can't seems to successfully procure the file or folder ID's. Any help greatly appreciated?
/**
* This script moves a specific file from one parent folder to another.
* For more information on interacting with files, see
* https://developers.google.com/apps-script/class_file
*/
function moveFileToFolder(fileId, targetFolderId) {
var targetFolder = DocsList.getFolderById(targetFolderId);
var file = DocsList.getFileById(fileId);
file.addToFolder(targetFolder);
};
now I had the same task and used this code:
var file = DriveApp.getFileById(fileId)
var folder = DriveApp.getFolderById(folderId);
folder.addFile(file);
Check out function getFolderByName in https://gist.github.com/suntong001/7955694 to see how to get folder ID by its name. For files the principle is the same.
function addDocToPublic() {
// Create random filename
var fn = "doc" + Math.floor(10000 * Math.random()) + ".doc";
// Create file with very MIME TYPE and return file ID
var fh = DriveApp.createFile(fn, "Das ist das " + fn + " Testfile", MimeType.MICROSOFT_WORD);
// Get first/next Iterator ID ( = folder ID) for Folder (with name PUBLIC)
var fid = DriveApp.getFoldersByName("PUBLIC").next();
// Copy File to Folder (using file and folder IDs
fh.makeCopy(fn,fid);
// Remove Source File
fh.setTrashed(true);
}
... maybe that way ...

Check if a file exists in the project in WinRT

I have a WinRT Metro project which displays images based on a selected item. However, some of the images selected will not exist. What I want to be able to do is trap the case where they don't exist and display an alternative.
Here is my code so far:
internal string GetMyImage(string imageDescription)
{
string myImage = string.Format("Assets/MyImages/{0}.jpg", imageDescription.Replace(" ", ""));
// Need to check here if the above asset actually exists
return myImage;
}
Example calls:
GetMyImage("First Picture");
GetMyImage("Second Picture");
So Assets/MyImages/SecondPicture.jpg exists, but Assets/MyImages/FirstPicture.jpg does not.
At first I thought of using the WinRT equivalent of File.Exists(), but there doesn't appear to be one. Without having to go to the extent of trying to open the file and catching an error, can I simply check if either the file exists, or the file exists in the project?
You could use GetFilesAsync from here to enumerate the existing files. This seems to make sense considering you have multiple files which might not exist.
Gets a list of all files in the current folder and its sub-folders. Files are filtered and sorted based on the specified CommonFileQuery.
var folder = await StorageFolder.GetFolderFromPathAsync("Assets/MyImages/");
var files = await folder.GetFilesAsync(CommonFileQuery.OrderByName);
var file = files.FirstOrDefault(x => x.Name == "fileName");
if (file != null)
{
//do stuff
}
Edit:
As #Filip Skakun pointed out, the resource manager has a resource mapping on which you can call ContainsKey which has the benefit of checking for qualified resources as well (i.e. localized, scaled etc).
Edit 2:
Windows 8.1 introduced a new method for getting files and folders:
var result = await ApplicationData.Current.LocalFolder.TryGetItemAsync("fileName") as IStorageFile;
if (result != null)
//file exists
else
//file doesn't exist
There's two ways you can handle it.
1) Catch the FileNotFoundException when trying to get the file:
Windows.Storage.StorageFolder installedLocation =
Windows.ApplicationModel.Package.Current.InstalledLocation;
try
{
// Don't forget to decorate your method or event with async when using await
var file = await installedLocation.GetFileAsync(fileName);
// Exception wasn't raised, therefore the file exists
System.Diagnostics.Debug.WriteLine("We have the file!");
}
catch (System.IO.FileNotFoundException fileNotFoundEx)
{
System.Diagnostics.Debug.WriteLine("File doesn't exist. Use default.");
}
catch (Exception ex)
{
// Handle unknown error
}
2) as mydogisbox recommends, using LINQ. Although the method I tested is slightly different:
Windows.Storage.StorageFolder installedLocation =
Windows.ApplicationModel.Package.Current.InstalledLocation;
var files = await installedLocation.GetFilesAsync(CommonFileQuery.OrderByName);
var file = files.FirstOrDefault(x => x.Name == fileName);
if (file != null)
{
System.Diagnostics.Debug.WriteLine("We have the file!");
}
else
{
System.Diagnostics.Debug.WriteLine("No File. Use default.");
}
BitmapImage has an ImageFailed event that fires if the image can't be loaded. This would let you try to load the original image, and then react if it's not there.
Of course, this requires that you instantiate the BitmapImage yourself, rather than just build the Uri.
Sample checking for resource availability for c++ /cx (tested with Windows Phone 8.1):
std::wstring resPath = L"Img/my.bmp";
std::wstring resKey = L"Files/" + resPath;
bool exists = Windows::ApplicationModel::Resources::Core::ResourceManager::Current->MainResourceMap->HasKey(ref new Platform::String(resKey.c_str()));