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
|
// Initialized as Zotero.Sync.Runner in zotero.js
|
||||||
Zotero.Sync.Runner_Module = function (options = {}) {
|
Zotero.Sync.Runner_Module = function (options = {}) {
|
||||||
const stopOnError = false;
|
const stopOnError = false;
|
||||||
|
const HTML_NS = 'http://www.w3.org/1999/xhtml';
|
||||||
|
|
||||||
Zotero.defineProperty(this, 'enabled', {
|
Zotero.defineProperty(this, 'enabled', {
|
||||||
get: () => {
|
get: () => {
|
||||||
|
@ -84,7 +85,9 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
||||||
var _lastSyncStatus;
|
var _lastSyncStatus;
|
||||||
var _currentSyncStatusLabel;
|
var _currentSyncStatusLabel;
|
||||||
var _currentLastSyncLabel;
|
var _currentLastSyncLabel;
|
||||||
|
var _currentTooltipMessages;
|
||||||
var _errors = [];
|
var _errors = [];
|
||||||
|
var _tooltipMessages = [];
|
||||||
|
|
||||||
Zotero.addShutdownListener(() => this.stop());
|
Zotero.addShutdownListener(() => this.stop());
|
||||||
|
|
||||||
|
@ -118,6 +121,7 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
||||||
this._sync = Zotero.Promise.coroutine(function* (options) {
|
this._sync = Zotero.Promise.coroutine(function* (options) {
|
||||||
// Clear message list
|
// Clear message list
|
||||||
_errors = [];
|
_errors = [];
|
||||||
|
_tooltipMessages = [];
|
||||||
|
|
||||||
// Shouldn't be possible because of serial()
|
// Shouldn't be possible because of serial()
|
||||||
if (_syncInProgress) {
|
if (_syncInProgress) {
|
||||||
|
@ -1033,7 +1037,8 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
||||||
upgrade: 4,
|
upgrade: 4,
|
||||||
|
|
||||||
// Skip these
|
// Skip these
|
||||||
animate: -1
|
animate: -1,
|
||||||
|
ignore: -2
|
||||||
};
|
};
|
||||||
var state = false;
|
var state = false;
|
||||||
for (let i = 0; i < errors.length; i++) {
|
for (let i = 0; i < errors.length; i++) {
|
||||||
|
@ -1265,21 +1270,32 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
||||||
}
|
}
|
||||||
// Show warning for unknown data that couldn't be saved
|
// Show warning for unknown data that couldn't be saved
|
||||||
else if (e.name && e.name == 'ZoteroInvalidDataError') {
|
else if (e.name && e.name == 'ZoteroInvalidDataError') {
|
||||||
e.message = Zotero.getString(
|
let library = Zotero.Libraries.get(e.libraryID);
|
||||||
|
let msg = Zotero.getString(
|
||||||
'sync.error.invalidDataError',
|
'sync.error.invalidDataError',
|
||||||
[
|
[
|
||||||
Zotero.Libraries.get(e.libraryID).name,
|
library.name,
|
||||||
Zotero.clientName
|
Zotero.clientName
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
+ "\n\n"
|
+ "\n\n"
|
||||||
+ Zotero.getString('sync.error.invalidDataError.otherData');
|
+ Zotero.getString('sync.error.invalidDataError.otherData');
|
||||||
|
|
||||||
|
// Show warning for My Library
|
||||||
|
if (library.libraryType == 'user') {
|
||||||
|
e.message = msg;
|
||||||
e.errorType = 'warning';
|
e.errorType = 'warning';
|
||||||
e.dialogButtonText = Zotero.getString('general.checkForUpdates');
|
e.dialogButtonText = Zotero.getString('general.checkForUpdates');
|
||||||
e.dialogButtonCallback = () => {
|
e.dialogButtonCallback = () => {
|
||||||
Zotero.openCheckForUpdatesWindow();
|
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)) {
|
if (!Array.isArray(errors)) {
|
||||||
errors = [errors];
|
errors = [errors];
|
||||||
}
|
}
|
||||||
|
errors = errors.filter(o => o.errorType !== 'ignore');
|
||||||
var state = this.getPrimaryErrorType(errors);
|
var state = this.getPrimaryErrorType(errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1512,10 +1529,12 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
||||||
if (tooltip) {
|
if (tooltip) {
|
||||||
_currentSyncStatusLabel = tooltip.firstChild.nextSibling;
|
_currentSyncStatusLabel = tooltip.firstChild.nextSibling;
|
||||||
_currentLastSyncLabel = tooltip.firstChild.nextSibling.nextSibling;
|
_currentLastSyncLabel = tooltip.firstChild.nextSibling.nextSibling;
|
||||||
|
_currentTooltipMessages = tooltip.querySelector('.sync-button-tooltip-messages');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
_currentSyncStatusLabel = null;
|
_currentSyncStatusLabel = null;
|
||||||
_currentLastSyncLabel = null;
|
_currentLastSyncLabel = null;
|
||||||
|
_currentTooltipMessages = null;
|
||||||
}
|
}
|
||||||
if (_currentSyncStatusLabel) {
|
if (_currentSyncStatusLabel) {
|
||||||
_updateSyncStatusLabel();
|
_updateSyncStatusLabel();
|
||||||
|
@ -1549,6 +1568,11 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
function _addTooltipMessage(msg) {
|
||||||
|
_tooltipMessages.push(msg.replace(/\n+/g, ' '));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
function _updateSyncStatusLabel() {
|
function _updateSyncStatusLabel() {
|
||||||
if (_lastSyncStatus) {
|
if (_lastSyncStatus) {
|
||||||
_currentSyncStatusLabel.value = _lastSyncStatus;
|
_currentSyncStatusLabel.value = _lastSyncStatus;
|
||||||
|
@ -1585,6 +1609,19 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
||||||
|
|
||||||
_currentLastSyncLabel.value = Zotero.getString('sync.status.lastSync') + " " + msg;
|
_currentLastSyncLabel.value = Zotero.getString('sync.status.lastSync') + " " + msg;
|
||||||
_currentLastSyncLabel.hidden = false;
|
_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-label"/>
|
||||||
<label id="zotero-tb-sync-status" hidden="true"/>
|
<label id="zotero-tb-sync-status" hidden="true"/>
|
||||||
<label id="zotero-tb-sync-last-sync"/>
|
<label id="zotero-tb-sync-last-sync"/>
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml" class="sync-button-tooltip-messages"/>
|
||||||
</tooltip>
|
</tooltip>
|
||||||
</toolbarbutton>
|
</toolbarbutton>
|
||||||
</hbox>
|
</hbox>
|
||||||
|
|
|
@ -27,5 +27,6 @@
|
||||||
@import "components/icons";
|
@import "components/icons";
|
||||||
@import "components/progressMeter";
|
@import "components/progressMeter";
|
||||||
@import "components/search";
|
@import "components/search";
|
||||||
|
@import "components/syncButtonTooltip";
|
||||||
@import "components/tagsBox";
|
@import "components/tagsBox";
|
||||||
@import "components/tagSelector";
|
@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);
|
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
|
// 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;
|
let library = Zotero.Libraries.userLibrary;
|
||||||
library.libraryVersion = 5;
|
library.libraryVersion = 1;
|
||||||
yield library.save();
|
await library.save();
|
||||||
|
|
||||||
setResponse('keyInfo.fullAccess');
|
setResponse('keyInfo.fullAccess');
|
||||||
setResponse('userGroups.groupVersionsEmpty');
|
setResponse('userGroups.groupVersionsEmpty');
|
||||||
// My Library
|
|
||||||
setResponse({
|
// No other responses, so settings response will be a 404
|
||||||
method: "GET",
|
|
||||||
url: "users/1/settings",
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
"Last-Modified-Version": 5
|
|
||||||
},
|
|
||||||
json: {
|
|
||||||
INVALID: true // TODO: Find a cleaner error
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
spy = sinon.spy(runner, "updateIcons");
|
spy = sinon.spy(runner, "updateIcons");
|
||||||
yield runner.sync();
|
await runner.sync();
|
||||||
assert.isTrue(spy.calledTwice);
|
assert.isTrue(spy.calledTwice);
|
||||||
assert.isArray(spy.args[1][0]);
|
assert.isArray(spy.args[1][0]);
|
||||||
assert.lengthOf(spy.args[1][0], 1);
|
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
|
// TODO: Test multiple long tags and tags across libraries
|
||||||
describe("Long Tag Fixer", function () {
|
describe("Long Tag Fixer", function () {
|
||||||
it("should split a tag", function* () {
|
it("should split a tag", function* () {
|
||||||
|
|
Loading…
Reference in a new issue