diff --git a/chrome/content/zotero/xpcom/retractions.js b/chrome/content/zotero/xpcom/retractions.js index 32e4d561b9..a6de86184f 100644 --- a/chrome/content/zotero/xpcom/retractions.js +++ b/chrome/content/zotero/xpcom/retractions.js @@ -911,7 +911,9 @@ Zotero.Retractions = { delete o.retractionDOI; delete o.retractionPMID; - var sql = "REPLACE INTO retractedItems (itemID, data) VALUES (?, ?)"; + // Leave the flag alone, so a hidden retraction stays hidden across refreshes + var sql = "INSERT INTO retractedItems (itemID, data) VALUES (?, ?) " + + "ON CONFLICT(itemID) DO UPDATE SET data=excluded.data"; await Zotero.DB.queryAsync(sql, [itemID, JSON.stringify(o)]); var item = await Zotero.Items.getAsync(itemID); diff --git a/test/tests/retractionsTest.js b/test/tests/retractionsTest.js index a575bd54d7..c3b0a61abc 100644 --- a/test/tests/retractionsTest.js +++ b/test/tests/retractionsTest.js @@ -257,6 +257,60 @@ describe("Retractions", function () { Zotero.Retractions._version = version; } }); + + it("should keep a hidden retraction hidden across an update", async function () { + var doi = '10.1234/defgh'; + var hash = Zotero.Utilities.Internal.sha1(doi); + var line = Zotero.Retractions.TYPE_DOI + hash.substr(0, 5) + ' 12345\n'; + var etags = ['first', 'second']; + + server.respond(function (req) { + if (req.method == 'GET' && req.url == baseURL + 'list') { + req.respond( + 200, + { 'Content-Type': 'text/plain', 'ETag': etags.shift() || 'second' }, + line + ); + } + else if (req.method == 'POST' && req.url == baseURL + 'search') { + req.respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify([ + { + doi: hash, + retractionDOI: '10.1234/efghi', + date: '2019-01-02', + reasons: ["Error in Data"], + urls: [] + } + ]) + ); + } + }); + + await Zotero.Retractions.updateFromServer(); + + let promise = waitForItemEvent('refresh'); + let item = createUnsavedDataObject('item', { itemType: 'journalArticle' }); + item.setField('DOI', doi); + await item.saveTx(); + await promise; + assert.isTrue(Zotero.Retractions.isRetracted(item)); + + await Zotero.Retractions.hideRetraction(item); + assert.isFalse(Zotero.Retractions.isRetracted(item)); + + // The stored flag is what survives a restart, so it has to outlast an update + await Zotero.Retractions.updateFromServer(); + assert.isFalse(Zotero.Retractions.isRetracted(item)); + assert.equal( + await Zotero.DB.valueQueryAsync( + "SELECT flag FROM retractedItems WHERE itemID=?", item.id + ), + Zotero.Retractions.FLAG_HIDDEN + ); + }); });