123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856 |
- /**
- * Copyright (c) 2006-2017, JGraph Ltd
- * Copyright (c) 2006-2017, Gaudenz Alder
- */
- DropboxClient = function(editorUi)
- {
- DrawioClient.call(this, editorUi, 'dbauth');
-
- this.client = new Dropbox({clientId: App.DROPBOX_APPKEY});
- this.client.setAccessToken(this.token);
- };
- // Extends DrawioClient
- mxUtils.extend(DropboxClient, DrawioClient);
- /**
- * FIXME: How to find name of app folder for current user. The Apps part of the
- * name is internationalized so this hardcoded check does not work everywhere.
- */
- DropboxClient.prototype.appPath = '/drawio/';
- /**
- * Executes the first step for connecting to Google Drive.
- */
- DropboxClient.prototype.extension = '.html';
- /**
- * Executes the first step for connecting to Google Drive.
- */
- DropboxClient.prototype.writingFile = false;
- /**
- * Executes the first step for connecting to Google Drive.
- */
- DropboxClient.prototype.maxRetries = 4;
- /**
- * Authorizes the client, gets the userId and calls <open>.
- */
- DropboxClient.prototype.logout = function()
- {
- this.clearPersistentToken();
- this.setUser(null);
- this.token = null;
-
- this.client.authTokenRevoke().then(mxUtils.bind(this, function()
- {
- this.client.setAccessToken(null);
- }));
- };
- /**
- * Checks if the client is authorized and calls the next step.
- */
- DropboxClient.prototype.updateUser = function(success, error, failOnAuth)
- {
- var acceptResponse = true;
-
- var timeoutThread = window.setTimeout(mxUtils.bind(this, function()
- {
- acceptResponse = false;
- error({code: App.ERROR_TIMEOUT});
- }), this.ui.timeout);
-
- var promise = this.client.usersGetCurrentAccount();
- promise.then(mxUtils.bind(this, function(response)
- {
- window.clearTimeout(timeoutThread);
-
- if (acceptResponse)
- {
- this.setUser(new DrawioUser(response.account_id,
- response.email, response.name.display_name));
- success();
- }
- }));
- // Workaround for IE8/9 support with catch function
- promise['catch'](mxUtils.bind(this, function(err)
- {
- window.clearTimeout(timeoutThread);
-
- if (acceptResponse)
- {
- if (err != null && err.status === 401 && !failOnAuth)
- {
- this.setUser(null);
- this.client.setAccessToken(null);
-
- this.authenticate(mxUtils.bind(this, function()
- {
- this.updateUser(success, error, true);
- }), error);
- }
- else
- {
- error({message: mxResources.get('accessDenied')});
- }
- }
- }));
- };
- /**
- * Authorizes the client, gets the userId and calls <open>.
- */
- DropboxClient.prototype.authenticate = function(success, error)
- {
- if (window.onDropboxCallback == null)
- {
- var auth = mxUtils.bind(this, function()
- {
- var acceptAuthResponse = true;
-
- this.ui.showAuthDialog(this, true, mxUtils.bind(this, function(remember, authSuccess)
- {
- var win = window.open(this.client.getAuthenticationUrl('https://' +
- window.location.host + '/dropbox.html'), 'dbauth');
-
- if (win != null)
- {
- window.onDropboxCallback = mxUtils.bind(this, function(token, authWindow)
- {
- if (acceptAuthResponse)
- {
- window.onDropboxCallback = null;
- acceptAuthResponse = false;
-
- try
- {
- if (token == null)
- {
- error({message: mxResources.get('accessDenied'), retry: auth});
- }
- else
- {
- if (authSuccess != null)
- {
- authSuccess();
- }
-
- this.client.setAccessToken(token);
- this.setUser(null);
-
- if (remember)
- {
- this.setPersistentToken(token);
- }
-
- success();
- }
- }
- catch (e)
- {
- error(e);
- }
- finally
- {
- if (authWindow != null)
- {
- authWindow.close();
- }
- }
- }
- else if (authWindow != null)
- {
- authWindow.close();
- }
- });
- }
- else
- {
- error({message: mxResources.get('serviceUnavailableOrBlocked'), retry: auth});
- }
- }), mxUtils.bind(this, function()
- {
- if (acceptAuthResponse)
- {
- window.onDropboxCallback = null;
- acceptAuthResponse = false;
- error({message: mxResources.get('accessDenied'), retry: auth});
- }
- }));
- });
-
- auth();
- }
- else
- {
- error({code: App.ERROR_BUSY});
- }
- };
- /**
- * Authorizes the client, gets the userId and calls <open>.
- */
- DropboxClient.prototype.executePromise = function(promise, success, error)
- {
- var doExecute = mxUtils.bind(this, function(failOnAuth)
- {
- var acceptResponse = true;
-
- var timeoutThread = window.setTimeout(mxUtils.bind(this, function()
- {
- acceptResponse = false;
- error({code: App.ERROR_TIMEOUT, retry: fn});
- }), this.ui.timeout);
-
- promise.then(mxUtils.bind(this, function(response)
- {
- window.clearTimeout(timeoutThread);
-
- if (acceptResponse && success != null)
- {
- success(response);
- }
- }));
- // Workaround for IE8/9 support with catch function
- promise['catch'](mxUtils.bind(this, function(err)
- {
- window.clearTimeout(timeoutThread);
-
- if (acceptResponse)
- {
- if (err != null && (err.status == 500 || err.status == 400 ||
- err.status == 401))
- {
- this.setUser(null);
- this.client.setAccessToken(null);
-
- if (!failOnAuth)
- {
- this.authenticate(function()
- {
- doExecute(true);
- }, error);
- }
- else
- {
- error({message: mxResources.get('accessDenied'), retry: mxUtils.bind(this, function()
- {
- this.authenticate(function()
- {
- fn(true);
- }, error);
- })});
- }
- }
- else
- {
- error({message: mxResources.get('error') + ' ' + err.status});
- }
- }
- }));
- });
-
- var fn = mxUtils.bind(this, function(failOnAuth)
- {
- if (this.user == null)
- {
- this.updateUser(function()
- {
- fn(true);
- }, error, failOnAuth);
- }
- else
- {
- doExecute(failOnAuth);
- }
- });
- if (this.client.getAccessToken() === null)
- {
- this.authenticate(function()
- {
- fn(true);
- }, error);
- }
- else
- {
- fn(false);
- }
- };
- /**
- * Checks if the client is authorized and calls the next step.
- */
- DropboxClient.prototype.getLibrary = function(path, success, error)
- {
- this.getFile(path, success, error, true);
- };
- /**
- * DenyConvert is ignored in this client, just added for API compatibility.
- */
- DropboxClient.prototype.getFile = function(path, success, error, asLibrary)
- {
- asLibrary = (asLibrary != null) ? asLibrary : false;
-
- if (/^https:\/\//i.test(path) || /\.vsdx$/i.test(path) || /\.gliffy$/i.test(path) || /\.png$/i.test(path))
- {
- // Should never be null
- if (this.token != null)
- {
- var tokens = path.split('/');
- var name = (tokens.length > 0) ? tokens[tokens.length - 1] : path;
-
- this.ui.convertFile(path, name, null, this.extension, success, error);
- }
- else
- {
- error({message: mxResources.get('accessDenied')});
- }
- }
- else
- {
- var arg = {path: '/' + path};
-
- if (urlParams['rev'] != null)
- {
- arg.rev = urlParams['rev'];
- }
-
- this.readFile(arg, mxUtils.bind(this, function(data, response)
- {
- success((asLibrary) ? new DropboxLibrary(this.ui, data, response) :
- new DropboxFile(this.ui, data, response));
- }), error);
- }
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- DropboxClient.prototype.readFile = function(arg, success, error)
- {
- var doExecute = mxUtils.bind(this, function(failOnAuth)
- {
- var acceptResponse = true;
-
- var timeoutThread = window.setTimeout(mxUtils.bind(this, function()
- {
- acceptResponse = false;
- error({code: App.ERROR_TIMEOUT});
- }), this.ui.timeout);
-
- // Workaround for Uncaught DOMException in filesDownload is to
- // get the metadata to handle the file not found case
- var checkPromise = this.client.filesGetMetadata({path: '/' + arg.path.substring(1), include_deleted: false});
-
- checkPromise.then(mxUtils.bind(this, function(response)
- {
- // ignore
- }));
- // Workaround for IE8/9 support with catch function
- checkPromise['catch'](function(err)
- {
- window.clearTimeout(timeoutThread);
-
- if (acceptResponse && err != null && err.status == 409)
- {
- acceptResponse = false;
- error({message: mxResources.get('fileNotFound')});
- }
- });
- // Download file in parallel
- // LATER: Report Uncaught DOMException with path/not_found in filesDownload
- var promise = this.client.filesDownload(arg);
-
- promise.then(mxUtils.bind(this, function(response)
- {
- window.clearTimeout(timeoutThread);
-
- if (acceptResponse)
- {
- acceptResponse = false;
-
- try
- {
- var reader = new FileReader();
-
- reader.onload = mxUtils.bind(this, function(event)
- {
- success(reader.result, response);
- });
-
- reader.readAsText(response.fileBlob);
- }
- catch (e)
- {
- error(e);
- }
- }
- }));
- // Workaround for IE8/9 support with catch function
- promise['catch'](mxUtils.bind(this, function(err)
- {
- window.clearTimeout(timeoutThread);
-
- if (acceptResponse)
- {
- acceptResponse = false;
- if (err != null && (err.status == 500 || err.status == 400 ||
- err.status == 401))
- {
- this.client.setAccessToken(null);
- this.setUser(null);
-
- if (!failOnAuth)
- {
- this.authenticate(function()
- {
- doExecute(true);
- }, error);
- }
- else
- {
- error({message: mxResources.get('accessDenied'), retry: mxUtils.bind(this, function()
- {
- this.authenticate(function()
- {
- fn(true);
- }, error);
- })});
- }
- }
- else
- {
- error({message: mxResources.get('error') + ' ' + err.status});
- }
- }
- }));
- });
-
- var fn = mxUtils.bind(this, function(failOnAuth)
- {
- if (this.user == null)
- {
- this.updateUser(function()
- {
- fn(true);
- }, error, failOnAuth);
- }
- else
- {
- doExecute(failOnAuth);
- }
- });
- if (this.client.getAccessToken() === null)
- {
- this.authenticate(function()
- {
- fn(true);
- }, error);
- }
- else
- {
- fn(false);
- }
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- DropboxClient.prototype.checkExists = function(filename, fn, noConfirm)
- {
- var promise = this.client.filesGetMetadata({path: '/' + filename.toLowerCase(), include_deleted: false});
-
- this.executePromise(promise, mxUtils.bind(this, function(response)
- {
- if (noConfirm)
- {
- fn(false, true, response);
- }
- else
- {
- this.ui.confirm(mxResources.get('replaceIt', [filename]), function()
- {
- fn(true, true, response);
- }, function()
- {
- fn(false, true, response);
- });
- }
- }), function(err)
- {
- fn(true, false);
- });
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- DropboxClient.prototype.renameFile = function(file, filename, success, error)
- {
- if (/[\\\/:\?\*"\|]/.test(filename))
- {
- error({message: mxResources.get('dropboxCharsNotAllowed')});
- }
- else
- {
- // Appends working directory of source file
- if (file != null && filename != null)
- {
- var path = file.stat.path_display.substring(1);
- var idx = path.lastIndexOf('/');
-
- if (idx > 0)
- {
- filename = path.substring(0, idx + 1) + filename;
- }
- }
-
- if (file != null && filename != null && file.stat.path_lower.substring(1) !== filename.toLowerCase())
- {
- // Checks if file exists
- this.checkExists(filename, mxUtils.bind(this, function(checked, exists, response)
- {
- if (checked)
- {
- var thenHandler = mxUtils.bind(this, function(deleteResponse)
- {
- var move = this.client.filesMove({from_path: file.stat.path_display, to_path: '/' +
- filename, autorename: false});
- this.executePromise(move, success, error);
- });
-
- // API fails on same name with different upper-/lowercase
- if (!exists || response.path_lower.substring(1) === filename.toLowerCase())
- {
- thenHandler();
- }
- else
- {
- // Deletes file first to avoid conflict in filesMove (non-atomic)
- var promise = this.client.filesDelete({path: '/' + filename.toLowerCase()});
- this.executePromise(promise, thenHandler, error);
- }
- }
- else
- {
- error();
- }
- }));
- }
- else
- {
- // Same name with different upper-/lowercase not supported by Dropbox API
- error({message: mxResources.get('invalidName')});
- }
- }
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- DropboxClient.prototype.insertLibrary = function(filename, data, success, error)
- {
- this.insertFile(filename, data, success, error, true);
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- DropboxClient.prototype.insertFile = function(filename, data, success, error, asLibrary)
- {
- asLibrary = (asLibrary != null) ? asLibrary : false;
-
- this.checkExists(filename, mxUtils.bind(this, function(checked)
- {
- if (checked)
- {
- this.saveFile(filename, data, mxUtils.bind(this, function(stat)
- {
- if (asLibrary)
- {
- success(new DropboxLibrary(this.ui, data, stat));
- }
- else
- {
- success(new DropboxFile(this.ui, data, stat));
- }
- }), error);
- }
- else
- {
- error();
- }
- }));
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- DropboxClient.prototype.saveFile = function(filename, data, success, error)
- {
- if (/[\\\/:\?\*"\|]/.test(filename))
- {
- error({message: mxResources.get('dropboxCharsNotAllowed')});
- }
- else if (data.length >= 150000000 /*150MB*/)
- {
- error({message: mxResources.get('drawingTooLarge') + ' (' +
- this.ui.formatFileSize(data.length) + ' / 150 MB)'});
- }
- else
- {
- // Mute switch is ignored
- var promise = this.client.filesUpload({path: '/' + filename, mode: {'.tag': 'overwrite'},
- contents: new Blob([data], {type: 'text/plain'}), mute: true});
- this.executePromise(promise, success, error);
- }
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- DropboxClient.prototype.pickLibrary = function(fn)
- {
- // Authentication will be carried out on open to make sure the
- // autosave does not show an auth dialog. Showing it here will
- // block the second dialog (the file picker) so it's too early.
- Dropbox.choose(
- {
- linkType : 'direct',
- cancel: mxUtils.bind(this, function()
- {
- // do nothing
- }),
- success : mxUtils.bind(this, function(files)
- {
- if (this.ui.spinner.spin(document.body, mxResources.get('loading')))
- {
- var error = mxUtils.bind(this, function(e)
- {
- this.ui.spinner.stop();
- this.ui.handleError(e);
- });
-
- var tmp = files[0].link.indexOf(this.appPath);
-
- if (tmp > 0)
- {
- // Checks if file is in app folder by loading file from there and comparing relative path and size
- // KNOWN: This check fails if a file is inside a drawio directory with same relative path and size
- var rel = decodeURIComponent(files[0].link.substring(tmp + this.appPath.length - 1));
-
- this.readFile({path: rel}, mxUtils.bind(this, function(data, stat)
- {
- if (stat != null && parseInt(files[0].bytes) === parseInt(stat.size) && rel === stat.path_display)
- {
- // No need to load file a second time
- try
- {
- this.ui.spinner.stop();
- fn(rel.substring(1), new DropboxLibrary(this.ui, data, stat));
- }
- catch (e)
- {
- this.ui.handleError(e);
- }
- }
- else
- {
- this.createLibrary(files[0], fn, error);
- }
- }), error);
- }
- else
- {
- this.createLibrary(files[0], fn, error);
- }
- }
- })
- });
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- DropboxClient.prototype.createLibrary = function(file, success, error)
- {
- this.ui.confirm(mxResources.get('note') + ': ' + mxResources.get('fileWillBeSavedInAppFolder', [file.name]), mxUtils.bind(this, function()
- {
- this.ui.loadUrl(file.link, mxUtils.bind(this, function(data)
- {
- this.insertFile(file.name, data, mxUtils.bind(this, function(newFile)
- {
- try
- {
- this.ui.spinner.stop();
- success(newFile.getHash().substring(1), newFile);
- }
- catch (e)
- {
- error(e);
- }
- }), error, true);
- }), error);
- }), mxUtils.bind(this, function()
- {
- this.ui.spinner.stop();
- }));
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- DropboxClient.prototype.pickFile = function(fn, readOnly)
- {
- if (Dropbox.choose != null)
- {
- fn = (fn != null) ? fn : mxUtils.bind(this, function(path, file)
- {
- this.ui.loadFile((path != null) ? 'D' + encodeURIComponent(path) : file.getHash(), null, file);
- });
-
- // Authentication will be carried out on open to make sure the
- // autosave does not show an auth dialog. Showing it here will
- // block the second dialog (the file picker) so it's too early.
- Dropbox.choose(
- {
- linkType : 'direct',
- cancel: mxUtils.bind(this, function()
- {
- // do nothing
- }),
- success : mxUtils.bind(this, function(files)
- {
- if (this.ui.spinner.spin(document.body, mxResources.get('loading')))
- {
- // File used for read-only
- if (readOnly)
- {
- this.ui.spinner.stop();
- fn(files[0].link);
- }
- else
- {
- var error = mxUtils.bind(this, function(e)
- {
- this.ui.spinner.stop();
- this.ui.handleError(e);
- });
-
- var success = mxUtils.bind(this, function(path, file)
- {
- this.ui.spinner.stop();
- fn(path, file);
- });
-
- if ((/\.vsdx$/i.test(files[0].name) || /\.gliffy$/i.test(files[0].name) ||
- /\.png$/i.test(files[0].name)))
- {
- success(files[0].link);
- }
- else
- {
- var tmp = files[0].link.indexOf(this.appPath);
-
- if (tmp > 0)
- {
- // Checks if file is in app folder by loading file from there and comparing relative path and size
- // KNOWN: This check fails if a file is inside a drawio directory with same relative path and size
- var rel = decodeURIComponent(files[0].link.substring(tmp + this.appPath.length - 1));
-
- this.readFile({path: rel}, mxUtils.bind(this, function(data, stat)
- {
- if (stat != null && parseInt(files[0].bytes) === parseInt(stat.size) && rel === stat.path_display)
- {
- this.ui.spinner.stop();
-
- // No need to load file a second time
- fn(rel.substring(1), new DropboxFile(this.ui, data, stat));
- }
- else
- {
- this.createFile(files[0], success, error);
- }
- }), error);
- }
- else
- {
- this.createFile(files[0], success, error);
- }
- }
- }
- }
- })
- });
- }
- else
- {
- this.ui.handleError({message: mxResources.get('serviceUnavailableOrBlocked')});
- }
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- DropboxClient.prototype.createFile = function(file, success, error)
- {
- this.ui.loadUrl(file.link, mxUtils.bind(this, function(data)
- {
- if (data != null && data.length > 0)
- {
- this.ui.confirm(mxResources.get('note') + ': ' + mxResources.get('fileWillBeSavedInAppFolder', [file.name]), mxUtils.bind(this, function()
- {
- this.insertFile(file.name, data, mxUtils.bind(this, function(newFile)
- {
- success(file.name, newFile);
- }), error);
- }), mxUtils.bind(this, function()
- {
- this.ui.spinner.stop();
- }));
- }
- else
- {
- this.ui.spinner.stop();
- error({message: mxResources.get('errorLoadingFile')});
- }
- }), error, /(\.png)$/i.test(file.name));
- };
|