Show hard warning dialog when unlinking or linking to a different account (#1047)
And give option to delete local data when unlinking This removes the old behavior of merging accounts when syncing with a different username.
This commit is contained in:
parent
455facee95
commit
11e7cef057
12 changed files with 353 additions and 122 deletions
78
chrome/content/zotero/hardConfirmationDialog.js
Normal file
78
chrome/content/zotero/hardConfirmationDialog.js
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright © 2016 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 *****
|
||||||
|
*/
|
||||||
|
|
||||||
|
Zotero.HardConfirmationDialog = {
|
||||||
|
init: function() {
|
||||||
|
var label, content;
|
||||||
|
this.io = window.arguments[0];
|
||||||
|
|
||||||
|
var vbox = document.getElementById('infoContainer');
|
||||||
|
var sep = vbox.firstChild;
|
||||||
|
for (let text of this.io.text) {
|
||||||
|
label = document.createElement('label');
|
||||||
|
content = document.createTextNode(text);
|
||||||
|
label.appendChild(content);
|
||||||
|
vbox.insertBefore(label, sep);
|
||||||
|
}
|
||||||
|
if (this.io.checkboxLabel) {
|
||||||
|
var checkbox = document.getElementById('zotero-hardConfirmationDialog-checkbox');
|
||||||
|
checkbox.hidden = false;
|
||||||
|
checkbox.setAttribute('label', this.io.checkboxLabel);
|
||||||
|
this.onCheckbox();
|
||||||
|
}
|
||||||
|
if (this.io.confirmationText) {
|
||||||
|
document.getElementById('zotero-hardConfirmationDialog-textbox').hidden = false;
|
||||||
|
this.onKeyUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.io.extra1Label) {
|
||||||
|
document.documentElement.buttons = document.documentElement.buttons + ',extra1';
|
||||||
|
document.documentElement.getButton('extra1').label = this.io.extra1Label
|
||||||
|
} if (this.io.acceptLabel) {
|
||||||
|
document.documentElement.getButton('accept').label = this.io.acceptLabel
|
||||||
|
}
|
||||||
|
|
||||||
|
document.documentElement.setAttribute('title', this.io.title);
|
||||||
|
},
|
||||||
|
|
||||||
|
onCheckbox: function(event) {
|
||||||
|
document.documentElement.getButton('accept').disabled =
|
||||||
|
!document.getElementById('zotero-hardConfirmationDialog-checkbox').checked;
|
||||||
|
},
|
||||||
|
|
||||||
|
onKeyUp: function(event) {
|
||||||
|
document.documentElement.getButton('accept').disabled =
|
||||||
|
document.getElementById('zotero-hardConfirmationDialog-textbox').value != this.io.confirmationText;
|
||||||
|
},
|
||||||
|
|
||||||
|
onAccept: function() {
|
||||||
|
this.io.accept = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
onExtra1: function() {
|
||||||
|
this.io.extra1 = true;
|
||||||
|
document.documentElement.cancelDialog();
|
||||||
|
}
|
||||||
|
};
|
62
chrome/content/zotero/hardConfirmationDialog.xul
Normal file
62
chrome/content/zotero/hardConfirmationDialog.xul
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!--
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright © 2016 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 *****
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||||
|
<?xml-stylesheet href="chrome://global/content/commonDialog.css" type="text/css"?>
|
||||||
|
<?xml-stylesheet href="chrome://global/skin/commonDialog.css" type="text/css"?>
|
||||||
|
|
||||||
|
<!DOCTYPE overlay [ <!ENTITY % zoteroDTD SYSTEM "chrome://zotero/locale/zotero.dtd"> %zoteroDTD; ]>
|
||||||
|
|
||||||
|
<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||||
|
title="" buttons="cancel,accept"
|
||||||
|
id="zotero-hardConfirmationDialog"
|
||||||
|
onload="Zotero.HardConfirmationDialog.init(); sizeToContent();"
|
||||||
|
ondialogaccept="Zotero.HardConfirmationDialog.onAccept();"
|
||||||
|
ondialogextra1="Zotero.HardConfirmationDialog.onExtra1();">
|
||||||
|
|
||||||
|
<script src="chrome://zotero/content/include.js"/>
|
||||||
|
<script src="hardConfirmationDialog.js"/>
|
||||||
|
|
||||||
|
<grid>
|
||||||
|
<columns>
|
||||||
|
<column/>
|
||||||
|
<column flex="1"/>
|
||||||
|
</columns>
|
||||||
|
<rows>
|
||||||
|
<row>
|
||||||
|
<hbox id="iconContainer" align="start"><image id="info.icon" class="spaced alert-icon"/></hbox>
|
||||||
|
<vbox id="infoContainer">
|
||||||
|
<separator class="thin"/>
|
||||||
|
|
||||||
|
<checkbox id="zotero-hardConfirmationDialog-checkbox" hidden="true" oncommand="Zotero.HardConfirmationDialog.onCheckbox(event)"/>
|
||||||
|
<textbox id="zotero-hardConfirmationDialog-textbox" hidden="true" onkeyup="Zotero.HardConfirmationDialog.onKeyUp(event)"/>
|
||||||
|
</vbox>
|
||||||
|
</row>
|
||||||
|
</rows>
|
||||||
|
</grid>
|
||||||
|
|
||||||
|
</dialog>
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||||
|
Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||||
|
|
||||||
Zotero_Preferences.Sync = {
|
Zotero_Preferences.Sync = {
|
||||||
init: Zotero.Promise.coroutine(function* () {
|
init: Zotero.Promise.coroutine(function* () {
|
||||||
|
@ -100,6 +101,7 @@ Zotero_Preferences.Sync = {
|
||||||
|
|
||||||
if (event.keyCode == 13) {
|
if (event.keyCode == 13) {
|
||||||
Zotero_Preferences.Sync.linkAccount(event);
|
Zotero_Preferences.Sync.linkAccount(event);
|
||||||
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -165,11 +167,29 @@ Zotero_Preferences.Sync = {
|
||||||
|
|
||||||
unlinkAccount: Zotero.Promise.coroutine(function* (showAlert=true) {
|
unlinkAccount: Zotero.Promise.coroutine(function* (showAlert=true) {
|
||||||
if (showAlert) {
|
if (showAlert) {
|
||||||
if (!Services.prompt.confirm(
|
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,
|
null,
|
||||||
Zotero.getString('general.warning'),
|
Zotero.getString('general.warning'),
|
||||||
Zotero.getString('sync.unlinkWarning', Zotero.clientName)
|
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.getZoteroDirectory().path, '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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,10 @@ Zotero.Notifier = new function(){
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ref {Object} - signature {notify: function(event, type, ids, extraData) {}}
|
* @param {Object} [ref] signature {notify: function(event, type, ids, extraData) {}}
|
||||||
* @param types {Array} - a list of types of events observer should be triggered on
|
* @param {Array} [types] a list of types of events observer should be triggered on
|
||||||
* @param id {String} - an id of the observer used in debug output
|
* @param {String} [id] an id of the observer used in debug output
|
||||||
* @param priority {Integer} - lower numbers correspond to higher priority of observer execution
|
* @param {Integer} [priority] lower numbers correspond to higher priority of observer execution
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
this.registerObserver = function (ref, types, id, priority) {
|
this.registerObserver = function (ref, types, id, priority) {
|
||||||
|
|
|
@ -88,12 +88,12 @@ Zotero.Sync.Data.Local = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make sure we're syncing with the same account we used last time, and prompt if not.
|
* Make sure we're syncing with the same account we used last time, and prompt if not.
|
||||||
* If user accepts, change the current user, delete existing groups, and update relation
|
* If user accepts, change the current user and initiate deletion of all user data after a
|
||||||
* URIs to point to the new user's library.
|
* restart.
|
||||||
*
|
*
|
||||||
* @param {Window|null}
|
* @param {Window|null}
|
||||||
* @param {Integer} userID - New userID
|
* @param {Integer} userID - New userID
|
||||||
* @param {Integer} libraryID - New libraryID
|
* @param {Integer} username - New username
|
||||||
* @return {Boolean} - True to continue, false to cancel
|
* @return {Boolean} - True to continue, false to cancel
|
||||||
*/
|
*/
|
||||||
checkUser: Zotero.Promise.coroutine(function* (win, userID, username) {
|
checkUser: Zotero.Promise.coroutine(function* (win, userID, username) {
|
||||||
|
@ -101,73 +101,55 @@ Zotero.Sync.Data.Local = {
|
||||||
var lastUsername = Zotero.Users.getCurrentUsername();
|
var lastUsername = Zotero.Users.getCurrentUsername();
|
||||||
|
|
||||||
if (lastUserID && lastUserID != userID) {
|
if (lastUserID && lastUserID != userID) {
|
||||||
var groups = Zotero.Groups.getAll();
|
var io = {
|
||||||
|
title: Zotero.getString('general.warning'),
|
||||||
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
text: [Zotero.getString('account.lastSyncWithDifferentAccount', [ZOTERO_CONFIG.CLIENT_NAME, lastUsername, username])],
|
||||||
.getService(Components.interfaces.nsIPromptService);
|
checkboxLabel: Zotero.getString('account.confirmDelete', lastUsername),
|
||||||
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
|
acceptLabel: Zotero.getString('account.confirmDelete.button')
|
||||||
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL)
|
};
|
||||||
+ (ps.BUTTON_POS_2) * (ps.BUTTON_TITLE_IS_STRING)
|
win.openDialog("chrome://zotero/content/hardConfirmationDialog.xul", "",
|
||||||
+ ps.BUTTON_POS_1_DEFAULT
|
"chrome, dialog, modal, centerscreen", io);
|
||||||
+ ps.BUTTON_DELAY_ENABLE;
|
|
||||||
|
var accept = false;
|
||||||
var msg = Zotero.getString(
|
if (io.accept) {
|
||||||
'sync.lastSyncWithDifferentAccount', [ZOTERO_CONFIG.CLIENT_NAME, lastUsername, username]
|
var resetDataDirFile = OS.Path.join(Zotero.getZoteroDirectory().path, 'reset-data-directory');
|
||||||
);
|
yield Zotero.File.putContentsAsync(resetDataDirFile, '');
|
||||||
var syncButtonText = Zotero.getString('sync.sync');
|
|
||||||
|
Zotero.Utilities.Internal.quitZotero(true);
|
||||||
msg += " " + Zotero.getString('sync.localDataWillBeCombined', [username, ZOTERO_CONFIG.DOMAIN_NAME]);
|
accept = true;
|
||||||
// If there are local groups belonging to the previous user,
|
|
||||||
// we need to remove them
|
|
||||||
if (groups.length) {
|
|
||||||
msg += " " + Zotero.getString('sync.localGroupsWillBeRemoved1');
|
|
||||||
var syncButtonText = Zotero.getString('sync.removeGroupsAndSync');
|
|
||||||
}
|
}
|
||||||
msg += "\n\n" + Zotero.getString('sync.avoidCombiningData', lastUsername);
|
// else if (io.extra1) {
|
||||||
|
// if (Zotero.forceNewDataDirectory(win)) {
|
||||||
var index = ps.confirmEx(
|
// var ps = Services.prompt;
|
||||||
win,
|
// ps.alert(null,
|
||||||
Zotero.getString('general.warning'),
|
// Zotero.getString('general.restartRequired'),
|
||||||
msg,
|
// Zotero.getString('general.restartRequiredForChange', Zotero.appName)
|
||||||
buttonFlags,
|
// );
|
||||||
syncButtonText,
|
// Zotero.Utilities.Internal.quitZotero(true);
|
||||||
null,
|
// accept = true;
|
||||||
Zotero.getString('sync.openSyncPreferences'),
|
// }
|
||||||
null, {}
|
// }
|
||||||
);
|
if (accept) {
|
||||||
|
Zotero.Prefs.clear('sync.storage.downloadMode.groups');
|
||||||
if (index > 0) {
|
Zotero.Prefs.clear('sync.storage.groups.enabled');
|
||||||
if (index == 2) {
|
Zotero.Prefs.clear('sync.storage.downloadMode.personal');
|
||||||
win.ZoteroPane.openPreferences('zotero-prefpane-sync');
|
Zotero.Prefs.clear('sync.storage.username');
|
||||||
}
|
Zotero.Prefs.clear('sync.storage.url');
|
||||||
return false;
|
Zotero.Prefs.clear('sync.storage.scheme');
|
||||||
|
Zotero.Prefs.clear('sync.storage.protocol');
|
||||||
|
Zotero.Prefs.clear('sync.storage.enabled');
|
||||||
}
|
}
|
||||||
|
return accept;
|
||||||
}
|
}
|
||||||
|
|
||||||
yield Zotero.DB.executeTransaction(function* () {
|
yield Zotero.DB.executeTransaction(function* () {
|
||||||
if (lastUserID != userID) {
|
|
||||||
if (lastUserID) {
|
|
||||||
// Delete all local groups if changing users
|
|
||||||
for (let group of groups) {
|
|
||||||
yield group.erase();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update relations pointing to the old library to point to this one
|
|
||||||
yield Zotero.Relations.updateUser(userID);
|
|
||||||
}
|
|
||||||
// Replace local user key with libraryID, in case duplicates were
|
|
||||||
// merged before the first sync
|
|
||||||
else {
|
|
||||||
yield Zotero.Relations.updateUser(userID);
|
|
||||||
}
|
|
||||||
|
|
||||||
yield Zotero.Users.setCurrentUserID(userID);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastUsername != username) {
|
if (lastUsername != username) {
|
||||||
yield Zotero.Users.setCurrentUsername(username);
|
yield Zotero.Users.setCurrentUsername(username);
|
||||||
|
}
|
||||||
|
if (!lastUserID) {
|
||||||
|
yield Zotero.Users.setCurrentUserID(userID);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -255,13 +255,13 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
||||||
|
|
||||||
if (!userID) {
|
if (!userID) {
|
||||||
let hasItems = yield library.hasItems();
|
let hasItems = yield library.hasItems();
|
||||||
if (!hasItems && feeds.length <= 0) {
|
if (!hasItems && feeds.length <= 0 && !Zotero.resetDataDir) {
|
||||||
let ps = Services.prompt;
|
let ps = Services.prompt;
|
||||||
let index = ps.confirmEx(
|
let index = ps.confirmEx(
|
||||||
null,
|
null,
|
||||||
Zotero.getString('general.warning'),
|
Zotero.getString('general.warning'),
|
||||||
Zotero.getString('sync.warning.emptyLibrary', [keyInfo.username, Zotero.clientName]) + "\n\n"
|
Zotero.getString('account.warning.emptyLibrary', [keyInfo.username, Zotero.clientName]) + "\n\n"
|
||||||
+ Zotero.getString('sync.warning.existingDataElsewhere', Zotero.clientName),
|
+ Zotero.getString('account.warning.existingDataElsewhere', Zotero.clientName),
|
||||||
(ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING)
|
(ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING)
|
||||||
+ (ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL)
|
+ (ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL)
|
||||||
+ (ps.BUTTON_POS_2 * ps.BUTTON_TITLE_IS_STRING),
|
+ (ps.BUTTON_POS_2 * ps.BUTTON_TITLE_IS_STRING),
|
||||||
|
|
|
@ -1052,6 +1052,15 @@ Zotero.Utilities.Internal = {
|
||||||
}
|
}
|
||||||
elem.appendChild(menu);
|
elem.appendChild(menu);
|
||||||
return menu;
|
return menu;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quits Zotero, optionally restarting.
|
||||||
|
* @param {Boolean} [restart=false]
|
||||||
|
*/
|
||||||
|
quitZotero: function(restart=false) {
|
||||||
|
var startup = Services.startup;
|
||||||
|
startup.quit(startup.eAttemptQuit | (restart ? startup.eRestart : 0) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -479,27 +479,40 @@ Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||||
var _initFull = Zotero.Promise.coroutine(function* () {
|
var _initFull = Zotero.Promise.coroutine(function* () {
|
||||||
Zotero.VersionHeader.init();
|
Zotero.VersionHeader.init();
|
||||||
|
|
||||||
// Check for DB restore
|
// Check for data reset/restore
|
||||||
var dataDir = Zotero.getZoteroDirectory();
|
var dataDir = Zotero.getZoteroDirectory();
|
||||||
var restoreFile = dataDir.clone();
|
var restoreFile = OS.Path.join(dataDir.path, 'restore-from-server');
|
||||||
restoreFile.append('restore-from-server');
|
var resetDataDirFile = OS.Path.join(dataDir.path, 'reset-data-directory');
|
||||||
if (restoreFile.exists()) {
|
|
||||||
|
var result = yield Zotero.Promise.all([OS.File.exists(restoreFile), OS.File.exists(resetDataDirFile)]);
|
||||||
|
if (result.some(r => r)) {
|
||||||
|
[Zotero.restoreFromServer, Zotero.resetDataDir] = result;
|
||||||
try {
|
try {
|
||||||
// TODO: better error handling
|
// TODO: better error handling
|
||||||
|
|
||||||
// TODO: prompt for location
|
// TODO: prompt for location
|
||||||
// TODO: Back up database
|
// TODO: Back up database
|
||||||
|
|
||||||
restoreFile.remove(false);
|
|
||||||
|
|
||||||
var dbfile = Zotero.getZoteroDatabase();
|
var dbfile = Zotero.getZoteroDatabase().path;
|
||||||
dbfile.remove(false);
|
yield OS.File.remove(dbfile, {ignoreAbsent: true});
|
||||||
|
|
||||||
|
if (Zotero.restoreFromServer) {
|
||||||
|
yield OS.File.remove(restoreFile);
|
||||||
|
Zotero.restoreFromServer = true;
|
||||||
|
} else if (Zotero.resetDataDir) {
|
||||||
|
Zotero.initAutoSync = true;
|
||||||
|
var storageDir = OS.Path.join(dataDir.path, 'storage');
|
||||||
|
yield Zotero.Promise.all([
|
||||||
|
OS.File.removeDir(storageDir, {ignoreAbsent: true}),
|
||||||
|
OS.File.remove(resetDataDirFile)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
// Recreate database with no quick start guide
|
// Recreate database with no quick start guide
|
||||||
Zotero.Schema.skipDefaultData = true;
|
Zotero.Schema.skipDefaultData = true;
|
||||||
yield Zotero.Schema.updateSchema();
|
yield Zotero.Schema.updateSchema();
|
||||||
|
|
||||||
Zotero.restoreFromServer = true;
|
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
// Restore from backup?
|
// Restore from backup?
|
||||||
|
@ -1190,6 +1203,42 @@ Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||||
|
|
||||||
return useProfileDir ? true : file;
|
return useProfileDir ? true : file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.forceNewDataDirectory = function(win) {
|
||||||
|
if (!win) {
|
||||||
|
win = Services.wm.getMostRecentWindow('navigator:browser');
|
||||||
|
}
|
||||||
|
var ps = Services.prompt;
|
||||||
|
|
||||||
|
var nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||||
|
while (true) {
|
||||||
|
var fp = Components.classes["@mozilla.org/filepicker;1"]
|
||||||
|
.createInstance(nsIFilePicker);
|
||||||
|
fp.init(win, Zotero.getString('dataDir.selectNewDir', Zotero.clientName), nsIFilePicker.modeGetFolder);
|
||||||
|
fp.displayDirectory = Zotero.getZoteroDirectory();
|
||||||
|
fp.appendFilters(nsIFilePicker.filterAll);
|
||||||
|
if (fp.show() == nsIFilePicker.returnOK) {
|
||||||
|
var file = fp.file;
|
||||||
|
|
||||||
|
if (file.directoryEntries.hasMoreElements()) {
|
||||||
|
ps.alert(null,
|
||||||
|
Zotero.getString('dataDir.mustSelectEmpty.title'),
|
||||||
|
Zotero.getString('dataDir.mustSelectEmpty.text')
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set new data directory
|
||||||
|
Zotero.Prefs.set('dataDir', file.persistentDescriptor);
|
||||||
|
Zotero.Prefs.set('lastDataDir', file.path);
|
||||||
|
Zotero.Prefs.set('useDataDir', true);
|
||||||
|
return file;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
this.warnOnUnsafeDataDir = true;
|
this.warnOnUnsafeDataDir = true;
|
||||||
|
|
|
@ -408,8 +408,8 @@ var ZoteroPane = new function()
|
||||||
var d2 = new Date();
|
var d2 = new Date();
|
||||||
Zotero.debug("Purged data tables in " + (d2 - d) + " ms");
|
Zotero.debug("Purged data tables in " + (d2 - d) + " ms");
|
||||||
|
|
||||||
// Auto-sync on pane open
|
// Auto-sync on pane open or if new account
|
||||||
if (Zotero.Prefs.get('sync.autoSync')) {
|
if (Zotero.Prefs.get('sync.autoSync') || Zotero.initAutoSync) {
|
||||||
yield Zotero.proxyAuthComplete.delay(1000);
|
yield Zotero.proxyAuthComplete.delay(1000);
|
||||||
|
|
||||||
if (!Zotero.Sync.Runner.enabled) {
|
if (!Zotero.Sync.Runner.enabled) {
|
||||||
|
@ -424,7 +424,7 @@ var ZoteroPane = new function()
|
||||||
else {
|
else {
|
||||||
Zotero.Sync.Runner.sync({
|
Zotero.Sync.Runner.sync({
|
||||||
background: true
|
background: true
|
||||||
});
|
}).then(() => Zotero.initAutoSync = false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,13 +113,17 @@ dataDir.notFound = The Zotero data directory could not be found.
|
||||||
dataDir.previousDir = Previous directory:
|
dataDir.previousDir = Previous directory:
|
||||||
dataDir.useProfileDir = Use %S profile directory
|
dataDir.useProfileDir = Use %S profile directory
|
||||||
dataDir.selectDir = Select a Zotero data directory
|
dataDir.selectDir = Select a Zotero data directory
|
||||||
|
dataDir.selectNewDir = Select a new %S data directory
|
||||||
dataDir.changeDataDirectory = Change Data Directory…
|
dataDir.changeDataDirectory = Change Data Directory…
|
||||||
|
dataDir.chooseNewDataDirectory = Choose New Data Directory…
|
||||||
dataDir.unsafeLocation.selected.dropbox = Choosing a data directory within Dropbox may corrupt your database.
|
dataDir.unsafeLocation.selected.dropbox = Choosing a data directory within Dropbox may corrupt your database.
|
||||||
dataDir.unsafeLocation.selected.useAnyway = Use this directory anyway?
|
dataDir.unsafeLocation.selected.useAnyway = Use this directory anyway?
|
||||||
dataDir.unsafeLocation.existing.dropbox = Your Zotero data directory is within Dropbox, which may lead to data corruption.
|
dataDir.unsafeLocation.existing.dropbox = Your Zotero data directory is within Dropbox, which may lead to data corruption.
|
||||||
dataDir.unsafeLocation.existing.chooseDifferent = Would you like to choose a different location now?
|
dataDir.unsafeLocation.existing.chooseDifferent = Would you like to choose a different location now?
|
||||||
dataDir.selectedDirNonEmpty.title = Directory Not Empty
|
dataDir.selectedDirNonEmpty.title = Directory Not Empty
|
||||||
dataDir.selectedDirNonEmpty.text = The directory you selected is not empty and does not appear to be a Zotero data directory.\n\nCreate Zotero files in this directory anyway?
|
dataDir.selectedDirNonEmpty.text = The directory you selected is not empty and does not appear to be a Zotero data directory.\n\nCreate Zotero files in this directory anyway?
|
||||||
|
dataDir.mustSelectEmpty.title = Directory Not Empty
|
||||||
|
dataDir.mustSelectEmpty.text = The directory you selected is not empty. You must select an empty directory to continue.
|
||||||
dataDir.selectedDirEmpty.title = Directory Empty
|
dataDir.selectedDirEmpty.title = Directory Empty
|
||||||
dataDir.selectedDirEmpty.text = The directory you selected is empty. To move an existing Zotero data directory, you will need to manually move files from the existing data directory to the new location after %1$S has closed.
|
dataDir.selectedDirEmpty.text = The directory you selected is empty. To move an existing Zotero data directory, you will need to manually move files from the existing data directory to the new location after %1$S has closed.
|
||||||
dataDir.selectedDirEmpty.useNewDir = Use the new directory?
|
dataDir.selectedDirEmpty.useNewDir = Use the new directory?
|
||||||
|
@ -846,13 +850,14 @@ sync.error.emptyResponseServer = Empty response from server.
|
||||||
sync.error.invalidCharsFilename = The filename '%S' contains invalid characters.\n\nRename the file and try again. If you rename the file via the OS, you will need to relink it in Zotero.
|
sync.error.invalidCharsFilename = The filename '%S' contains invalid characters.\n\nRename the file and try again. If you rename the file via the OS, you will need to relink it in Zotero.
|
||||||
sync.error.apiKeyInvalid = %S could not authenticate your account. Please re-enter your account details.
|
sync.error.apiKeyInvalid = %S could not authenticate your account. Please re-enter your account details.
|
||||||
|
|
||||||
sync.lastSyncWithDifferentAccount = This Zotero database was last synced with a different %1$S account (‘%2$S’) from the current one (‘%3$S’).
|
account.unlinkWarning = Unlinking your account will prevent %S from syncing your data.
|
||||||
sync.localDataWillBeCombined = If you continue, local data will be combined with data from the ‘%1$S’ account on %2$S.
|
account.unlinkWarning.removeData = Remove my %S data from this computer
|
||||||
sync.localGroupsWillBeRemoved1 = Local groups, including any with changed items, will also be removed from this computer.
|
account.unlinkWarning.button = Unlink Account
|
||||||
sync.avoidCombiningData = To avoid combining data, revert to the ‘%S’ account or use the Reset options in the Sync pane of the Zotero preferences.
|
account.warning.emptyLibrary = You are about to sync the ‘%1$S’ account to an empty %2$S database. This could happen if you removed your previous database or if the location of your %2$S data directory changed.
|
||||||
sync.unlinkWarning = Are you sure you want to unlink this account?\n\n%S will no longer sync your data, but your data will remain locally.
|
account.warning.existingDataElsewhere = If your %S data exists elsewhere on your computer, you should move it to your current data directory or change your data directory to point to the existing data.
|
||||||
sync.warning.emptyLibrary = You are about to sync the ‘%1$S’ account to an empty %2$S database. This could happen if you removed your previous database or if the location of your %2$S data directory changed.
|
account.lastSyncWithDifferentAccount = This %1$S database was last synced with a different account (‘%2$S’) from the current one (‘%3$S’). If you continue, data associated with the ‘%2$S’ account will be removed from this computer.
|
||||||
sync.warning.existingDataElsewhere = If your %S data exists elsewhere on your computer, you should move it to your current data directory or change your data directory to point to the existing data.
|
account.confirmDelete = Remove data associated with the ‘%S’ account
|
||||||
|
account.confirmDelete.button = Switch Accounts
|
||||||
|
|
||||||
sync.conflict.autoChange.alert = One or more locally deleted Zotero %S have been modified remotely since the last sync.
|
sync.conflict.autoChange.alert = One or more locally deleted Zotero %S have been modified remotely since the last sync.
|
||||||
sync.conflict.autoChange.log = A Zotero %S has changed both locally and remotely since the last sync:
|
sync.conflict.autoChange.log = A Zotero %S has changed both locally and remotely since the last sync:
|
||||||
|
|
|
@ -95,6 +95,17 @@ describe("Sync Preferences", function () {
|
||||||
assert.equal(Zotero.Sync.Data.Local.getAPIKey(), "");
|
assert.equal(Zotero.Sync.Data.Local.getAPIKey(), "");
|
||||||
assert.equal(doc.getElementById('sync-authorized').getAttribute('hidden'), 'true');
|
assert.equal(doc.getElementById('sync-authorized').getAttribute('hidden'), 'true');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should not unlink on pressing cancel", function* () {
|
||||||
|
getAPIKeyFromCredentialsStub.resolves(apiResponse);
|
||||||
|
yield setCredentials("Username", "correctPassword");
|
||||||
|
|
||||||
|
waitForDialog(null, 'cancel');
|
||||||
|
|
||||||
|
yield win.Zotero_Preferences.Sync.unlinkAccount();
|
||||||
|
assert.equal(Zotero.Sync.Data.Local.getAPIKey(), apiKey);
|
||||||
|
assert.equal(doc.getElementById('sync-unauthorized').getAttribute('hidden'), 'true');
|
||||||
|
});
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -22,7 +22,22 @@ describe("Zotero.Sync.Data.Local", function() {
|
||||||
|
|
||||||
|
|
||||||
describe("#checkUser()", function () {
|
describe("#checkUser()", function () {
|
||||||
it("should prompt for user update and perform on accept", function* () {
|
var resetDataDirFile = OS.Path.join(Zotero.getZoteroDirectory().path, 'reset-data-directory');
|
||||||
|
|
||||||
|
before(function() {
|
||||||
|
sinon.stub(Zotero.Utilities.Internal, 'quitZotero');
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(function* () {
|
||||||
|
yield OS.File.remove(resetDataDirFile, {ignoreAbsent: true});
|
||||||
|
Zotero.Utilities.Internal.quitZotero.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
after(function() {
|
||||||
|
Zotero.Utilities.Internal.quitZotero.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should prompt for data reset and create a temp 'reset-data-directory' file on accept", function* (){
|
||||||
yield Zotero.Users.setCurrentUserID(1);
|
yield Zotero.Users.setCurrentUserID(1);
|
||||||
yield Zotero.Users.setCurrentUsername("A");
|
yield Zotero.Users.setCurrentUsername("A");
|
||||||
|
|
||||||
|
@ -30,54 +45,54 @@ describe("Zotero.Sync.Data.Local", function() {
|
||||||
waitForDialog(function (dialog) {
|
waitForDialog(function (dialog) {
|
||||||
var text = dialog.document.documentElement.textContent;
|
var text = dialog.document.documentElement.textContent;
|
||||||
var matches = text.match(/‘[^’]*’/g);
|
var matches = text.match(/‘[^’]*’/g);
|
||||||
assert.equal(matches.length, 4);
|
assert.equal(matches.length, 3);
|
||||||
assert.equal(matches[0], "‘A’");
|
assert.equal(matches[0], "‘A’");
|
||||||
assert.equal(matches[1], "‘B’");
|
assert.equal(matches[1], "‘B’");
|
||||||
assert.equal(matches[2], "‘B’");
|
assert.equal(matches[2], "‘A’");
|
||||||
assert.equal(matches[3], "‘A’");
|
|
||||||
|
dialog.document.getElementById('zotero-hardConfirmationDialog-checkbox').checked = true;
|
||||||
|
dialog.document.getElementById('zotero-hardConfirmationDialog-checkbox')
|
||||||
|
.dispatchEvent(new Event('command'));
|
||||||
|
|
||||||
handled = true;
|
handled = true;
|
||||||
});
|
}, 'accept', 'chrome://zotero/content/hardConfirmationDialog.xul');
|
||||||
var cont = yield Zotero.Sync.Data.Local.checkUser(null, 2, "B");
|
var cont = yield Zotero.Sync.Data.Local.checkUser(window, 2, "B");
|
||||||
|
var resetDataDirFileExists = yield OS.File.exists(resetDataDirFile);
|
||||||
assert.isTrue(handled);
|
assert.isTrue(handled);
|
||||||
assert.isTrue(cont);
|
assert.isTrue(cont);
|
||||||
|
assert.isTrue(resetDataDirFileExists);
|
||||||
assert.equal(Zotero.Users.getCurrentUserID(), 2);
|
});
|
||||||
assert.equal(Zotero.Users.getCurrentUsername(), "B");
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should prompt for user update and cancel", function* () {
|
it("should prompt for data reset and cancel", function* () {
|
||||||
yield Zotero.Users.setCurrentUserID(1);
|
yield Zotero.Users.setCurrentUserID(1);
|
||||||
yield Zotero.Users.setCurrentUsername("A");
|
yield Zotero.Users.setCurrentUsername("A");
|
||||||
|
|
||||||
waitForDialog(false, 'cancel');
|
waitForDialog(false, 'cancel', 'chrome://zotero/content/hardConfirmationDialog.xul');
|
||||||
var cont = yield Zotero.Sync.Data.Local.checkUser(null, 2, "B");
|
var cont = yield Zotero.Sync.Data.Local.checkUser(window, 2, "B");
|
||||||
|
var resetDataDirFileExists = yield OS.File.exists(resetDataDirFile);
|
||||||
assert.isFalse(cont);
|
assert.isFalse(cont);
|
||||||
|
assert.isFalse(resetDataDirFileExists);
|
||||||
|
|
||||||
assert.equal(Zotero.Users.getCurrentUserID(), 1);
|
assert.equal(Zotero.Users.getCurrentUserID(), 1);
|
||||||
assert.equal(Zotero.Users.getCurrentUsername(), "A");
|
assert.equal(Zotero.Users.getCurrentUsername(), "A");
|
||||||
})
|
});
|
||||||
|
|
||||||
it("should update local relations when syncing for the first time", function* () {
|
// extra1 functionality not used at the moment
|
||||||
yield resetDB({
|
it.skip("should prompt for data reset and allow to choose a new data directory", function* (){
|
||||||
thisArg: this,
|
sinon.stub(Zotero, 'forceNewDataDirectory').returns(true);
|
||||||
skipBundledFiles: true
|
yield Zotero.Users.setCurrentUserID(1);
|
||||||
});
|
yield Zotero.Users.setCurrentUsername("A");
|
||||||
|
|
||||||
var item1 = yield createDataObject('item');
|
waitForDialog(null, 'extra1', 'chrome://zotero/content/hardConfirmationDialog.xul');
|
||||||
var item2 = yield createDataObject(
|
waitForDialog();
|
||||||
'item', { libraryID: Zotero.Libraries.publicationsLibraryID }
|
var cont = yield Zotero.Sync.Data.Local.checkUser(window, 2, "B");
|
||||||
);
|
var resetDataDirFileExists = yield OS.File.exists(resetDataDirFile);
|
||||||
|
|
||||||
yield item1.addLinkedItem(item2);
|
|
||||||
|
|
||||||
var cont = yield Zotero.Sync.Data.Local.checkUser(null, 1, "A");
|
|
||||||
assert.isTrue(cont);
|
assert.isTrue(cont);
|
||||||
|
assert.isTrue(Zotero.forceNewDataDirectory.called);
|
||||||
|
assert.isFalse(resetDataDirFileExists);
|
||||||
|
|
||||||
var json = item1.toJSON();
|
Zotero.forceNewDataDirectory.restore();
|
||||||
var uri = json.relations[Zotero.Relations.linkedObjectPredicate][0];
|
});
|
||||||
assert.notInclude(uri, 'users/local');
|
|
||||||
assert.include(uri, 'users/1/publications');
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue