define(["jquery", "core", "async-runner"], function($, core, asyncRunner) { var client = undefined; var authenticated = false; var dropboxHelper = {}; // Try to connect dropbox by downloading client.js function connect(task) { task.onRun(function() { if(core.isOffline === true) { client = undefined; task.error(new Error("Operation not available in offline mode.")); return; } if (client !== undefined) { task.chain(); return; } $.ajax({ url : "lib/dropbox.min.js", dataType : "script", timeout : AJAX_TIMEOUT }).done(function() { client = new Dropbox.Client({ key: DROPBOX_APP_KEY, secret: DROPBOX_APP_SECRET }); client.authDriver(new Dropbox.Drivers.Popup({ receiverUrl: BASE_URL + "dropbox-oauth-receiver.html", rememberUser: true })); task.chain(); }).fail(function(jqXHR) { var error = { status: jqXHR.status, responseText: jqXHR.statusText }; handleError(error, task); }); }); } // Try to authenticate with Oauth function authenticate(task) { task.onRun(function() { if (authenticated === true) { task.chain(); return; } var immediate = true; function localAuthenticate() { if (immediate === false) { core.showMessage("Please make sure the Dropbox authorization popup is not blocked by your browser."); // If not immediate we add time for user to enter his credentials task.timeout = ASYNC_TASK_LONG_TIMEOUT; } client.reset(); client.authenticate({interactive: !immediate}, function(error, client) { // Success if (client.authState === Dropbox.Client.DONE) { authenticated = true; task.chain(); return; } // If immediate did not work retry without immediate flag if (immediate === true) { immediate = false; task.chain(localAuthenticate); return; } // Error task.error(new Error("Access to Dropbox account is not authorized.")); }); } task.chain(localAuthenticate); }); } dropboxHelper.upload = function(path, content, callback) { callback = callback || core.doNothing; var result = undefined; var task = asyncRunner.createTask(); connect(task); authenticate(task); task.onRun(function() { client.writeFile(path, content, function(error, stat) { if (!error) { result = stat; task.chain(); return; } // Handle error if(error.status === Dropbox.ApiError.INVALID_PARAM) { error = 'Could not upload document into path "' + path + '".'; } handleError(error, task); }); }); task.onSuccess(function() { callback(undefined, result); }); task.onError(function(error) { callback(error); }); asyncRunner.addTask(task); }; dropboxHelper.checkChanges = function(lastChangeId, callback) { callback = callback || core.doNothing; var changes = []; var newChangeId = lastChangeId || 0; var task = asyncRunner.createTask(); connect(task); authenticate(task); task.onRun(function() { function retrievePageOfChanges() { client.pullChanges(newChangeId, function(error, pullChanges) { if (error) { handleError(error, task); return; } // Retrieve success newChangeId = pullChanges.cursor(); if(pullChanges.changes !== undefined) { changes = changes.concat(pullChanges.changes); } if (pullChanges.shouldPullAgain) { task.chain(retrievePageOfChanges); } else { task.chain(); } }); } task.chain(retrievePageOfChanges); }); task.onSuccess(function() { callback(undefined, changes, newChangeId); }); task.onError(function(error) { callback(error); }); asyncRunner.addTask(task); }; dropboxHelper.downloadMetadata = function(paths, callback) { callback = callback || core.doNothing; var result = []; var task = asyncRunner.createTask(); connect(task); authenticate(task); task.onRun(function() { function recursiveDownloadMetadata() { if(paths.length === 0) { task.chain(); return; } var path = paths[0]; client.stat(path, function(error, stat) { if(stat) { result.push(stat); paths.shift(); task.chain(recursiveDownloadMetadata); return; } handleError(error, task); }); } task.chain(recursiveDownloadMetadata); }); task.onSuccess(function() { callback(undefined, result); }); task.onError(function(error) { callback(error); }); asyncRunner.addTask(task); }; dropboxHelper.downloadContent = function(objects, callback) { callback = callback || core.doNothing; var result = []; var task = asyncRunner.createTask(); connect(task); authenticate(task); task.onRun(function() { function recursiveDownloadContent() { if(objects.length === 0) { task.chain(); return; } var object = objects[0]; result.push(object); var file = undefined; // object may be a file if(object.isFile === true) { file = object; } // object may be a change else if(object.wasRemoved !== undefined) { file = object.stat; } if(!file) { objects.shift(); task.chain(recursiveDownloadContent); return; } client.readFile(file.path, function(error, data) { if(data) { file.content = data; objects.shift(); task.chain(recursiveDownloadContent); return; } handleError(error, task); }); } task.chain(recursiveDownloadContent); }); task.onSuccess(function() { callback(undefined, result); }); task.onError(function(error) { callback(error); }); asyncRunner.addTask(task); }; function handleError(error, task) { var errorMsg = true; if (error) { console.error(error); // Try to analyze the error if (typeof error === "string") { errorMsg = error; } else { errorMsg = "Dropbox error (" + error.status + ": " + error.responseText + ")."; if (error.status === Dropbox.ApiError.INVALID_TOKEN || error.status === Dropbox.ApiError.OAUTH_ERROR) { authenticated = false; errorMsg = "Access to Dropbox account is not authorized."; task.retry(new Error(errorMsg), 1); return; } else if(error.status === Dropbox.ApiError.INVALID_PARAM && error.responseText .indexOf("oauth_nonce") !== -1) { // A bug I guess... _.each(_.keys(localStorage), function(key) { // We have to remove the Oauth cache from the localStorage if(key.indexOf("dropbox-auth") === 0) { localStorage.removeItem(key); } }); authenticated = false; task.retry(new Error(errorMsg), 1); return; } else if (error.status === Dropbox.ApiError.NETWORK_ERROR || error.status < 0) { client = undefined; authenticated = false; core.setOffline(); errorMsg = "|stopPublish"; } } } task.error(new Error(errorMsg)); } var pickerLoaded = false; function loadPicker(task) { task.onRun(function() { if (pickerLoaded === true) { task.chain(); return; } $.ajax({ url : "https://www.dropbox.com/static/api/1/dropbox.js", dataType : "script", timeout : AJAX_TIMEOUT }).done(function() { pickerLoaded = true; task.chain(); }).fail(function(jqXHR) { var error = { status: jqXHR.status, responseText: jqXHR.statusText }; handleError(error, task); }); }); } dropboxHelper.picker = function(callback) { callback = callback || core.doNothing; var paths = []; var task = asyncRunner.createTask(); // Add some time for user to choose his files task.timeout = ASYNC_TASK_LONG_TIMEOUT; connect(task); loadPicker(task); task.onRun(function() { var options = {}; options.multiselect = true; options.linkType = "direct"; options.success = function(files) { for(var i=0; i