preferences_sync.js (25212B)
1 /* 2 ***** BEGIN LICENSE BLOCK ***** 3 4 Copyright © 2008–2013 Center for History and New Media 5 George Mason University, Fairfax, Virginia, USA 6 http://zotero.org 7 8 This file is part of Zotero. 9 10 Zotero is free software: you can redistribute it and/or modify 11 it under the terms of the GNU Affero General Public License as published by 12 the Free Software Foundation, either version 3 of the License, or 13 (at your option) any later version. 14 15 Zotero is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU Affero General Public License for more details. 19 20 You should have received a copy of the GNU Affero General Public License 21 along with Zotero. If not, see <http://www.gnu.org/licenses/>. 22 23 ***** END LICENSE BLOCK ***** 24 */ 25 26 "use strict"; 27 Components.utils.import("resource://gre/modules/Services.jsm"); 28 Components.utils.import("resource://gre/modules/osfile.jsm"); 29 Components.utils.import("resource://zotero/config.js"); 30 31 Zotero_Preferences.Sync = { 32 init: Zotero.Promise.coroutine(function* () { 33 this.updateStorageSettingsUI(); 34 this.updateStorageSettingsGroupsUI(); 35 36 var username = Zotero.Users.getCurrentUsername() || Zotero.Prefs.get('sync.server.username') || " "; 37 var apiKey = yield Zotero.Sync.Data.Local.getAPIKey(); 38 this.displayFields(apiKey ? username : ""); 39 40 var pass = Zotero.Sync.Runner.getStorageController('webdav').password; 41 if (pass) { 42 document.getElementById('storage-password').value = pass; 43 } 44 45 if (apiKey) { 46 try { 47 var keyInfo = yield Zotero.Sync.Runner.checkAccess( 48 Zotero.Sync.Runner.getAPIClient({apiKey}), 49 {timeout: 5000} 50 ); 51 this.displayFields(keyInfo.username); 52 } 53 catch (e) { 54 // API key wrong/invalid 55 if (e instanceof Zotero.Error && e.error == Zotero.Error.ERROR_API_KEY_INVALID) { 56 Zotero.alert( 57 window, 58 Zotero.getString('general.error'), 59 Zotero.getString('sync.error.apiKeyInvalid', Zotero.clientName) 60 ); 61 this.unlinkAccount(false); 62 } 63 else { 64 throw e; 65 } 66 } 67 } 68 69 this.initResetPane(); 70 }), 71 72 displayFields: function (username) { 73 document.getElementById('sync-unauthorized').hidden = !!username; 74 document.getElementById('sync-authorized').hidden = !username; 75 document.getElementById('sync-reset-tab').disabled = !username; 76 document.getElementById('sync-username').value = username; 77 document.getElementById('sync-password').value = ''; 78 document.getElementById('sync-username-textbox').value = Zotero.Prefs.get('sync.server.username'); 79 80 var img = document.getElementById('sync-status-indicator'); 81 img.removeAttribute('verified'); 82 img.removeAttribute('animated'); 83 84 window.sizeToContent(); 85 }, 86 87 88 credentialsChange: function (event) { 89 var username = document.getElementById('sync-username-textbox'); 90 var password = document.getElementById('sync-password'); 91 92 var syncAuthButton = document.getElementById('sync-auth-button'); 93 94 syncAuthButton.setAttribute('disabled', 'true'); 95 96 // When using backspace, the value is not updated until after the keypress event 97 setTimeout(function() { 98 if (username.value.length && password.value.length) { 99 syncAuthButton.setAttribute('disabled', 'false'); 100 } 101 }); 102 }, 103 104 105 credentialsKeyPress: function (event) { 106 if (event.keyCode == 13) { 107 this.linkAccount(event); 108 event.preventDefault(); 109 } 110 }, 111 112 113 trimUsername: function () { 114 var tb = document.getElementById('sync-username-textbox'); 115 var username = tb.value; 116 var trimmed = username.trim(); 117 if (username != trimmed) { 118 tb.value = trimmed; 119 // Setting .value alone doesn't seem to cause the pref to sync, so set it manually 120 Zotero.Prefs.set('sync.server.username', trimmed); 121 } 122 }, 123 124 125 linkAccount: Zotero.Promise.coroutine(function* (event) { 126 this.trimUsername(); 127 var username = document.getElementById('sync-username-textbox').value; 128 var password = document.getElementById('sync-password').value; 129 130 if (!username.length || !password.length) { 131 this.updateSyncIndicator(); 132 return; 133 } 134 135 // Try to acquire API key with current credentials 136 this.updateSyncIndicator('animated'); 137 try { 138 var json = yield Zotero.Sync.Runner.createAPIKeyFromCredentials(username, password); 139 } 140 catch (e) { 141 setTimeout(function () { 142 Zotero.alert( 143 window, 144 Zotero.getString('general.error'), 145 e.message 146 ); 147 }); 148 throw e; 149 } 150 finally { 151 this.updateSyncIndicator(); 152 } 153 154 // Invalid credentials 155 if (!json) { 156 Zotero.alert(window, 157 Zotero.getString('general.error'), 158 Zotero.getString('sync.error.invalidLogin') 159 ); 160 return; 161 } 162 163 if (!(yield Zotero.Sync.Data.Local.checkUser(window, json.userID, json.username))) { 164 // createAPIKeyFromCredentials will have created an API key, 165 // but user decided not to use it, so we remove it here. 166 Zotero.Sync.Runner.deleteAPIKey(); 167 return; 168 } 169 this.displayFields(json.username); 170 }), 171 172 /** 173 * Updates the auth indicator icon, depending on status 174 * @param {string} status 175 */ 176 updateSyncIndicator: function (status) { 177 var img = document.getElementById('sync-status-indicator'); 178 179 img.removeAttribute('animated'); 180 if (status == 'animated') { 181 img.setAttribute('animated', true); 182 } 183 }, 184 185 unlinkAccount: Zotero.Promise.coroutine(function* (showAlert=true) { 186 if (showAlert) { 187 var check = {value: false}; 188 var ps = Services.prompt; 189 var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING) + 190 (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL); 191 var index = ps.confirmEx( 192 null, 193 Zotero.getString('general.warning'), 194 Zotero.getString('account.unlinkWarning', Zotero.clientName), 195 buttonFlags, 196 Zotero.getString('account.unlinkWarning.button'), null, null, 197 Zotero.getString('account.unlinkWarning.removeData', Zotero.clientName), 198 check 199 ); 200 if (index == 0) { 201 if (check.value) { 202 var resetDataDirFile = OS.Path.join(Zotero.DataDirectory.dir, 'reset-data-directory'); 203 yield Zotero.File.putContentsAsync(resetDataDirFile, ''); 204 205 yield Zotero.Sync.Runner.deleteAPIKey(); 206 Zotero.Prefs.clear('sync.server.username'); 207 return Zotero.Utilities.Internal.quitZotero(true); 208 } 209 } else { 210 return; 211 } 212 } 213 214 this.displayFields(); 215 Zotero.Prefs.clear('sync.librariesToSync'); 216 yield Zotero.Sync.Runner.deleteAPIKey(); 217 }), 218 219 220 showLibrariesToSyncDialog: function() { 221 var io = {}; 222 window.openDialog('chrome://zotero/content/preferences/librariesToSync.xul', 223 "zotero-preferences-librariesToSyncDialog", "chrome,modal,centerscreen", io); 224 }, 225 226 227 dblClickLibraryToSync: function (event) { 228 var tree = document.getElementById("libraries-to-sync-tree"); 229 var row = {}, col = {}, child = {}; 230 tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, child); 231 232 // Below the list or on checkmark column 233 if (!col.value || col.value.element.id == 'libraries-to-sync-checked') { 234 return; 235 } 236 // if dblclicked anywhere but the checkbox update pref 237 return this.toggleLibraryToSync(row.value); 238 }, 239 240 241 clickLibraryToSync: function (event) { 242 var tree = document.getElementById("libraries-to-sync-tree"); 243 var row = {}, col = {}, child = {}; 244 tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, child); 245 246 // Below the list or not on checkmark column 247 if (!col.value || col.value.element.id != 'libraries-to-sync-checked') { 248 return; 249 } 250 // if clicked on checkbox update pref 251 return this.toggleLibraryToSync(row.value); 252 }, 253 254 255 toggleLibraryToSync: function (index) { 256 var treechildren = document.getElementById('libraries-to-sync-rows'); 257 if (index >= treechildren.childNodes.length) { 258 return; 259 } 260 var row = treechildren.childNodes[index]; 261 var val = row.firstChild.childNodes[1].getAttribute('value'); 262 if (!val) { 263 return 264 } 265 266 var librariesToSkip = JSON.parse(Zotero.Prefs.get('sync.librariesToSkip') || '[]'); 267 var indexOfId = librariesToSkip.indexOf(val); 268 if (indexOfId == -1) { 269 librariesToSkip.push(val); 270 } else { 271 librariesToSkip.splice(indexOfId, 1); 272 } 273 Zotero.Prefs.set('sync.librariesToSkip', JSON.stringify(librariesToSkip)); 274 275 var cell = row.firstChild.firstChild; 276 cell.setAttribute('value', indexOfId != -1); 277 }, 278 279 280 initLibrariesToSync: Zotero.Promise.coroutine(function* () { 281 var tree = document.getElementById("libraries-to-sync-tree"); 282 var treechildren = document.getElementById('libraries-to-sync-rows'); 283 while (treechildren.hasChildNodes()) { 284 treechildren.removeChild(treechildren.firstChild); 285 } 286 287 function addRow(libraryName, id, checked=false, editable=true) { 288 var treeitem = document.createElement('treeitem'); 289 var treerow = document.createElement('treerow'); 290 var checkboxCell = document.createElement('treecell'); 291 var nameCell = document.createElement('treecell'); 292 293 nameCell.setAttribute('label', libraryName); 294 nameCell.setAttribute('value', id); 295 nameCell.setAttribute('editable', false); 296 checkboxCell.setAttribute('value', checked); 297 checkboxCell.setAttribute('editable', editable); 298 299 treerow.appendChild(checkboxCell); 300 treerow.appendChild(nameCell); 301 treeitem.appendChild(treerow); 302 treechildren.appendChild(treeitem); 303 } 304 305 // Add loading row while we're loading a group list 306 var loadingLabel = Zotero.getString("zotero.preferences.sync.librariesToSync.loadingLibraries"); 307 addRow(loadingLabel, "loading", false, false); 308 309 var apiKey = yield Zotero.Sync.Data.Local.getAPIKey(); 310 var client = Zotero.Sync.Runner.getAPIClient({apiKey}); 311 var groups = []; 312 try { 313 // Load up remote groups 314 var keyInfo = yield Zotero.Sync.Runner.checkAccess(client, {timeout: 5000}); 315 groups = yield client.getGroups(keyInfo.userID); 316 } 317 catch (e) { 318 // Connection problems 319 if ((e instanceof Zotero.HTTP.UnexpectedStatusException) 320 || (e instanceof Zotero.HTTP.TimeoutException) 321 || (e instanceof Zotero.HTTP.BrowserOfflineException)) { 322 Zotero.alert( 323 window, 324 Zotero.getString('general.error'), 325 Zotero.getString('sync.error.checkConnection', Zotero.clientName) 326 ); 327 } 328 else { 329 throw e; 330 } 331 document.getElementsByTagName('dialog')[0].acceptDialog(); 332 } 333 334 // Remove the loading row 335 treechildren.removeChild(treechildren.firstChild); 336 337 var librariesToSkip = JSON.parse(Zotero.Prefs.get('sync.librariesToSkip') || '[]'); 338 // Add default rows 339 addRow(Zotero.getString("pane.collections.libraryAndFeeds"), "L" + Zotero.Libraries.userLibraryID, 340 librariesToSkip.indexOf("L" + Zotero.Libraries.userLibraryID) == -1); 341 342 // Sort groups 343 var collation = Zotero.getLocaleCollation(); 344 groups.sort((a, b) => collation.compareString(1, a.data.name, b.data.name)); 345 // Add group rows 346 for (let group of groups) { 347 addRow(group.data.name, "G" + group.id, librariesToSkip.indexOf("G" + group.id) == -1); 348 } 349 }), 350 351 352 updateStorageSettingsUI: Zotero.Promise.coroutine(function* () { 353 this.unverifyStorageServer(); 354 355 var protocol = document.getElementById('pref-storage-protocol').value; 356 var enabled = document.getElementById('pref-storage-enabled').value; 357 358 var storageSettings = document.getElementById('storage-settings'); 359 var protocolMenu = document.getElementById('storage-protocol'); 360 var settings = document.getElementById('storage-webdav-settings'); 361 var sep = document.getElementById('storage-separator'); 362 363 if (!enabled || protocol == 'zotero') { 364 settings.hidden = true; 365 sep.hidden = false; 366 } 367 else { 368 settings.hidden = false; 369 sep.hidden = true; 370 } 371 372 document.getElementById('storage-user-download-mode').disabled = !enabled; 373 this.updateStorageTerms(); 374 375 window.sizeToContent(); 376 }), 377 378 379 updateStorageSettingsGroupsUI: function () { 380 setTimeout(() => { 381 var enabled = document.getElementById('pref-storage-groups-enabled').value; 382 document.getElementById('storage-groups-download-mode').disabled = !enabled; 383 this.updateStorageTerms(); 384 }); 385 }, 386 387 388 updateStorageTerms: function () { 389 var terms = document.getElementById('storage-terms'); 390 391 var libraryEnabled = document.getElementById('pref-storage-enabled').value; 392 var storageProtocol = document.getElementById('pref-storage-protocol').value; 393 var groupsEnabled = document.getElementById('pref-storage-groups-enabled').value; 394 395 terms.hidden = !((libraryEnabled && storageProtocol == 'zotero') || groupsEnabled); 396 }, 397 398 399 onStorageSettingsKeyPress: Zotero.Promise.coroutine(function* (event) { 400 if (event.keyCode == 13) { 401 yield this.verifyStorageServer(); 402 } 403 }), 404 405 406 onStorageSettingsChange: Zotero.Promise.coroutine(function* () { 407 // Clean URL 408 var urlPref = document.getElementById('pref-storage-url'); 409 urlPref.value = urlPref.value.replace(/(^https?:\/\/|\/zotero\/?$|\/$)/g, ''); 410 411 var oldProtocol = document.getElementById('pref-storage-protocol').value; 412 var oldEnabled = document.getElementById('pref-storage-enabled').value; 413 414 yield Zotero.Promise.delay(1); 415 416 var newProtocol = document.getElementById('pref-storage-protocol').value; 417 var newEnabled = document.getElementById('pref-storage-enabled').value; 418 419 if (oldProtocol != newProtocol) { 420 yield Zotero.Sync.Storage.Local.resetAllSyncStates(Zotero.Libraries.userLibraryID); 421 } 422 423 if (oldProtocol == 'webdav') { 424 this.unverifyStorageServer(); 425 Zotero.Sync.Runner.resetStorageController(oldProtocol); 426 427 var username = document.getElementById('storage-username').value; 428 var password = document.getElementById('storage-password').value; 429 if (username) { 430 Zotero.Sync.Runner.getStorageController('webdav').password = password; 431 } 432 } 433 434 if (oldProtocol == 'zotero' && newProtocol == 'webdav') { 435 var sql = "SELECT COUNT(*) FROM settings " 436 + "WHERE setting='storage' AND key='zfsPurge' AND value='user'"; 437 if (!Zotero.DB.valueQueryAsync(sql)) { 438 var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] 439 .getService(Components.interfaces.nsIPromptService); 440 var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING) 441 + (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_IS_STRING) 442 + ps.BUTTON_DELAY_ENABLE; 443 var account = Zotero.Sync.Server.username; 444 var index = ps.confirmEx( 445 null, 446 Zotero.getString('zotero.preferences.sync.purgeStorage.title'), 447 Zotero.getString('zotero.preferences.sync.purgeStorage.desc'), 448 buttonFlags, 449 Zotero.getString('zotero.preferences.sync.purgeStorage.confirmButton'), 450 Zotero.getString('zotero.preferences.sync.purgeStorage.cancelButton'), null, null, {} 451 ); 452 453 if (index == 0) { 454 var sql = "INSERT OR IGNORE INTO settings VALUES (?,?,?)"; 455 yield Zotero.DB.queryAsync(sql, ['storage', 'zfsPurge', 'user']); 456 457 try { 458 yield Zotero.Sync.Storage.ZFS.purgeDeletedStorageFiles(); 459 ps.alert( 460 null, 461 Zotero.getString("general.success"), 462 "Attachment files from your personal library have been removed from the Zotero servers." 463 ); 464 } 465 catch (e) { 466 Zotero.logError(e); 467 ps.alert( 468 null, 469 Zotero.getString("general.error"), 470 "An error occurred. Please try again later." 471 ); 472 } 473 } 474 } 475 } 476 477 this.updateStorageSettingsUI(); 478 }), 479 480 481 verifyStorageServer: Zotero.Promise.coroutine(function* () { 482 // onchange weirdly isn't triggered when clicking straight from a field to the button, 483 // so we have to trigger this here (and we don't trigger it for Enter in 484 // onStorageSettingsKeyPress()). 485 yield this.onStorageSettingsChange(); 486 487 Zotero.debug("Verifying storage"); 488 489 var verifyButton = document.getElementById("storage-verify"); 490 var abortButton = document.getElementById("storage-abort"); 491 var progressMeter = document.getElementById("storage-progress"); 492 var urlField = document.getElementById("storage-url"); 493 var usernameField = document.getElementById("storage-username"); 494 var passwordField = document.getElementById("storage-password"); 495 496 verifyButton.hidden = true; 497 abortButton.hidden = false; 498 progressMeter.hidden = false; 499 500 var success = false; 501 var request = null; 502 503 var controller = Zotero.Sync.Runner.getStorageController('webdav'); 504 505 try { 506 yield controller.checkServer({ 507 // Get the XMLHttpRequest for possible cancelling 508 onRequest: r => request = r 509 }) 510 511 success = true; 512 } 513 catch (e) { 514 if (e instanceof controller.VerificationError) { 515 switch (e.error) { 516 case "NO_URL": 517 urlField.focus(); 518 break; 519 520 case "NO_USERNAME": 521 usernameField.focus(); 522 break; 523 524 case "NO_PASSWORD": 525 case "AUTH_FAILED": 526 passwordField.focus(); 527 break; 528 } 529 } 530 success = yield controller.handleVerificationError(e); 531 } 532 finally { 533 verifyButton.hidden = false; 534 abortButton.hidden = true; 535 progressMeter.hidden = true; 536 } 537 538 if (success) { 539 Zotero.debug("WebDAV verification succeeded"); 540 541 Zotero.alert( 542 window, 543 Zotero.getString('sync.storage.serverConfigurationVerified'), 544 Zotero.getString('sync.storage.fileSyncSetUp') 545 ); 546 } 547 else { 548 Zotero.logError("WebDAV verification failed"); 549 } 550 551 abortButton.onclick = function () { 552 if (request) { 553 Zotero.debug("Cancelling verification request"); 554 request.onreadystatechange = undefined; 555 request.abort(); 556 verifyButton.hidden = false; 557 abortButton.hidden = true; 558 progressMeter.hidden = true; 559 } 560 } 561 }), 562 563 564 unverifyStorageServer: function () { 565 Zotero.debug("Unverifying storage"); 566 Zotero.Prefs.set('sync.storage.verified', false); 567 }, 568 569 570 // 571 // Reset pane 572 // 573 initResetPane: function () { 574 // 575 // Build library selector 576 // 577 var libraryMenu = document.getElementById('sync-reset-library-menu'); 578 // Some options need to be disabled when certain libraries are selected 579 libraryMenu.onchange = (event) => { 580 this.onResetLibraryChange(parseInt(event.target.value)); 581 } 582 this.onResetLibraryChange(Zotero.Libraries.userLibraryID); 583 var libraries = Zotero.Libraries.getAll() 584 .filter(x => x.libraryType == 'user' || x.libraryType == 'group'); 585 Zotero.Utilities.Internal.buildLibraryMenuHTML(libraryMenu, libraries); 586 // Disable read-only libraries, at least until there are options that make sense for those 587 Array.from(libraryMenu.querySelectorAll('option')) 588 .filter(x => x.getAttribute('data-editable') == 'false') 589 .forEach(x => x.disabled = true); 590 591 var list = document.getElementById('sync-reset-list'); 592 for (let li of document.querySelectorAll('#sync-reset-list li')) { 593 li.addEventListener('click', function (event) { 594 // Ignore clicks if disabled 595 if (this.hasAttribute('disabled')) { 596 event.stopPropagation(); 597 return; 598 } 599 document.getElementById('sync-reset-button').disabled = false; 600 }); 601 } 602 }, 603 604 605 onResetLibraryChange: function (libraryID) { 606 var library = Zotero.Libraries.get(libraryID); 607 var section = document.getElementById('reset-file-sync-history'); 608 var input = section.querySelector('input'); 609 if (library.filesEditable) { 610 section.removeAttribute('disabled'); 611 input.disabled = false; 612 } 613 else { 614 section.setAttribute('disabled', ''); 615 // If radio we're disabling is already selected, select the first one in the list 616 // instead 617 if (input.checked) { 618 document.querySelector('#sync-reset-list li:first-child input').checked = true; 619 } 620 input.disabled = true; 621 } 622 }, 623 624 625 reset: async function () { 626 var ps = Services.prompt; 627 628 if (Zotero.Sync.Runner.syncInProgress) { 629 Zotero.alert( 630 null, 631 Zotero.getString('general.error'), 632 Zotero.getString('sync.error.syncInProgress') 633 + "\n\n" 634 + Zotero.getString('general.operationInProgress.waitUntilFinishedAndTryAgain') 635 ); 636 return; 637 } 638 639 var libraryID = parseInt( 640 Array.from(document.querySelectorAll('#sync-reset-library-menu option')) 641 .filter(x => x.selected)[0] 642 .value 643 ); 644 var library = Zotero.Libraries.get(libraryID); 645 var action = Array.from(document.querySelectorAll('#sync-reset-list input[name=sync-reset-radiogroup]')) 646 .filter(x => x.checked)[0] 647 .getAttribute('value'); 648 649 switch (action) { 650 /*case 'full-sync': 651 var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING) 652 + (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL) 653 + ps.BUTTON_POS_1_DEFAULT; 654 var index = ps.confirmEx( 655 null, 656 Zotero.getString('general.warning'), 657 // TODO: localize 658 "On the next sync, Zotero will compare all local and remote data and merge any " 659 + "data that does not exist in both locations.\n\n" 660 + "This option is not necessary during normal usage and should " 661 + "generally be used only to troubleshoot specific issues as recommended " 662 + "by Zotero support staff.", 663 buttonFlags, 664 Zotero.getString('general.reset'), 665 null, null, null, {} 666 ); 667 668 switch (index) { 669 case 0: 670 let libraries = Zotero.Libraries.getAll().filter(library => library.syncable); 671 await Zotero.DB.executeTransaction(function* () { 672 for (let library of libraries) { 673 library.libraryVersion = -1; 674 yield library.save(); 675 } 676 }); 677 break; 678 679 // Cancel 680 case 1: 681 return; 682 } 683 684 break; 685 686 case 'restore-from-server': 687 var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING) 688 + (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL) 689 + ps.BUTTON_POS_1_DEFAULT; 690 var index = ps.confirmEx( 691 null, 692 Zotero.getString('general.warning'), 693 Zotero.getString('zotero.preferences.sync.reset.restoreFromServer', account), 694 buttonFlags, 695 Zotero.getString('zotero.preferences.sync.reset.replaceLocalData'), 696 null, null, null, {} 697 ); 698 699 switch (index) { 700 case 0: 701 // TODO: better error handling 702 703 // Verify username and password 704 var callback = async function () { 705 Zotero.Schema.stopRepositoryTimer(); 706 Zotero.Sync.Runner.clearSyncTimeout(); 707 708 Zotero.DB.skipBackup = true; 709 710 await Zotero.File.putContentsAsync( 711 OS.Path.join(Zotero.DataDirectory.dir, 'restore-from-server'), 712 '' 713 ); 714 715 var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING); 716 var index = ps.confirmEx( 717 null, 718 Zotero.getString('general.restartRequired'), 719 Zotero.getString('zotero.preferences.sync.reset.restartToComplete'), 720 buttonFlags, 721 Zotero.getString('general.restartNow'), 722 null, null, null, {} 723 ); 724 725 var appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"] 726 .getService(Components.interfaces.nsIAppStartup); 727 appStartup.quit(Components.interfaces.nsIAppStartup.eRestart | Components.interfaces.nsIAppStartup.eAttemptQuit); 728 }; 729 730 // TODO: better way of checking for an active session? 731 if (Zotero.Sync.Server.sessionIDComponent == 'sessionid=') { 732 Zotero.Sync.Server.login() 733 .then(callback) 734 .done(); 735 } 736 else { 737 callback(); 738 } 739 break; 740 741 // Cancel 742 case 1: 743 return; 744 } 745 break;*/ 746 747 case 'restore-to-server': 748 var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING) 749 + (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL) 750 + ps.BUTTON_POS_1_DEFAULT; 751 var index = ps.confirmEx( 752 null, 753 Zotero.getString('general.warning'), 754 Zotero.getString( 755 'zotero.preferences.sync.reset.restoreToServer', 756 [Zotero.clientName, library.name, ZOTERO_CONFIG.DOMAIN_NAME] 757 ), 758 buttonFlags, 759 Zotero.getString('zotero.preferences.sync.reset.restoreToServer.button'), 760 null, null, null, {} 761 ); 762 763 switch (index) { 764 case 0: 765 var resetButton = document.getElementById('sync-reset-button'); 766 resetButton.disabled = true; 767 try { 768 await Zotero.Sync.Runner.sync({ 769 libraries: [libraryID], 770 resetMode: Zotero.Sync.Runner.RESET_MODE_TO_SERVER 771 }); 772 } 773 finally { 774 resetButton.disabled = false; 775 } 776 break; 777 778 // Cancel 779 case 1: 780 return; 781 } 782 783 break; 784 785 786 case 'reset-file-sync-history': 787 var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING 788 + ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL 789 + ps.BUTTON_POS_1_DEFAULT; 790 var index = ps.confirmEx( 791 null, 792 Zotero.getString('general.warning'), 793 Zotero.getString( 794 'zotero.preferences.sync.reset.fileSyncHistory', 795 [Zotero.clientName, library.name] 796 ), 797 buttonFlags, 798 Zotero.getString('general.reset'), 799 null, null, null, {} 800 ); 801 802 switch (index) { 803 case 0: 804 await Zotero.Sync.Storage.Local.resetAllSyncStates(libraryID); 805 ps.alert( 806 null, 807 Zotero.getString('general.success'), 808 Zotero.getString( 809 'zotero.preferences.sync.reset.fileSyncHistory.cleared', 810 library.name 811 ) 812 ); 813 break; 814 815 // Cancel 816 case 1: 817 return; 818 } 819 820 break; 821 822 default: 823 throw new Error(`Invalid action '${action}' in handleSyncReset()`); 824 } 825 } 826 };