Show invalid-data warning in sync button tooltip for group libraries
Instead of showing warning button on toolbar Otherwise if one person in a group upgrades to a beta with new fields, everyone in the group will get a warning, even if they're already on the latest release version. In a personal library, the user can upgrade to the same version.
This commit is contained in:
parent
b919143630
commit
843bcbb68a
5 changed files with 238 additions and 32 deletions
|
@ -32,6 +32,7 @@ if (!Zotero.Sync) {
|
|||
// Initialized as Zotero.Sync.Runner in zotero.js
|
||||
Zotero.Sync.Runner_Module = function (options = {}) {
|
||||
const stopOnError = false;
|
||||
const HTML_NS = 'http://www.w3.org/1999/xhtml';
|
||||
|
||||
Zotero.defineProperty(this, 'enabled', {
|
||||
get: () => {
|
||||
|
@ -84,7 +85,9 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
|||
var _lastSyncStatus;
|
||||
var _currentSyncStatusLabel;
|
||||
var _currentLastSyncLabel;
|
||||
var _currentTooltipMessages;
|
||||
var _errors = [];
|
||||
var _tooltipMessages = [];
|
||||
|
||||
Zotero.addShutdownListener(() => this.stop());
|
||||
|
||||
|
@ -118,6 +121,7 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
|||
this._sync = Zotero.Promise.coroutine(function* (options) {
|
||||
// Clear message list
|
||||
_errors = [];
|
||||
_tooltipMessages = [];
|
||||
|
||||
// Shouldn't be possible because of serial()
|
||||
if (_syncInProgress) {
|
||||
|
@ -1033,7 +1037,8 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
|||
upgrade: 4,
|
||||
|
||||
// Skip these
|
||||
animate: -1
|
||||
animate: -1,
|
||||
ignore: -2
|
||||
};
|
||||
var state = false;
|
||||
for (let i = 0; i < errors.length; i++) {
|
||||
|
@ -1265,20 +1270,31 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
|||
}
|
||||
// Show warning for unknown data that couldn't be saved
|
||||
else if (e.name && e.name == 'ZoteroInvalidDataError') {
|
||||
e.message = Zotero.getString(
|
||||
'sync.error.invalidDataError',
|
||||
[
|
||||
Zotero.Libraries.get(e.libraryID).name,
|
||||
Zotero.clientName
|
||||
]
|
||||
)
|
||||
+ "\n\n"
|
||||
+ Zotero.getString('sync.error.invalidDataError.otherData');
|
||||
e.errorType = 'warning';
|
||||
e.dialogButtonText = Zotero.getString('general.checkForUpdates');
|
||||
e.dialogButtonCallback = () => {
|
||||
Zotero.openCheckForUpdatesWindow();
|
||||
};
|
||||
let library = Zotero.Libraries.get(e.libraryID);
|
||||
let msg = Zotero.getString(
|
||||
'sync.error.invalidDataError',
|
||||
[
|
||||
library.name,
|
||||
Zotero.clientName
|
||||
]
|
||||
)
|
||||
+ "\n\n"
|
||||
+ Zotero.getString('sync.error.invalidDataError.otherData');
|
||||
|
||||
// Show warning for My Library
|
||||
if (library.libraryType == 'user') {
|
||||
e.message = msg;
|
||||
e.errorType = 'warning';
|
||||
e.dialogButtonText = Zotero.getString('general.checkForUpdates');
|
||||
e.dialogButtonCallback = () => {
|
||||
Zotero.openCheckForUpdatesWindow();
|
||||
};
|
||||
}
|
||||
// Otherwise just show in sync button tooltip
|
||||
else {
|
||||
_addTooltipMessage(msg);
|
||||
e.errorType = 'ignore';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1299,6 +1315,7 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
|||
if (!Array.isArray(errors)) {
|
||||
errors = [errors];
|
||||
}
|
||||
errors = errors.filter(o => o.errorType !== 'ignore');
|
||||
var state = this.getPrimaryErrorType(errors);
|
||||
}
|
||||
|
||||
|
@ -1512,10 +1529,12 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
|||
if (tooltip) {
|
||||
_currentSyncStatusLabel = tooltip.firstChild.nextSibling;
|
||||
_currentLastSyncLabel = tooltip.firstChild.nextSibling.nextSibling;
|
||||
_currentTooltipMessages = tooltip.querySelector('.sync-button-tooltip-messages');
|
||||
}
|
||||
else {
|
||||
_currentSyncStatusLabel = null;
|
||||
_currentLastSyncLabel = null;
|
||||
_currentTooltipMessages = null;
|
||||
}
|
||||
if (_currentSyncStatusLabel) {
|
||||
_updateSyncStatusLabel();
|
||||
|
@ -1547,7 +1566,12 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
|||
Zotero.Sync.Data.Local.setAPIKey();
|
||||
yield client.deleteAPIKey();
|
||||
})
|
||||
|
||||
|
||||
|
||||
function _addTooltipMessage(msg) {
|
||||
_tooltipMessages.push(msg.replace(/\n+/g, ' '));
|
||||
};
|
||||
|
||||
|
||||
function _updateSyncStatusLabel() {
|
||||
if (_lastSyncStatus) {
|
||||
|
@ -1585,6 +1609,19 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
|||
|
||||
_currentLastSyncLabel.value = Zotero.getString('sync.status.lastSync') + " " + msg;
|
||||
_currentLastSyncLabel.hidden = false;
|
||||
|
||||
if (_tooltipMessages.length) {
|
||||
_currentTooltipMessages.textContent = '';
|
||||
for (let message of _tooltipMessages) {
|
||||
let elem = _currentTooltipMessages.ownerDocument.createElementNS(HTML_NS, 'p');
|
||||
elem.textContent = message;
|
||||
_currentTooltipMessages.appendChild(elem);
|
||||
}
|
||||
_currentTooltipMessages.hidden = false;
|
||||
}
|
||||
else {
|
||||
_currentTooltipMessages.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -214,6 +214,7 @@
|
|||
<label id="zotero-tb-sync-label"/>
|
||||
<label id="zotero-tb-sync-status" hidden="true"/>
|
||||
<label id="zotero-tb-sync-last-sync"/>
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" class="sync-button-tooltip-messages"/>
|
||||
</tooltip>
|
||||
</toolbarbutton>
|
||||
</hbox>
|
||||
|
|
|
@ -27,5 +27,6 @@
|
|||
@import "components/icons";
|
||||
@import "components/progressMeter";
|
||||
@import "components/search";
|
||||
@import "components/syncButtonTooltip";
|
||||
@import "components/tagsBox";
|
||||
@import "components/tagSelector";
|
||||
|
|
8
scss/components/_syncButtonTooltip.scss
Normal file
8
scss/components/_syncButtonTooltip.scss
Normal file
|
@ -0,0 +1,8 @@
|
|||
.sync-button-tooltip-messages {
|
||||
max-width: 400px;
|
||||
|
||||
p {
|
||||
color: gray;
|
||||
margin: 8px 6px;
|
||||
}
|
||||
}
|
|
@ -109,6 +109,63 @@ describe("Zotero.Sync.Runner", function () {
|
|||
setHTTPResponse(server, baseURL, response, responses);
|
||||
}
|
||||
|
||||
function setDefaultResponses(options = {}) {
|
||||
var target = options.target || 'users/1';
|
||||
var headers = {
|
||||
"Last-Modified-Version": options.libraryVersion || 5
|
||||
};
|
||||
var lastLibraryVersion = options.lastLibraryVersion || 4;
|
||||
setResponse({
|
||||
method: "GET",
|
||||
url: `${target}/settings?since=${lastLibraryVersion}`,
|
||||
status: 200,
|
||||
headers,
|
||||
json: {}
|
||||
});
|
||||
setResponse({
|
||||
method: "GET",
|
||||
url: `${target}/collections?format=versions&since=${lastLibraryVersion}`,
|
||||
status: 200,
|
||||
headers,
|
||||
json: {}
|
||||
});
|
||||
setResponse({
|
||||
method: "GET",
|
||||
url: `${target}/searches?format=versions&since=${lastLibraryVersion}`,
|
||||
status: 200,
|
||||
headers,
|
||||
json: {}
|
||||
});
|
||||
setResponse({
|
||||
method: "GET",
|
||||
url: `${target}/items/top?format=versions&since=${lastLibraryVersion}&includeTrashed=1`,
|
||||
status: 200,
|
||||
headers,
|
||||
json: {}
|
||||
});
|
||||
setResponse({
|
||||
method: "GET",
|
||||
url: `${target}/items?format=versions&since=${lastLibraryVersion}&includeTrashed=1`,
|
||||
status: 200,
|
||||
headers,
|
||||
json: {}
|
||||
});
|
||||
setResponse({
|
||||
method: "GET",
|
||||
url: `${target}/deleted?since=${lastLibraryVersion}`,
|
||||
status: 200,
|
||||
headers,
|
||||
json: {}
|
||||
});
|
||||
setResponse({
|
||||
method: "GET",
|
||||
url: `${target}/fulltext?format=versions`,
|
||||
status: 200,
|
||||
headers,
|
||||
json: {}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Tests
|
||||
|
@ -993,28 +1050,18 @@ describe("Zotero.Sync.Runner", function () {
|
|||
}
|
||||
});
|
||||
|
||||
it("should show the sync error icon on error", function* () {
|
||||
it("should show the sync error icon on error", async function () {
|
||||
let library = Zotero.Libraries.userLibrary;
|
||||
library.libraryVersion = 5;
|
||||
yield library.save();
|
||||
library.libraryVersion = 1;
|
||||
await library.save();
|
||||
|
||||
setResponse('keyInfo.fullAccess');
|
||||
setResponse('userGroups.groupVersionsEmpty');
|
||||
// My Library
|
||||
setResponse({
|
||||
method: "GET",
|
||||
url: "users/1/settings",
|
||||
status: 200,
|
||||
headers: {
|
||||
"Last-Modified-Version": 5
|
||||
},
|
||||
json: {
|
||||
INVALID: true // TODO: Find a cleaner error
|
||||
}
|
||||
});
|
||||
|
||||
// No other responses, so settings response will be a 404
|
||||
|
||||
spy = sinon.spy(runner, "updateIcons");
|
||||
yield runner.sync();
|
||||
await runner.sync();
|
||||
assert.isTrue(spy.calledTwice);
|
||||
assert.isArray(spy.args[1][0]);
|
||||
assert.lengthOf(spy.args[1][0], 1);
|
||||
|
@ -1097,6 +1144,118 @@ describe("Zotero.Sync.Runner", function () {
|
|||
});
|
||||
|
||||
|
||||
it("should show an error for invalid My Library data", async function () {
|
||||
let library = Zotero.Libraries.userLibrary;
|
||||
library.libraryVersion = 1;
|
||||
await library.save();
|
||||
|
||||
var collection = await createDataObject('collection', { synced: true });
|
||||
var json = collection.toResponseJSON();
|
||||
json.version = json.data.version = 2;
|
||||
json.data.INVALID = true;
|
||||
|
||||
setResponse('keyInfo.fullAccess');
|
||||
setResponse('userGroups.groupVersionsEmpty');
|
||||
setDefaultResponses({
|
||||
lastLibraryVersion: 1,
|
||||
libraryVersion: 2
|
||||
});
|
||||
setResponse({
|
||||
method: "GET",
|
||||
url: "users/1/collections?format=versions&since=1",
|
||||
status: 200,
|
||||
headers: {
|
||||
"Last-Modified-Version": 2
|
||||
},
|
||||
json: {
|
||||
[json.key]: 2
|
||||
}
|
||||
});
|
||||
setResponse({
|
||||
method: "GET",
|
||||
url: `users/1/collections?format=json&collectionKey=${json.key}`,
|
||||
status: 200,
|
||||
headers: {
|
||||
"Last-Modified-Version": 2
|
||||
},
|
||||
json: [json]
|
||||
});
|
||||
|
||||
spy = sinon.spy(runner, "updateIcons");
|
||||
await runner.sync();
|
||||
assert.isTrue(spy.calledTwice);
|
||||
assert.isArray(spy.args[1][0]);
|
||||
assert.lengthOf(spy.args[1][0], 1);
|
||||
// Not an instance of Error for some reason
|
||||
var error = spy.args[1][0][0];
|
||||
assert.equal(Object.getPrototypeOf(error).constructor.name, "Error");
|
||||
assert.match(error.message, /^Some data in My Library/);
|
||||
});
|
||||
|
||||
|
||||
it("should show a warning in the sync button tooltip for invalid group data", async function () {
|
||||
win = await loadZoteroPane();
|
||||
var doc = win.document;
|
||||
|
||||
// Create group with same id and version as groups response
|
||||
var groupData = responses.groups.memberGroup;
|
||||
var group = await createGroup({
|
||||
id: groupData.json.id,
|
||||
version: groupData.json.version
|
||||
});
|
||||
group.libraryVersion = 1;
|
||||
await group.save();
|
||||
|
||||
var collection = await createDataObject('collection', { synced: true });
|
||||
var json = collection.toResponseJSON();
|
||||
json.version = json.data.version = 2;
|
||||
json.data.INVALID = true;
|
||||
|
||||
var target = 'groups/' + group.id;
|
||||
setResponse('keyInfo.fullAccess');
|
||||
setResponse('userGroups.groupVersionsOnlyMemberGroup');
|
||||
setResponse('groups.memberGroup');
|
||||
setDefaultResponses({
|
||||
target,
|
||||
lastLibraryVersion: 1,
|
||||
libraryVersion: 2
|
||||
});
|
||||
setResponse({
|
||||
method: "GET",
|
||||
url: target + '/collections?format=versions&since=1',
|
||||
status: 200,
|
||||
headers: {
|
||||
"Last-Modified-Version": 2
|
||||
},
|
||||
json: {
|
||||
[json.key]: 2
|
||||
}
|
||||
});
|
||||
setResponse({
|
||||
method: "GET",
|
||||
url: target + `/collections?format=json&collectionKey=${json.key}`,
|
||||
status: 200,
|
||||
headers: {
|
||||
"Last-Modified-Version": 2
|
||||
},
|
||||
json: [json]
|
||||
});
|
||||
|
||||
await runner.sync({ libraries: [group.libraryID] });
|
||||
|
||||
assert.isTrue(doc.getElementById('zotero-tb-sync-error').hidden);
|
||||
|
||||
// Fake what happens on button mouseover
|
||||
var tooltip = doc.getElementById('zotero-tb-sync-tooltip');
|
||||
runner.registerSyncStatus(tooltip);
|
||||
|
||||
var html = doc.getElementById('zotero-tb-sync-tooltip').innerHTML;
|
||||
assert.match(html, /Some data in .+\. Other data will continue to sync\./);
|
||||
|
||||
runner.registerSyncStatus();
|
||||
});
|
||||
|
||||
|
||||
// TODO: Test multiple long tags and tags across libraries
|
||||
describe("Long Tag Fixer", function () {
|
||||
it("should split a tag", function* () {
|
||||
|
|
Loading…
Reference in a new issue