Trigger downloading and opening of evicted iCloud Drive files
This commit is contained in:
		
					parent
					
						
							
								e48a1a2abb
							
						
					
				
			
			
				commit
				
					
						ec76575645
					
				
			
		
					 3 changed files with 110 additions and 66 deletions
				
			
		| 
						 | 
				
			
			@ -1334,6 +1334,11 @@ Zotero.File = new function(){
 | 
			
		|||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	this.getEvictedICloudPath = function (path) {
 | 
			
		||||
		return OS.Path.join(OS.Path.dirname(path), '.' + OS.Path.basename(path) + '.icloud');
 | 
			
		||||
	};
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	this.isDropboxDirectory = function(path) {
 | 
			
		||||
		return path.toLowerCase().indexOf('dropbox') != -1;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1010,6 +1010,8 @@ Services.scriptloader.loadSubScript("resource://zotero/polyfill.js");
 | 
			
		|||
	 * Launch a file with the given application
 | 
			
		||||
	 */
 | 
			
		||||
	this.launchFileWithApplication = function (filePath, applicationPath) {
 | 
			
		||||
		Zotero.debug(`Launching ${filePath} with ${applicationPath}`);
 | 
			
		||||
		
 | 
			
		||||
		var exec = Zotero.File.pathToFile(applicationPath);
 | 
			
		||||
		if (!exec.exists()) {
 | 
			
		||||
			throw new Error("'" + applicationPath + "' does not exist");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4055,7 +4055,7 @@ var ZoteroPane = new function()
 | 
			
		|||
	});
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	this.viewAttachment = Zotero.serial(Zotero.Promise.coroutine(function* (itemIDs, event, noLocateOnMissing, forceExternalViewer) {
 | 
			
		||||
	this.viewAttachment = Zotero.serial(async function (itemIDs, event, noLocateOnMissing, forceExternalViewer) {
 | 
			
		||||
		// If view isn't editable, don't show Locate button, since the updated
 | 
			
		||||
		// path couldn't be sent back up
 | 
			
		||||
		if (!this.collectionsView.editable) {
 | 
			
		||||
| 
						 | 
				
			
			@ -4071,9 +4071,26 @@ var ZoteroPane = new function()
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		var launchFile = async function (path, contentType) {
 | 
			
		||||
			// Custom PDF handler
 | 
			
		||||
			if (contentType === 'application/pdf') {
 | 
			
		||||
				let pdfHandler  = Zotero.Prefs.get("fileHandler.pdf");
 | 
			
		||||
				if (pdfHandler) {
 | 
			
		||||
					if (await OS.File.exists(pdfHandler)) {
 | 
			
		||||
						Zotero.launchFileWithApplication(path, pdfHandler);
 | 
			
		||||
						return;
 | 
			
		||||
					}
 | 
			
		||||
					else {
 | 
			
		||||
						Zotero.logError(`${pdfHandler} not found -- launching file normally`);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			Zotero.launchFile(path);
 | 
			
		||||
		};
 | 
			
		||||
		
 | 
			
		||||
		for (let i = 0; i < itemIDs.length; i++) {
 | 
			
		||||
			let itemID = itemIDs[i];
 | 
			
		||||
			var item = yield Zotero.Items.getAsync(itemID);
 | 
			
		||||
			let item = await Zotero.Items.getAsync(itemID);
 | 
			
		||||
			if (!item.isAttachment()) {
 | 
			
		||||
				throw new Error("Item " + itemID + " is not an attachment");
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -4083,58 +4100,79 @@ var ZoteroPane = new function()
 | 
			
		|||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			var path = yield item.getFilePathAsync();
 | 
			
		||||
			if (path) {
 | 
			
		||||
				let file = Zotero.File.pathToFile(path);
 | 
			
		||||
			let isLinkedFile = !item.isImportedAttachment();
 | 
			
		||||
			let path = item.getFilePath();
 | 
			
		||||
			let fileExists = await OS.File.exists(path);
 | 
			
		||||
			let evictedICloudPath;
 | 
			
		||||
			
 | 
			
		||||
				Zotero.debug("Opening " + path);
 | 
			
		||||
				
 | 
			
		||||
				if(forceExternalViewer !== undefined) {
 | 
			
		||||
					var externalViewer = forceExternalViewer;
 | 
			
		||||
				} else {
 | 
			
		||||
					var mimeType = yield Zotero.MIME.getMIMETypeFromFile(file);
 | 
			
		||||
					
 | 
			
		||||
					//var mimeType = attachment.attachmentMIMEType;
 | 
			
		||||
					// TODO: update DB with new info if changed?
 | 
			
		||||
					
 | 
			
		||||
					var ext = Zotero.File.getExtension(file);
 | 
			
		||||
					var externalViewer = !Zotero.MIME.hasInternalHandler(mimeType, ext)
 | 
			
		||||
						|| Zotero.Prefs.get('launchNonNativeFiles');
 | 
			
		||||
			// If the file is an evicted iCloud Drive file, launch that to trigger a download.
 | 
			
		||||
			// As of 10.13.6, launching an .icloud file triggers the download and opens the
 | 
			
		||||
			// associated program (e.g., Preview) but won't actually open the file, so we wait a bit
 | 
			
		||||
			// for the original file to exist and then continue with regular file opening below.
 | 
			
		||||
			//
 | 
			
		||||
			// To trigger eviction for testing, use Cirrus from https://eclecticlight.co/downloads/
 | 
			
		||||
			if (!fileExists && Zotero.isMac && isLinkedFile) {
 | 
			
		||||
				// Get the path to the .icloud file
 | 
			
		||||
				let iCloudPath = Zotero.File.getEvictedICloudPath(item.getFilePath());
 | 
			
		||||
				if (await OS.File.exists(iCloudPath)) {
 | 
			
		||||
					Zotero.debug("Triggering download of iCloud file");
 | 
			
		||||
					await launchFile(iCloudPath, item.attachmentContentType);
 | 
			
		||||
					let time = new Date();
 | 
			
		||||
					let maxTime = 5000;
 | 
			
		||||
					let revealed = false;
 | 
			
		||||
					while (true) {
 | 
			
		||||
						// If too much time has elapsed, just reveal the file in Finder instead
 | 
			
		||||
						if (new Date() - time > maxTime) {
 | 
			
		||||
							Zotero.debug(`File not available after ${maxTime} -- revealing instead`);
 | 
			
		||||
							try {
 | 
			
		||||
								Zotero.File.reveal(iCloudPath);
 | 
			
		||||
								revealed = true;
 | 
			
		||||
							}
 | 
			
		||||
							catch (e) {
 | 
			
		||||
								Zotero.logError(e);
 | 
			
		||||
								// In case the main file became available
 | 
			
		||||
								try {
 | 
			
		||||
									Zotero.File.reveal(path);
 | 
			
		||||
									revealed = true;
 | 
			
		||||
								}
 | 
			
		||||
								catch (e) {
 | 
			
		||||
									Zotero.logError(e);
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
							break;
 | 
			
		||||
						}
 | 
			
		||||
						
 | 
			
		||||
				if (!externalViewer) {
 | 
			
		||||
					let url = Services.io.newFileURI(file).spec;
 | 
			
		||||
					this.loadURI(url, event);
 | 
			
		||||
						// Wait a bit for the download and check again
 | 
			
		||||
						await Zotero.Promise.delay(250);
 | 
			
		||||
						Zotero.debug("Checking for downloaded file");
 | 
			
		||||
						if (await OS.File.exists(path)) {
 | 
			
		||||
							Zotero.debug("File is ready");
 | 
			
		||||
							fileExists = true;
 | 
			
		||||
							break;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				else {
 | 
			
		||||
					Zotero.Notifier.trigger('open', 'file', itemID);
 | 
			
		||||
					
 | 
			
		||||
					// Custom PDF handler
 | 
			
		||||
					if (item.attachmentContentType === 'application/pdf') {
 | 
			
		||||
						let pdfHandler  = Zotero.Prefs.get("fileHandler.pdf");
 | 
			
		||||
						if (pdfHandler) {
 | 
			
		||||
							if (yield OS.File.exists(pdfHandler)) {
 | 
			
		||||
								Zotero.launchFileWithApplication(file.path, pdfHandler);
 | 
			
		||||
					if (revealed) {
 | 
			
		||||
						continue;
 | 
			
		||||
					}
 | 
			
		||||
							else {
 | 
			
		||||
								Zotero.logError(`${pdfHandler} not found -- launching file normally`);
 | 
			
		||||
							}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
					Zotero.launchFile(file);
 | 
			
		||||
			if (fileExists) {
 | 
			
		||||
				Zotero.debug("Opening " + path);
 | 
			
		||||
				Zotero.Notifier.trigger('open', 'file', item.id);
 | 
			
		||||
				
 | 
			
		||||
				launchFile(path, item.attachmentContentType);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				if (!item.isImportedAttachment()
 | 
			
		||||
						|| !Zotero.Sync.Storage.Local.getEnabledForLibrary(item.libraryID)) {
 | 
			
		||||
			
 | 
			
		||||
			if (isLinkedFile || !Zotero.Sync.Storage.Local.getEnabledForLibrary(item.libraryID)) {
 | 
			
		||||
				this.showAttachmentNotFoundDialog(itemID, noLocateOnMissing);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			try {
 | 
			
		||||
					yield Zotero.Sync.Runner.downloadFile(item);
 | 
			
		||||
				await Zotero.Sync.Runner.downloadFile(item);
 | 
			
		||||
			}
 | 
			
		||||
			catch (e) {
 | 
			
		||||
				// TODO: show error somewhere else
 | 
			
		||||
| 
						 | 
				
			
			@ -4143,7 +4181,7 @@ var ZoteroPane = new function()
 | 
			
		|||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
				if (!(yield item.getFilePathAsync())) {
 | 
			
		||||
			if (!await item.getFilePathAsync()) {
 | 
			
		||||
				ZoteroPane_Local.showAttachmentNotFoundDialog(item.id, noLocateOnMissing, true);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -4155,8 +4193,7 @@ var ZoteroPane = new function()
 | 
			
		|||
			// Retry after download
 | 
			
		||||
			i--;
 | 
			
		||||
		}
 | 
			
		||||
		}
 | 
			
		||||
	}));
 | 
			
		||||
	});
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue