zotero/chrome/content/zotero/preferences/preferences_sync.jsx

880 lines
26 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
***** BEGIN LICENSE BLOCK *****
Copyright © 20082013 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://zotero.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
*/
"use strict";
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/osfile.jsm");
Components.utils.import("resource://zotero/config.js");
var React = require('react');
var ReactDOM = require('react-dom');
var VirtualizedTable = require('components/virtualized-table');
var { getDOMElement } = require('components/icons');
var { IntlProvider } = require('react-intl');
var { renderCell } = VirtualizedTable;
Zotero_Preferences.Sync = {
checkmarkChar: '\u2705',
noChar: '\uD83D\uDEAB',
init: Zotero.Promise.coroutine(function* () {
this.updateStorageSettingsUI();
this.updateStorageSettingsGroupsUI();
var username = Zotero.Users.getCurrentUsername() || Zotero.Prefs.get('sync.server.username') || " ";
var apiKey = yield Zotero.Sync.Data.Local.getAPIKey();
this.displayFields(apiKey ? username : "");
var pass = Zotero.Sync.Runner.getStorageController('webdav').password;
if (pass) {
document.getElementById('storage-password').value = pass;
}
if (apiKey) {
try {
var keyInfo = yield Zotero.Sync.Runner.checkAccess(
Zotero.Sync.Runner.getAPIClient({apiKey}),
{timeout: 5000}
);
this.displayFields(keyInfo.username);
}
catch (e) {
// API key wrong/invalid
if (e instanceof Zotero.Error && e.error == Zotero.Error.ERROR_API_KEY_INVALID) {
Zotero.alert(
window,
Zotero.getString('general.error'),
Zotero.getString('sync.error.apiKeyInvalid', Zotero.clientName)
);
this.unlinkAccount(false);
}
else {
throw e;
}
}
}
this.initResetPane();
}),
displayFields: function (username) {
document.getElementById('sync-unauthorized').hidden = !!username;
document.getElementById('sync-authorized').hidden = !username;
document.getElementById('sync-reset-tab').disabled = !username;
document.getElementById('sync-username').value = username;
document.getElementById('sync-password').value = '';
document.getElementById('sync-username-textbox').value = Zotero.Prefs.get('sync.server.username');
var img = document.getElementById('sync-status-indicator');
img.removeAttribute('verified');
img.removeAttribute('animated');
window.sizeToContent();
},
credentialsChange: function (event) {
var username = document.getElementById('sync-username-textbox');
var password = document.getElementById('sync-password');
var syncAuthButton = document.getElementById('sync-auth-button');
syncAuthButton.setAttribute('disabled', 'true');
// When using backspace, the value is not updated until after the keypress event
setTimeout(function() {
if (username.value.length && password.value.length) {
syncAuthButton.setAttribute('disabled', 'false');
}
});
},
credentialsKeyPress: function (event) {
if (event.keyCode == 13) {
this.linkAccount(event);
event.preventDefault();
}
},
trimUsername: function () {
var tb = document.getElementById('sync-username-textbox');
var username = tb.value;
var trimmed = username.trim();
if (username != trimmed) {
tb.value = trimmed;
// Setting .value alone doesn't seem to cause the pref to sync, so set it manually
Zotero.Prefs.set('sync.server.username', trimmed);
}
},
linkAccount: Zotero.Promise.coroutine(function* (event) {
this.trimUsername();
var username = document.getElementById('sync-username-textbox').value;
var password = document.getElementById('sync-password').value;
if (!username.length || !password.length) {
this.updateSyncIndicator();
return;
}
// Try to acquire API key with current credentials
this.updateSyncIndicator('animated');
try {
var json = yield Zotero.Sync.Runner.createAPIKeyFromCredentials(username, password);
}
catch (e) {
setTimeout(function () {
Zotero.alert(
window,
Zotero.getString('general.error'),
e.message
);
});
throw e;
}
finally {
this.updateSyncIndicator();
}
// Invalid credentials
if (!json) {
Zotero.alert(window,
Zotero.getString('general.error'),
Zotero.getString('sync.error.invalidLogin')
);
return;
}
var ok = yield Zotero.Sync.Data.Local.checkUser(
window,
json.userID,
json.username,
json.displayName
);
if (!ok) {
// createAPIKeyFromCredentials will have created an API key,
// but user decided not to use it, so we remove it here.
Zotero.Sync.Runner.deleteAPIKey();
return;
}
this.displayFields(json.username);
}),
/**
* Updates the auth indicator icon, depending on status
* @param {string} status
*/
updateSyncIndicator: function (status) {
var img = document.getElementById('sync-status-indicator');
img.removeAttribute('animated');
if (status == 'animated') {
img.setAttribute('animated', true);
}
},
unlinkAccount: Zotero.Promise.coroutine(function* (showAlert=true) {
if (showAlert) {
var check = {value: false};
var ps = Services.prompt;
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING) +
(ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
var index = ps.confirmEx(
null,
Zotero.getString('general.warning'),
Zotero.getString('account.unlinkWarning', Zotero.clientName),
buttonFlags,
Zotero.getString('account.unlinkWarning.button'), null, null,
Zotero.getString('account.unlinkWarning.removeData', Zotero.clientName),
check
);
if (index == 0) {
if (check.value) {
var resetDataDirFile = OS.Path.join(Zotero.DataDirectory.dir, 'reset-data-directory');
yield Zotero.File.putContentsAsync(resetDataDirFile, '');
yield Zotero.Sync.Runner.deleteAPIKey();
Zotero.Prefs.clear('sync.server.username');
return Zotero.Utilities.Internal.quitZotero(true);
}
} else {
return;
}
}
this.displayFields();
Zotero.Prefs.clear('sync.librariesToSync');
yield Zotero.Sync.Runner.deleteAPIKey();
}),
showLibrariesToSyncDialog: function() {
var io = {};
window.openDialog('chrome://zotero/content/preferences/librariesToSync.xul',
"zotero-preferences-librariesToSyncDialog", "chrome,modal,centerscreen", io);
},
toggleLibraryToSync: function (index) {
if (typeof index != "number") {
index = this._tree.selection.focused;
}
if (index == -1 || !this._rows[index].editable) return;
const row = this._rows[index];
this._rows[index].checked = !this._rows[index].checked;
this._tree.invalidateRow(index);
var librariesToSkip = JSON.parse(Zotero.Prefs.get('sync.librariesToSkip') || '[]');
var indexOfId = librariesToSkip.indexOf(row.id);
if (indexOfId == -1) {
librariesToSkip.push(row.id);
}
else {
librariesToSkip.splice(indexOfId, 1);
}
Zotero.Prefs.set('sync.librariesToSkip', JSON.stringify(librariesToSkip));
},
initLibrariesToSync: async function () {
const columns = [
{
dataKey: "checked",
label: "zotero.preferences.sync.librariesToSync.sync",
fixedWidth: true,
// TODO: Specify in ems?
width: '45'
},
{
dataKey: "name",
label: "zotero.preferences.sync.librariesToSync.library"
}
];
this._rows = [];
let renderItem = (index, selection, oldDiv=null, columns) => {
const row = this._rows[index];
let div;
if (oldDiv) {
div = oldDiv;
div.innerHTML = "";
}
else {
div = document.createElementNS("http://www.w3.org/1999/xhtml", 'div');
div.className = "row";
div.addEventListener('dblclick', () => {
this.toggleLibraryToSync(index);
});
}
div.classList.toggle('selected', selection.isSelected(index));
for (let column of columns) {
if (column.dataKey === 'checked') {
let span = document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
span.className = `cell ${column.className}`;
if (row.id != 'loading') {
span.innerText = row.checked ? this.checkmarkChar : this.noChar;
span.style.textAlign = 'center';
}
span.addEventListener('mousedown', () => {
this.toggleLibraryToSync(index);
});
span.style.pointerEvents = 'initial';
div.appendChild(span);
}
else {
div.appendChild(renderCell(index, row[column.dataKey], column));
}
}
return div;
}
let handleKeyDown = (e) => {
if (e.key == ' ') {
this.toggleLibraryToSync();
return false;
}
};
let elem = (
<IntlProvider locale={Zotero.locale} messages={Zotero.Intl.strings}>
<VirtualizedTable
getRowCount={() => this._rows.length}
id="librariesToSync-table"
ref={ref => this._tree = ref}
renderItem={renderItem}
showHeader={true}
columns={columns}
staticColumns={true}
getRowString={index => this._rows[index].name}
disableFontSizeScaling={true}
onKeyDown={handleKeyDown}
/>
</IntlProvider>
);
ReactDOM.render(elem, document.getElementById("libraries-to-sync-tree"));
var addRow = function (libraryName, id, checked=false, editable=true) {
this._rows.push({
name: libraryName,
id,
checked,
editable
});
this._tree.invalidate();
}.bind(this);
// Add loading row while we're loading a group list
var loadingLabel = Zotero.getString("zotero.preferences.sync.librariesToSync.loadingLibraries");
addRow(loadingLabel, "loading", false, false);
var apiKey = await Zotero.Sync.Data.Local.getAPIKey();
var client = Zotero.Sync.Runner.getAPIClient({ apiKey });
var groups = [];
try {
// Load up remote groups
var keyInfo = await Zotero.Sync.Runner.checkAccess(client, {timeout: 5000});
groups = await client.getGroups(keyInfo.userID);
}
catch (e) {
// Connection problems
if ((e instanceof Zotero.HTTP.UnexpectedStatusException)
|| (e instanceof Zotero.HTTP.TimeoutException)
|| (e instanceof Zotero.HTTP.BrowserOfflineException)) {
Zotero.alert(
window,
Zotero.getString('general.error'),
Zotero.getString('sync.error.checkConnection', Zotero.clientName)
);
}
else {
throw e;
}
document.getElementsByTagName('dialog')[0].acceptDialog();
}
// Remove the loading row
this._rows = [];
this._tree.invalidate();
var librariesToSkip = JSON.parse(Zotero.Prefs.get('sync.librariesToSkip') || '[]');
// Add default rows
addRow(Zotero.getString("pane.collections.libraryAndFeeds"), "L" + Zotero.Libraries.userLibraryID,
librariesToSkip.indexOf("L" + Zotero.Libraries.userLibraryID) == -1);
// Sort groups
var collation = Zotero.getLocaleCollation();
groups.sort((a, b) => collation.compareString(1, a.data.name, b.data.name));
// Add group rows
for (let group of groups) {
addRow(group.data.name, "G" + group.id, librariesToSkip.indexOf("G" + group.id) == -1);
}
},
updateStorageSettingsUI: Zotero.Promise.coroutine(function* () {
this.unverifyStorageServer();
var protocol = document.getElementById('pref-storage-protocol').value;
var enabled = document.getElementById('pref-storage-enabled').value;
var storageSettings = document.getElementById('storage-settings');
var protocolMenu = document.getElementById('storage-protocol');
var settings = document.getElementById('storage-webdav-settings');
var sep = document.getElementById('storage-separator');
if (!enabled || protocol == 'zotero') {
settings.hidden = true;
sep.hidden = false;
}
else {
settings.hidden = false;
sep.hidden = true;
}
document.getElementById('storage-user-download-mode').disabled = !enabled;
this.updateStorageTerms();
window.sizeToContent();
}),
updateStorageSettingsGroupsUI: function () {
setTimeout(() => {
var enabled = document.getElementById('pref-storage-groups-enabled').value;
document.getElementById('storage-groups-download-mode').disabled = !enabled;
this.updateStorageTerms();
});
},
updateStorageTerms: function () {
var terms = document.getElementById('storage-terms');
var libraryEnabled = document.getElementById('pref-storage-enabled').value;
var storageProtocol = document.getElementById('pref-storage-protocol').value;
var groupsEnabled = document.getElementById('pref-storage-groups-enabled').value;
terms.hidden = !((libraryEnabled && storageProtocol == 'zotero') || groupsEnabled);
},
onStorageSettingsKeyPress: Zotero.Promise.coroutine(function* (event) {
if (event.keyCode == 13) {
yield this.verifyStorageServer();
}
}),
onStorageSettingsChange: Zotero.Promise.coroutine(function* () {
// Clean URL
var urlPref = document.getElementById('pref-storage-url');
urlPref.value = urlPref.value.replace(/(^https?:\/\/|\/zotero\/?$|\/$)/g, '');
var oldProtocol = document.getElementById('pref-storage-protocol').value;
var oldEnabled = document.getElementById('pref-storage-enabled').value;
yield Zotero.Promise.delay(1);
var newProtocol = document.getElementById('pref-storage-protocol').value;
var newEnabled = document.getElementById('pref-storage-enabled').value;
if (oldProtocol != newProtocol) {
yield Zotero.Sync.Storage.Local.resetAllSyncStates(Zotero.Libraries.userLibraryID);
}
if (oldProtocol == 'webdav') {
this.unverifyStorageServer();
// The controller is getting replaced anyway, but this removes the WebDAV URL from
// Zotero.HTTP.CookieBlocker
Zotero.Sync.Runner.getStorageController('webdav').clearCachedCredentials();
Zotero.Sync.Runner.resetStorageController(oldProtocol);
var username = document.getElementById('storage-username').value;
var password = document.getElementById('storage-password').value;
if (username) {
// Get a new controller
Zotero.Sync.Runner.getStorageController('webdav').password = password;
}
}
if (oldProtocol == 'zotero' && newProtocol == 'webdav') {
var sql = "SELECT COUNT(*) FROM settings "
+ "WHERE setting='storage' AND key='zfsPurge' AND value='user'";
if (!Zotero.DB.valueQueryAsync(sql)) {
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_IS_STRING)
+ ps.BUTTON_DELAY_ENABLE;
var account = Zotero.Sync.Server.username;
var index = ps.confirmEx(
null,
Zotero.getString('zotero.preferences.sync.purgeStorage.title'),
Zotero.getString('zotero.preferences.sync.purgeStorage.desc'),
buttonFlags,
Zotero.getString('zotero.preferences.sync.purgeStorage.confirmButton'),
Zotero.getString('zotero.preferences.sync.purgeStorage.cancelButton'), null, null, {}
);
if (index == 0) {
var sql = "INSERT OR IGNORE INTO settings VALUES (?,?,?)";
yield Zotero.DB.queryAsync(sql, ['storage', 'zfsPurge', 'user']);
try {
yield Zotero.Sync.Storage.ZFS.purgeDeletedStorageFiles();
ps.alert(
null,
Zotero.getString("general.success"),
"Attachment files from your personal library have been removed from the Zotero servers."
);
}
catch (e) {
Zotero.logError(e);
ps.alert(
null,
Zotero.getString("general.error"),
"An error occurred. Please try again later."
);
}
}
}
}
this.updateStorageSettingsUI();
}),
verifyStorageServer: Zotero.Promise.coroutine(function* () {
// onchange weirdly isn't triggered when clicking straight from a field to the button,
// so we have to trigger this here (and we don't trigger it for Enter in
// onStorageSettingsKeyPress()).
yield this.onStorageSettingsChange();
Zotero.debug("Verifying storage");
var verifyButton = document.getElementById("storage-verify");
var abortButton = document.getElementById("storage-abort");
var progressMeter = document.getElementById("storage-progress");
var urlField = document.getElementById("storage-url");
var usernameField = document.getElementById("storage-username");
var passwordField = document.getElementById("storage-password");
// These don't get set until window close on Windows/Linux (no instantApply),
// so set them explicitly when verifying
Zotero.Prefs.set('sync.storage.url', urlField.value);
Zotero.Prefs.set('sync.storage.username', usernameField.value);
verifyButton.hidden = true;
abortButton.hidden = false;
progressMeter.hidden = false;
var success = false;
var request = null;
var controller = Zotero.Sync.Runner.getStorageController('webdav');
try {
yield controller.checkServer({
// Get the XMLHttpRequest for possible cancelling
onRequest: r => request = r
})
success = true;
}
catch (e) {
if (e instanceof controller.VerificationError) {
switch (e.error) {
case "NO_URL":
urlField.focus();
break;
case "NO_USERNAME":
usernameField.focus();
break;
case "NO_PASSWORD":
case "AUTH_FAILED":
passwordField.focus();
break;
}
}
success = yield controller.handleVerificationError(e);
}
finally {
verifyButton.hidden = false;
abortButton.hidden = true;
progressMeter.hidden = true;
}
if (success) {
Zotero.debug("WebDAV verification succeeded");
Zotero.alert(
window,
Zotero.getString('sync.storage.serverConfigurationVerified'),
Zotero.getString('sync.storage.fileSyncSetUp')
);
}
else {
Zotero.logError("WebDAV verification failed");
}
abortButton.onclick = function () {
if (request) {
Zotero.debug("Cancelling verification request");
request.onreadystatechange = undefined;
request.abort();
verifyButton.hidden = false;
abortButton.hidden = true;
progressMeter.hidden = true;
}
}
}),
unverifyStorageServer: function () {
Zotero.debug("Unverifying storage");
Zotero.Prefs.set('sync.storage.verified', false);
},
//
// Reset pane
//
initResetPane: function () {
//
// Build library selector
//
var libraryMenu = document.getElementById('sync-reset-library-menu');
// Some options need to be disabled when certain libraries are selected
libraryMenu.onchange = (event) => {
this.onResetLibraryChange(parseInt(event.target.value));
}
this.onResetLibraryChange(Zotero.Libraries.userLibraryID);
var libraries = Zotero.Libraries.getAll()
.filter(x => x.libraryType == 'user' || x.libraryType == 'group');
Zotero.Utilities.Internal.buildLibraryMenuHTML(libraryMenu, libraries);
// Disable read-only libraries, at least until there are options that make sense for those
Array.from(libraryMenu.querySelectorAll('option'))
.filter(x => x.getAttribute('data-editable') == 'false')
.forEach(x => x.disabled = true);
var list = document.getElementById('sync-reset-list');
for (let li of document.querySelectorAll('#sync-reset-list li')) {
li.addEventListener('click', function (event) {
// Ignore clicks if disabled
if (this.hasAttribute('disabled')) {
event.stopPropagation();
return;
}
document.getElementById('sync-reset-button').disabled = false;
});
}
},
onResetLibraryChange: function (libraryID) {
var library = Zotero.Libraries.get(libraryID);
var section = document.getElementById('reset-file-sync-history');
var input = section.querySelector('input');
if (library.filesEditable) {
section.removeAttribute('disabled');
input.disabled = false;
}
else {
section.setAttribute('disabled', '');
// If radio we're disabling is already selected, select the first one in the list
// instead
if (input.checked) {
document.querySelector('#sync-reset-list li:first-child input').checked = true;
}
input.disabled = true;
}
},
reset: async function () {
var ps = Services.prompt;
if (Zotero.Sync.Runner.syncInProgress) {
Zotero.alert(
null,
Zotero.getString('general.error'),
Zotero.getString('sync.error.syncInProgress')
+ "\n\n"
+ Zotero.getString('general.operationInProgress.waitUntilFinishedAndTryAgain')
);
return;
}
var libraryID = parseInt(
Array.from(document.querySelectorAll('#sync-reset-library-menu option'))
.filter(x => x.selected)[0]
.value
);
var library = Zotero.Libraries.get(libraryID);
var action = Array.from(document.querySelectorAll('#sync-reset-list input[name=sync-reset-radiogroup]'))
.filter(x => x.checked)[0]
.getAttribute('value');
switch (action) {
/*case 'full-sync':
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL)
+ ps.BUTTON_POS_1_DEFAULT;
var index = ps.confirmEx(
null,
Zotero.getString('general.warning'),
// TODO: localize
"On the next sync, Zotero will compare all local and remote data and merge any "
+ "data that does not exist in both locations.\n\n"
+ "This option is not necessary during normal usage and should "
+ "generally be used only to troubleshoot specific issues as recommended "
+ "by Zotero support staff.",
buttonFlags,
Zotero.getString('general.reset'),
null, null, null, {}
);
switch (index) {
case 0:
let libraries = Zotero.Libraries.getAll().filter(library => library.syncable);
await Zotero.DB.executeTransaction(async function () {
for (let library of libraries) {
library.libraryVersion = -1;
await library.save();
}
});
break;
// Cancel
case 1:
return;
}
break;
case 'restore-from-server':
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL)
+ ps.BUTTON_POS_1_DEFAULT;
var index = ps.confirmEx(
null,
Zotero.getString('general.warning'),
Zotero.getString('zotero.preferences.sync.reset.restoreFromServer', account),
buttonFlags,
Zotero.getString('zotero.preferences.sync.reset.replaceLocalData'),
null, null, null, {}
);
switch (index) {
case 0:
// TODO: better error handling
// Verify username and password
var callback = async function () {
Zotero.Schema.stopRepositoryTimer();
Zotero.Sync.Runner.clearSyncTimeout();
Zotero.DB.skipBackup = true;
await Zotero.File.putContentsAsync(
OS.Path.join(Zotero.DataDirectory.dir, 'restore-from-server'),
''
);
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING);
var index = ps.confirmEx(
null,
Zotero.getString('general.restartRequired'),
Zotero.getString('zotero.preferences.sync.reset.restartToComplete'),
buttonFlags,
Zotero.getString('general.restartNow'),
null, null, null, {}
);
var appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"]
.getService(Components.interfaces.nsIAppStartup);
appStartup.quit(Components.interfaces.nsIAppStartup.eRestart | Components.interfaces.nsIAppStartup.eAttemptQuit);
};
// TODO: better way of checking for an active session?
if (Zotero.Sync.Server.sessionIDComponent == 'sessionid=') {
Zotero.Sync.Server.login()
.then(callback)
.done();
}
else {
callback();
}
break;
// Cancel
case 1:
return;
}
break;*/
case 'restore-to-server':
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL)
+ ps.BUTTON_POS_1_DEFAULT;
var index = ps.confirmEx(
null,
Zotero.getString('general.warning'),
Zotero.getString(
'zotero.preferences.sync.reset.restoreToServer',
[Zotero.clientName, library.name, ZOTERO_CONFIG.DOMAIN_NAME]
),
buttonFlags,
Zotero.getString('zotero.preferences.sync.reset.restoreToServer.button'),
null, null, null, {}
);
switch (index) {
case 0:
var resetButton = document.getElementById('sync-reset-button');
resetButton.disabled = true;
try {
await Zotero.Sync.Runner.sync({
libraries: [libraryID],
resetMode: Zotero.Sync.Runner.RESET_MODE_TO_SERVER
});
}
finally {
resetButton.disabled = false;
}
break;
// Cancel
case 1:
return;
}
break;
case 'reset-file-sync-history':
var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL
+ ps.BUTTON_POS_1_DEFAULT;
var index = ps.confirmEx(
null,
Zotero.getString('general.warning'),
Zotero.getString(
'zotero.preferences.sync.reset.fileSyncHistory',
[Zotero.clientName, library.name]
),
buttonFlags,
Zotero.getString('general.reset'),
null, null, null, {}
);
switch (index) {
case 0:
await Zotero.Sync.Storage.Local.resetAllSyncStates(libraryID);
ps.alert(
null,
Zotero.getString('general.success'),
Zotero.getString(
'zotero.preferences.sync.reset.fileSyncHistory.cleared',
library.name
)
);
break;
// Cancel
case 1:
return;
}
break;
default:
throw new Error(`Invalid action '${action}' in handleSyncReset()`);
}
}
};