Add mechanism for showing dialog with notice from repo
- Messages are shown once a day by default (within the same session for id-less messages) - Messages with an `id` attribute include a checkbox to not show again for 30 days - If an `infoURL` attribute is provided, a "More Information" button is shown that launches that URL - If `title` is provided, it's used for the dialog title. Otherwise "Warning" is shown.
This commit is contained in:
parent
ba7d0a18aa
commit
2459614f04
2 changed files with 261 additions and 0 deletions
|
@ -53,6 +53,7 @@ Zotero.Schema = new function(){
|
||||||
var _nextRepositoryUpdate;
|
var _nextRepositoryUpdate;
|
||||||
var _remoteUpdateInProgress = false;
|
var _remoteUpdateInProgress = false;
|
||||||
var _localUpdateInProgress = false;
|
var _localUpdateInProgress = false;
|
||||||
|
var _hiddenNoticesWithoutIDs = new Map();
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
@ -2364,6 +2365,8 @@ Zotero.Schema = new function(){
|
||||||
var translatorUpdates = xmlhttp.responseXML.getElementsByTagName('translator');
|
var translatorUpdates = xmlhttp.responseXML.getElementsByTagName('translator');
|
||||||
var styleUpdates = xmlhttp.responseXML.getElementsByTagName('style');
|
var styleUpdates = xmlhttp.responseXML.getElementsByTagName('style');
|
||||||
|
|
||||||
|
_showRepositoryMessage(xmlhttp.responseXML);
|
||||||
|
|
||||||
if (!translatorUpdates.length && !styleUpdates.length){
|
if (!translatorUpdates.length && !styleUpdates.length){
|
||||||
await Zotero.DB.executeTransaction(function* (conn) {
|
await Zotero.DB.executeTransaction(function* (conn) {
|
||||||
// Store the timestamp provided by the server
|
// Store the timestamp provided by the server
|
||||||
|
@ -2415,6 +2418,111 @@ Zotero.Schema = new function(){
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show dialog if repo returns a message
|
||||||
|
*/
|
||||||
|
function _showRepositoryMessage(responseXML) {
|
||||||
|
try {
|
||||||
|
var messageElem = responseXML.querySelector('message');
|
||||||
|
if (!messageElem || !messageElem.textContent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let hiddenNotices = Zotero.Prefs.get('hiddenNotices') || '{}';
|
||||||
|
try {
|
||||||
|
hiddenNotices = JSON.parse(hiddenNotices);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
Zotero.logError(e);
|
||||||
|
hiddenNotices = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = messageElem.getAttribute('id');
|
||||||
|
let title = messageElem.getAttribute('title');
|
||||||
|
let text = messageElem.textContent;
|
||||||
|
let url = messageElem.getAttribute('infoURL');
|
||||||
|
let now = Math.round(Date.now() / 1000);
|
||||||
|
let thirtyDays = 86400 * 30;
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
if (hiddenNotices[id] && hiddenNotices[id] > now) {
|
||||||
|
Zotero.debug("Not showing hidden notice " + id, 2);
|
||||||
|
Zotero.debug(text, 2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Zotero.debug("CHECKING");
|
||||||
|
let exp = _hiddenNoticesWithoutIDs.get(text);
|
||||||
|
Zotero.debug(exp);
|
||||||
|
Zotero.debug(now);
|
||||||
|
if (exp && exp > now) {
|
||||||
|
Zotero.debug("Not showing hidden notice", 2);
|
||||||
|
Zotero.debug(text, 2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
Zotero.debug(text, 2);
|
||||||
|
|
||||||
|
var ps = Services.prompt;
|
||||||
|
var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_OK
|
||||||
|
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_IS_STRING;
|
||||||
|
var checkState = {};
|
||||||
|
var index = ps.confirmEx(
|
||||||
|
null,
|
||||||
|
title || Zotero.getString('general.warning'),
|
||||||
|
text,
|
||||||
|
buttonFlags,
|
||||||
|
"",
|
||||||
|
// Show "More Information" button if repo includes a URL
|
||||||
|
url ? Zotero.getString('general.moreInformation') : "",
|
||||||
|
"",
|
||||||
|
// Show "Don't show again for 30 days" if repo includes an id
|
||||||
|
id ? Zotero.getString('general.dontShowAgainFor', 30, 30) : null,
|
||||||
|
checkState
|
||||||
|
);
|
||||||
|
|
||||||
|
if (index == 1) {
|
||||||
|
setTimeout(function () {
|
||||||
|
Zotero.launchURL(url);
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
// Handle "Don't show again for 30 days" checkbox
|
||||||
|
if (id) {
|
||||||
|
if (checkState.value) {
|
||||||
|
hiddenNotices[id] = now + thirtyDays;
|
||||||
|
}
|
||||||
|
// If not checked, still don't show again for a day
|
||||||
|
else {
|
||||||
|
hiddenNotices[id] = now + 86400;
|
||||||
|
}
|
||||||
|
// Remove expired hidden notices
|
||||||
|
for (let i in hiddenNotices) {
|
||||||
|
if (hiddenNotices[i] < now) {
|
||||||
|
delete hiddenNotices[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(hiddenNotices).length) {
|
||||||
|
Zotero.Prefs.set('hiddenNotices', JSON.stringify(hiddenNotices));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Zotero.Prefs.clear('hiddenNotices');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Don't show id-less messages again for a day
|
||||||
|
_hiddenNoticesWithoutIDs.set(text, now + 86400);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
Zotero.logError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the interval between repository queries
|
* Set the interval between repository queries
|
||||||
*
|
*
|
||||||
|
|
|
@ -240,6 +240,159 @@ describe("Zotero.Schema", function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe("Repository Check", function () {
|
||||||
|
describe("Notices", function () {
|
||||||
|
var win;
|
||||||
|
var server;
|
||||||
|
|
||||||
|
before(async function () {
|
||||||
|
win = await loadZoteroPane();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
Zotero.HTTP.mock = sinon.FakeXMLHttpRequest;
|
||||||
|
server = sinon.fakeServer.create();
|
||||||
|
server.autoRespond = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
Zotero.Prefs.clear('hiddenNotices');
|
||||||
|
});
|
||||||
|
|
||||||
|
after(function () {
|
||||||
|
win.close();
|
||||||
|
Zotero.HTTP.mock = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
function createResponseWithMessage(message) {
|
||||||
|
server.respond(function (req) {
|
||||||
|
if (req.method != "POST" || !req.url.includes('/repo/updated')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
req.respond(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
"Content-Type": "application/xml"
|
||||||
|
},
|
||||||
|
'<xml>'
|
||||||
|
+ '<currentTime>1630219842</currentTime>'
|
||||||
|
+ message
|
||||||
|
+ '</xml>'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
it("should show dialog if repo returns a message", async function () {
|
||||||
|
createResponseWithMessage(
|
||||||
|
`<message infoURL="https://example.com">This is a warning</message>`
|
||||||
|
);
|
||||||
|
|
||||||
|
var promise = waitForDialog(function (dialog) {
|
||||||
|
var html = dialog.document.documentElement.outerHTML;
|
||||||
|
assert.include(html, "This is a warning");
|
||||||
|
});
|
||||||
|
await Zotero.Schema.updateFromRepository(3);
|
||||||
|
await promise;
|
||||||
|
|
||||||
|
// Don't show id-less message again for a day
|
||||||
|
var spy = sinon.spy(Zotero, 'debug');
|
||||||
|
await Zotero.Schema.updateFromRepository(3);
|
||||||
|
assert.notEqual(spy.args.findIndex(x => {
|
||||||
|
return typeof x[0] == 'string' && x[0].startsWith("Not showing hidden");
|
||||||
|
}), -1);
|
||||||
|
spy.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shouldn't show message with id again for 1 day even if not hidden", async function () {
|
||||||
|
var id = Zotero.Utilities.randomString();
|
||||||
|
createResponseWithMessage(
|
||||||
|
`<message id="${id}" infoURL="https://example.com">This is a warning</message>`
|
||||||
|
);
|
||||||
|
|
||||||
|
var promise = waitForDialog();
|
||||||
|
await Zotero.Schema.updateFromRepository(3);
|
||||||
|
await promise;
|
||||||
|
|
||||||
|
// Make sure notice is hidden for 1 day
|
||||||
|
var hiddenNotices;
|
||||||
|
var tries = 0;
|
||||||
|
var ttl = 86400;
|
||||||
|
while (tries < 100) {
|
||||||
|
tries++;
|
||||||
|
hiddenNotices = Zotero.Prefs.get('hiddenNotices');
|
||||||
|
if (!hiddenNotices) {
|
||||||
|
await Zotero.Promise.delay(10);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
hiddenNotices = JSON.parse(hiddenNotices);
|
||||||
|
assert.property(hiddenNotices, id);
|
||||||
|
assert.approximately(hiddenNotices[id], Math.round(Date.now() / 1000) + ttl, 10);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shouldn't show message with id again for 30 days", async function () {
|
||||||
|
var id = Zotero.Utilities.randomString();
|
||||||
|
createResponseWithMessage(
|
||||||
|
`<message id="${id}" infoURL="https://example.com">This is a warning</message>`
|
||||||
|
);
|
||||||
|
|
||||||
|
var promise = waitForDialog(function (dialog) {
|
||||||
|
var doc = dialog.document;
|
||||||
|
var innerHTML = doc.documentElement.innerHTML;
|
||||||
|
assert.include(innerHTML, "This is a warning");
|
||||||
|
assert.include(innerHTML, Zotero.getString('general.dontShowAgainFor', 30, 30));
|
||||||
|
// Check "Don't show again"
|
||||||
|
doc.getElementById('checkbox').click();
|
||||||
|
});
|
||||||
|
await Zotero.Schema.updateFromRepository(3);
|
||||||
|
await promise;
|
||||||
|
|
||||||
|
// Make sure notice is hidden for 30 days
|
||||||
|
var hiddenNotices;
|
||||||
|
var tries = 0;
|
||||||
|
var ttl = 30 * 86400;
|
||||||
|
while (tries < 100) {
|
||||||
|
tries++;
|
||||||
|
hiddenNotices = Zotero.Prefs.get('hiddenNotices');
|
||||||
|
if (!hiddenNotices) {
|
||||||
|
await Zotero.Promise.delay(10);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
hiddenNotices = JSON.parse(hiddenNotices);
|
||||||
|
assert.property(hiddenNotices, id);
|
||||||
|
assert.approximately(hiddenNotices[id], Math.round(Date.now() / 1000) + ttl, 10);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shouldn't show message with id if before expiration", async function () {
|
||||||
|
var id = Zotero.Utilities.randomString();
|
||||||
|
createResponseWithMessage(
|
||||||
|
`<message id="${id}" infoURL="https://example.com">This is a warning</message>`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set expiration for 30 days from now
|
||||||
|
var ttl = 30 * 86400;
|
||||||
|
Zotero.Prefs.set(
|
||||||
|
'hiddenNotices',
|
||||||
|
JSON.stringify({
|
||||||
|
[id]: Math.round(Date.now() / 1000) + ttl
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Message should be hidden
|
||||||
|
var spy = sinon.spy(Zotero, 'debug');
|
||||||
|
await Zotero.Schema.updateFromRepository(3);
|
||||||
|
assert.notEqual(spy.args.findIndex(x => {
|
||||||
|
return typeof x[0] == 'string' && x[0].startsWith("Not showing hidden");
|
||||||
|
}), -1);
|
||||||
|
spy.restore();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
describe("#integrityCheck()", function () {
|
describe("#integrityCheck()", function () {
|
||||||
before(function* () {
|
before(function* () {
|
||||||
yield resetDB({
|
yield resetDB({
|
||||||
|
|
Loading…
Reference in a new issue