OneDriveClient.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. /**
  2. * Copyright (c) 2006-2017, JGraph Ltd
  3. * Copyright (c) 2006-2017, Gaudenz Alder
  4. */
  5. OneDriveClient = function(editorUi)
  6. {
  7. DrawioClient.call(this, editorUi, 'odauth');
  8. this.token = this.token;
  9. WL.init({client_id: this.clientId, redirect_uri: this.redirectUri});
  10. };
  11. // Extends DrawioClient
  12. mxUtils.extend(OneDriveClient, DrawioClient);
  13. /**
  14. * Specifies if thumbnails should be enabled. Default is true.
  15. * LATER: If thumbnails are disabled, make sure to replace the
  16. * existing thumbnail with the placeholder only once.
  17. */
  18. OneDriveClient.prototype.clientId = (window.location.hostname == 'test.draw.io') ? '0000000048148130' :
  19. ((window.location.hostname == 'drive.draw.io') ? '000000004413EC37' : '0000000040145A19');
  20. /**
  21. * OAuth 2.0 scopes for installing Drive Apps.
  22. */
  23. OneDriveClient.prototype.scopes = 'wl.skydrive_update wl.signin';
  24. /**
  25. * OAuth 2.0 scopes for installing Drive Apps.
  26. */
  27. OneDriveClient.prototype.redirectUri = 'https://' + window.location.hostname + '/onedrive.html';
  28. /**
  29. * Executes the first step for connecting to Google Drive.
  30. */
  31. OneDriveClient.prototype.extension = '.html';
  32. /**
  33. * Executes the first step for connecting to Google Drive.
  34. */
  35. OneDriveClient.prototype.baseUrl = 'https://api.onedrive.com/v1.0';
  36. /**
  37. * Checks if the client is authorized and calls the next step.
  38. */
  39. OneDriveClient.prototype.updateUser = function(success, error, failOnAuth)
  40. {
  41. var acceptResponse = true;
  42. var timeoutThread = window.setTimeout(mxUtils.bind(this, function()
  43. {
  44. acceptResponse = false;
  45. error({code: App.ERROR_TIMEOUT});
  46. }), this.ui.timeout);
  47. mxUtils.get(this.baseUrl + '/drive?access_token=' + this.token, mxUtils.bind(this, function(req)
  48. {
  49. window.clearTimeout(timeoutThread);
  50. if (acceptResponse)
  51. {
  52. if (req.getStatus() < 200 || req.getStatus() >= 300)
  53. {
  54. if (!failOnAuth)
  55. {
  56. this.logout();
  57. this.authenticate(mxUtils.bind(this, function()
  58. {
  59. this.updateUser(success, error, true);
  60. }), error);
  61. }
  62. else
  63. {
  64. error({message: mxResources.get('accessDenied')});
  65. }
  66. }
  67. else
  68. {
  69. var data = JSON.parse(req.getText());
  70. this.setUser(new DrawioUser(data.owner.user.id, null, data.owner.user.displayName));
  71. success();
  72. }
  73. }
  74. }), error);
  75. };
  76. /**
  77. * Authorizes the client, gets the userId and calls <open>.
  78. */
  79. OneDriveClient.prototype.authenticate = function(success, error)
  80. {
  81. if (window.onOneDriveCallback == null)
  82. {
  83. var auth = mxUtils.bind(this, function()
  84. {
  85. var acceptAuthResponse = true;
  86. this.ui.showAuthDialog(this, true, mxUtils.bind(this, function(remember, authSuccess)
  87. {
  88. var url = 'https://login.live.com/oauth20_authorize.srf?client_id=' + this.clientId +
  89. '&scope=' + encodeURIComponent(this.scopes) + '&response_type=token' +
  90. '&redirect_uri=' + encodeURIComponent(this.redirectUri);
  91. var width = 525,
  92. height = 525,
  93. screenX = window.screenX,
  94. screenY = window.screenY,
  95. outerWidth = window.outerWidth,
  96. outerHeight = window.outerHeight;
  97. var left = screenX + Math.max(outerWidth - width, 0) / 2;
  98. var top = screenY + Math.max(outerHeight - height, 0) / 2;
  99. var features = ['width=' + width, 'height=' + height,
  100. 'top=' + top, 'left=' + left,
  101. 'status=no', 'resizable=yes',
  102. 'toolbar=no', 'menubar=no',
  103. 'scrollbars=yes'];
  104. var popup = window.open(url, 'odauth', features.join(','));
  105. if (popup != null)
  106. {
  107. window.onOneDriveCallback = mxUtils.bind(this, function(token, authWindow)
  108. {
  109. if (acceptAuthResponse)
  110. {
  111. window.onOneDriveCallback = null;
  112. acceptAuthResponse = false;
  113. try
  114. {
  115. if (token == null)
  116. {
  117. error({message: mxResources.get('accessDenied'), retry: auth});
  118. }
  119. else
  120. {
  121. if (authSuccess != null)
  122. {
  123. authSuccess();
  124. }
  125. this.setUser(null);
  126. this.token = token;
  127. if (remember)
  128. {
  129. this.setPersistentToken(token);
  130. }
  131. success();
  132. }
  133. }
  134. catch (e)
  135. {
  136. error(e);
  137. }
  138. finally
  139. {
  140. if (authWindow != null)
  141. {
  142. authWindow.close();
  143. }
  144. }
  145. }
  146. else if (authWindow != null)
  147. {
  148. authWindow.close();
  149. }
  150. });
  151. popup.focus();
  152. }
  153. }), mxUtils.bind(this, function()
  154. {
  155. if (acceptAuthResponse)
  156. {
  157. window.onOneDriveCallback = null;
  158. acceptAuthResponse = false;
  159. error({message: mxResources.get('accessDenied'), retry: auth});
  160. }
  161. }));
  162. });
  163. auth();
  164. }
  165. else
  166. {
  167. error({code: App.ERROR_BUSY});
  168. }
  169. };
  170. /**
  171. * Checks if the client is authorized and calls the next step.
  172. */
  173. OneDriveClient.prototype.executeRequest = function(url, success, error)
  174. {
  175. var doExecute = mxUtils.bind(this, function(failOnAuth)
  176. {
  177. var acceptResponse = true;
  178. var timeoutThread = window.setTimeout(mxUtils.bind(this, function()
  179. {
  180. acceptResponse = false;
  181. error({code: App.ERROR_TIMEOUT, retry: fn});
  182. }), this.ui.timeout);
  183. mxUtils.get(url + '?access_token=' + this.token, mxUtils.bind(this, function(req)
  184. {
  185. window.clearTimeout(timeoutThread);
  186. if (acceptResponse)
  187. {
  188. // 404 (file not found) is a valid response for checkExists
  189. if ((req.getStatus() >= 200 && req.getStatus() <= 299) || req.getStatus() == 404)
  190. {
  191. success(req);
  192. }
  193. else if (req.getStatus() === 401)
  194. {
  195. this.clearPersistentToken();
  196. this.setUser(null);
  197. this.token = null;
  198. if (!failOnAuth)
  199. {
  200. this.authenticate(function()
  201. {
  202. doExecute(true);
  203. }, error);
  204. }
  205. else
  206. {
  207. error({message: mxResources.get('accessDenied'), retry: mxUtils.bind(this, function()
  208. {
  209. this.authenticate(function()
  210. {
  211. fn(true);
  212. }, error);
  213. })});
  214. }
  215. }
  216. else
  217. {
  218. error(this.parseRequestText(req));
  219. }
  220. }
  221. }), error);
  222. });
  223. var fn = mxUtils.bind(this, function(failOnAuth)
  224. {
  225. if (this.user == null)
  226. {
  227. this.updateUser(function()
  228. {
  229. fn(true);
  230. }, error, failOnAuth);
  231. }
  232. else
  233. {
  234. doExecute(failOnAuth);
  235. }
  236. });
  237. if (this.token == null)
  238. {
  239. this.authenticate(function()
  240. {
  241. fn(true);
  242. }, error);
  243. }
  244. else
  245. {
  246. fn(false);
  247. }
  248. };
  249. /**
  250. * Checks if the client is authorized and calls the next step.
  251. */
  252. OneDriveClient.prototype.getLibrary = function(id, success, error)
  253. {
  254. this.getFile(id, success, error, false, true);
  255. };
  256. /**
  257. * Checks if the client is authorized and calls the next step.
  258. */
  259. OneDriveClient.prototype.getFile = function(id, success, error, denyConvert, asLibrary)
  260. {
  261. asLibrary = (asLibrary != null) ? asLibrary : false;
  262. this.executeRequest(this.baseUrl + '/drive/items/' + id, mxUtils.bind(this, function(req)
  263. {
  264. if (req.getStatus() >= 200 && req.getStatus() <= 299)
  265. {
  266. var meta = JSON.parse(req.getText());
  267. // Handles .vsdx, Gliffy and PNG+XML files by creating a temporary file
  268. if ((/\.vsdx$/i.test(meta.name) || /\.gliffy$/i.test(meta.name) || /\.png$/i.test(meta.name)))
  269. {
  270. var mimeType = (meta.file != null) ? meta.file.mimeType : null;
  271. this.ui.convertFile(meta['@content.downloadUrl'], meta.name, mimeType,
  272. this.extension, success, error);
  273. }
  274. else
  275. {
  276. var acceptResponse = true;
  277. var timeoutThread = window.setTimeout(mxUtils.bind(this, function()
  278. {
  279. acceptResponse = false;
  280. error({code: App.ERROR_TIMEOUT})
  281. }), this.ui.timeout);
  282. this.ui.loadUrl(meta['@content.downloadUrl'], mxUtils.bind(this, function(data)
  283. {
  284. window.clearTimeout(timeoutThread);
  285. if (acceptResponse)
  286. {
  287. if (asLibrary)
  288. {
  289. success(new OneDriveLibrary(this.ui, data, meta));
  290. }
  291. else
  292. {
  293. success(new OneDriveFile(this.ui, data, meta));
  294. }
  295. }
  296. }), mxUtils.bind(this, function(req)
  297. {
  298. window.clearTimeout(timeoutThread);
  299. if (acceptResponse)
  300. {
  301. error(this.parseRequestText(req));
  302. }
  303. }), meta.file != null && meta.file.mimeType != null &&
  304. meta.file.mimeType.substring(0, 6) == 'image/');
  305. }
  306. }
  307. else
  308. {
  309. error(this.parseRequestText(req));
  310. }
  311. }), error);
  312. };
  313. /**
  314. * Translates this point by the given vector.
  315. *
  316. * @param {number} dx X-coordinate of the translation.
  317. * @param {number} dy Y-coordinate of the translation.
  318. */
  319. OneDriveClient.prototype.renameFile = function(file, filename, success, error)
  320. {
  321. if (file != null && filename != null)
  322. {
  323. // TODO: How to force overwrite file with same name?
  324. this.checkExists(file.meta.parentReference.id, filename, false, mxUtils.bind(this, function(checked)
  325. {
  326. if (checked)
  327. {
  328. var url = this.baseUrl + '/drive/items/' + file.meta.id;
  329. this.writeFile(url, JSON.stringify({name: filename}), 'PATCH', 'application/json', success, error);
  330. }
  331. else
  332. {
  333. error();
  334. }
  335. }));
  336. }
  337. };
  338. /**
  339. * Translates this point by the given vector.
  340. *
  341. * @param {number} dx X-coordinate of the translation.
  342. * @param {number} dy Y-coordinate of the translation.
  343. */
  344. OneDriveClient.prototype.moveFile = function(id, folderId, success, error)
  345. {
  346. var url = this.baseUrl + '/drive/items/' + id;
  347. this.writeFile(url, JSON.stringify({parentReference: {id: folderId}}), 'PATCH', 'application/json', success, error);
  348. };
  349. /**
  350. * Translates this point by the given vector.
  351. *
  352. * @param {number} dx X-coordinate of the translation.
  353. * @param {number} dy Y-coordinate of the translation.
  354. */
  355. OneDriveClient.prototype.insertLibrary = function(filename, data, success, error, folderId)
  356. {
  357. this.insertFile(filename, data, success, error, true, folderId);
  358. };
  359. /**
  360. * Translates this point by the given vector.
  361. *
  362. * @param {number} dx X-coordinate of the translation.
  363. * @param {number} dy Y-coordinate of the translation.
  364. */
  365. OneDriveClient.prototype.insertFile = function(filename, data, success, error, asLibrary, folderId)
  366. {
  367. asLibrary = (asLibrary != null) ? asLibrary : false;
  368. this.checkExists(folderId, filename, true, mxUtils.bind(this, function(checked)
  369. {
  370. if (checked)
  371. {
  372. var folder = (folderId != null) ? 'items/' + folderId : 'special/documents';
  373. var url = this.baseUrl + '/drive/' + folder + '/children/' + filename + '/content';
  374. this.writeFile(url, data, 'PUT', null, mxUtils.bind(this, function(meta)
  375. {
  376. if (asLibrary)
  377. {
  378. success(new OneDriveLibrary(this.ui, data, meta));
  379. }
  380. else
  381. {
  382. success(new OneDriveFile(this.ui, data, meta));
  383. }
  384. }), error);
  385. }
  386. else
  387. {
  388. error();
  389. }
  390. }))
  391. };
  392. /**
  393. * Translates this point by the given vector.
  394. *
  395. * @param {number} dx X-coordinate of the translation.
  396. * @param {number} dy Y-coordinate of the translation.
  397. */
  398. OneDriveClient.prototype.checkExists = function(parentId, filename, askReplace, fn)
  399. {
  400. var path = (parentId != null) ? 'items/' + parentId : 'special/documents';
  401. this.executeRequest(this.baseUrl + '/drive/' + path + '/children/' + filename, mxUtils.bind(this, function(req)
  402. {
  403. if (req.getStatus() == 404)
  404. {
  405. fn(true);
  406. }
  407. else
  408. {
  409. if (askReplace)
  410. {
  411. this.ui.spinner.stop();
  412. this.ui.confirm(mxResources.get('replaceIt', [filename]), function()
  413. {
  414. fn(true);
  415. }, function()
  416. {
  417. fn(false);
  418. });
  419. }
  420. else
  421. {
  422. this.ui.spinner.stop();
  423. this.ui.showError(mxResources.get('error'), mxResources.get('fileExists'), mxResources.get('ok'), function()
  424. {
  425. fn(false);
  426. });
  427. }
  428. }
  429. }), function(req)
  430. {
  431. fn(false);
  432. }, true);
  433. };
  434. /**
  435. * Translates this point by the given vector.
  436. *
  437. * @param {number} dx X-coordinate of the translation.
  438. * @param {number} dy Y-coordinate of the translation.
  439. */
  440. OneDriveClient.prototype.saveFile = function(file, success, error)
  441. {
  442. var url = this.baseUrl + '/drive/items/' + file.meta.id + '/content/';
  443. this.writeFile(url, file.getData(), 'PUT', null, success, error);
  444. };
  445. /**
  446. * Translates this point by the given vector.
  447. *
  448. * @param {number} dx X-coordinate of the translation.
  449. * @param {number} dy Y-coordinate of the translation.
  450. */
  451. OneDriveClient.prototype.writeFile = function(url, data, method, contentType, success, error)
  452. {
  453. if (url != null && data != null)
  454. {
  455. var doExecute = mxUtils.bind(this, function(failOnAuth)
  456. {
  457. var acceptResponse = true;
  458. var timeoutThread = window.setTimeout(mxUtils.bind(this, function()
  459. {
  460. acceptResponse = false;
  461. error({code: App.ERROR_TIMEOUT, retry: fn});
  462. }), this.ui.timeout);
  463. var req = new mxXmlRequest(url + '?access_token=' + this.token, data, method);
  464. req.setRequestHeaders = function(request, params)
  465. {
  466. // Space deletes content type header. Specification says "text/plain"
  467. // should work but returns an 415 Unsupported Media Type error
  468. request.setRequestHeader('Content-Type', contentType || ' ');
  469. };
  470. req.send(mxUtils.bind(this, function(req)
  471. {
  472. window.clearTimeout(timeoutThread);
  473. if (acceptResponse)
  474. {
  475. if (req.getStatus() >= 200 && req.getStatus() <= 299)
  476. {
  477. success(JSON.parse(req.getText()));
  478. }
  479. else if (req.getStatus() === 401)
  480. {
  481. this.clearPersistentToken();
  482. this.setUser(null);
  483. this.token = null;
  484. if (!failOnAuth)
  485. {
  486. this.authenticate(function()
  487. {
  488. doExecute(true);
  489. }, error);
  490. }
  491. else
  492. {
  493. error({message: mxResources.get('accessDenied'), retry: mxUtils.bind(this, function()
  494. {
  495. this.authenticate(function()
  496. {
  497. fn(true);
  498. }, error);
  499. })});
  500. }
  501. }
  502. else
  503. {
  504. error(this.parseRequestText(req));
  505. }
  506. }
  507. }), mxUtils.bind(this, function(req)
  508. {
  509. window.clearTimeout(timeoutThread);
  510. if (acceptResponse)
  511. {
  512. error(this.parseRequestText(req));
  513. }
  514. }));
  515. });
  516. var fn = mxUtils.bind(this, function(failOnAuth)
  517. {
  518. if (this.user == null)
  519. {
  520. this.updateUser(function()
  521. {
  522. fn(true);
  523. }, error, failOnAuth);
  524. }
  525. else
  526. {
  527. doExecute(failOnAuth);
  528. }
  529. });
  530. if (this.token == null)
  531. {
  532. this.authenticate(function()
  533. {
  534. fn(true);
  535. }, error);
  536. }
  537. else
  538. {
  539. fn(false);
  540. }
  541. }
  542. else
  543. {
  544. error({message: mxResources.get('unknownError')});
  545. }
  546. };
  547. /**
  548. * Checks if the client is authorized and calls the next step.
  549. */
  550. OneDriveClient.prototype.parseRequestText = function(req)
  551. {
  552. var result = {message: mxResources.get('unknownError')};
  553. try
  554. {
  555. result = JSON.parse(req.getText());
  556. }
  557. catch (e)
  558. {
  559. // ignore
  560. }
  561. return result;
  562. };
  563. /**
  564. * Checks if the client is authorized and calls the next step.
  565. */
  566. OneDriveClient.prototype.pickLibrary = function(fn)
  567. {
  568. this.pickFile(fn);
  569. };
  570. /**
  571. * Checks if the client is authorized and calls the next step.
  572. */
  573. OneDriveClient.prototype.pickFolder = function(fn)
  574. {
  575. WL.fileDialog(
  576. {
  577. mode: 'save'
  578. }).then(
  579. function (resp)
  580. {
  581. fn(resp);
  582. },
  583. function (responseFailed)
  584. {
  585. fn(null);
  586. }
  587. );
  588. };
  589. /**
  590. * Checks if the client is authorized and calls the next step.
  591. */
  592. OneDriveClient.prototype.pickFile = function(fn)
  593. {
  594. fn = (fn != null) ? fn : mxUtils.bind(this, function(id)
  595. {
  596. this.ui.loadFile('W' + encodeURIComponent(id));
  597. });
  598. WL.fileDialog(
  599. {
  600. mode: 'open',
  601. select: 'multi'
  602. }).then(
  603. function (resp)
  604. {
  605. if (resp != null && resp.data != null && resp.data.files != null)
  606. {
  607. for (var i = 0; i < resp.data.files.length; i++)
  608. {
  609. var id = resp.data.files[i].id;
  610. id = id.substring(id.lastIndexOf('.') + 1);
  611. fn(id);
  612. }
  613. }
  614. },
  615. function (responseFailed) {}
  616. );
  617. };
  618. /**
  619. * Checks if the client is authorized and calls the next step.
  620. */
  621. OneDriveClient.prototype.logout = function()
  622. {
  623. this.clearPersistentToken();
  624. this.setUser(null);
  625. this.token = null;
  626. // LATER: Check why async callback does not work
  627. WL.logout();
  628. };