From d353439980e2585ef598385f6ddc5d72b80b4600 Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Tue, 11 Jun 2019 21:24:17 -0400 Subject: [PATCH] Add retractions.enabled hidden pref to disable retraction checking --- chrome/content/zotero/xpcom/retractions.js | 95 +++++++++++++++++----- defaults/preferences/zotero.js | 2 + test/tests/retractionsTest.js | 56 ++++++++++--- 3 files changed, 120 insertions(+), 33 deletions(-) diff --git a/chrome/content/zotero/xpcom/retractions.js b/chrome/content/zotero/xpcom/retractions.js index 2c21755af0..fb45f2a7fd 100644 --- a/chrome/content/zotero/xpcom/retractions.js +++ b/chrome/content/zotero/xpcom/retractions.js @@ -28,28 +28,21 @@ Zotero.Retractions = { TYPE_PMID: 'p', TYPE_NAMES: ['DOI', 'PMID'], + _prefObserverRegistered: false, _initialized: false, _version: 1, - _cacheFile: null, - _cacheVersion: null, - _cacheETag: null, - _cacheDOIPrefixLength: null, - _cachePMIDPrefixLength: null, - _cachePrefixList: new Set(), // Prefix strings from server - - _queuedItems: new Set(), - _queuedPrefixStrings: new Set(), - - _keyItems: {}, - _itemKeys: {}, - - _retractedItems: new Set(), - _retractedItemsByLibrary: {}, - _librariesWithRetractions: new Set(), - init: async function () { - this._cacheFile = OS.Path.join(Zotero.Profile.dir, 'retractions.json'); + this._resetState(); + + if (!this._prefObserverRegistered) { + Zotero.Prefs.registerObserver('retractions.enabled', this._handlePrefChange.bind(this)); + this._prefObserverRegistered = true; + } + + if (!Zotero.Prefs.get('retractions.enabled')) { + return; + } // Load mappings of keys (DOI hashes and PMIDs) to items and vice versa and register for // item changes so they can be kept up to date in notify(). @@ -89,6 +82,22 @@ Zotero.Retractions = { } }, + _resetState: function () { + this._initialized = false; + this._keyItems = {}; + this._itemKeys = {}; + this._queuedItems = new Set(); + this._queuedPrefixStrings = new Set(); + this._retractedItems = new Set(); + this._retractedItemsByLibrary = {}; + this._librariesWithRetractions = new Set(); + this._cacheVersion = null; + this._cacheETag = null; + this._cacheDOIPrefixLength = null; + this._cachePMIDPrefixLength = null; + this._cachePrefixList = new Set(); + }, + /** * @param {Zotero.Item} * @return {Boolean} @@ -193,6 +202,11 @@ Zotero.Retractions = { }, notify: async function (action, type, ids, extraData) { + // The observer is removed when disabled but something might already be in progress + if (!this._initialized) { + return; + } + // Clean up cache on group deletion if (action == 'delete' && type == 'group') { for (let libraryID of ids) { @@ -646,11 +660,29 @@ Zotero.Retractions = { await Zotero.Notifier.trigger('refresh', 'item', [itemID]); }, - _loadCacheFile: async function () { - if (!await OS.File.exists(this._cacheFile)) { + _removeAllEntries: async function () { + var libraryIDs = await Zotero.DB.columnQueryAsync( + "SELECT libraryID FROM items WHERE itemID IN (SELECT itemID FROM retractedItems)" + ); + var itemIDs = await Zotero.DB.columnQueryAsync("SELECT itemID FROM retractedItems"); + if (!itemIDs.length) { return; } - var data = JSON.parse(await Zotero.File.getContentsAsync(this._cacheFile)); + await Zotero.DB.queryAsync("DELETE FROM retractedItems"); + this._retractedItems.clear(); + this._retractedItemsByLibrary = {}; + for (let libraryID of libraryIDs) { + await this._updateLibraryRetractions(libraryID); + } + await Zotero.Notifier.trigger('refresh', 'item', itemIDs); + }, + + _loadCacheFile: async function () { + var cacheFile = OS.Path.join(Zotero.Profile.dir, 'retractions.json'); + if (!await OS.File.exists(cacheFile)) { + return; + } + var data = JSON.parse(await Zotero.File.getContentsAsync(cacheFile)); if (data) { this._processCacheData(data); } @@ -680,6 +712,7 @@ Zotero.Retractions = { * Cache prefix list in profile directory */ _saveCacheFile: async function (data, etag, doiPrefixLength, pmidPrefixLength) { + var cacheFile = OS.Path.join(Zotero.Profile.dir, 'retractions.json'); var cacheJSON = { version: this._version, etag, @@ -688,7 +721,7 @@ Zotero.Retractions = { data }; try { - await Zotero.File.putContentsAsync(this._cacheFile, JSON.stringify(cacheJSON)); + await Zotero.File.putContentsAsync(cacheFile, JSON.stringify(cacheJSON)); this._processCacheData(cacheJSON); } catch (e) { @@ -705,6 +738,24 @@ Zotero.Retractions = { return url; }, + _handlePrefChange: async function () { + // Enable + if (Zotero.Prefs.get('retractions.enabled')) { + await this.init(); + } + // Disable + else { + if (this._notifierID) { + Zotero.Notifier.unregisterObserver(this._notifierID); + delete this._notifierID; + } + await this._removeAllEntries(); + this._resetState(); + let cacheFile = OS.Path.join(Zotero.Profile.dir, 'retractions.json'); + await OS.File.remove(cacheFile); + } + }, + // https://retractionwatch.com/retraction-watch-database-user-guide/retraction-watch-database-user-guide-appendix-b-reasons/ _reasonDescriptions: { "Author Unresponsive": "Author(s) lack of communication after prior contact by Journal, Publisher or other original Authors", diff --git a/defaults/preferences/zotero.js b/defaults/preferences/zotero.js index daf63cdf50..9093e2f77e 100644 --- a/defaults/preferences/zotero.js +++ b/defaults/preferences/zotero.js @@ -192,4 +192,6 @@ pref("extensions.zotero.translators.supplementaryAsLink", false); pref("extensions.zotero.translators.RIS.import.ignoreUnknown", true); pref("extensions.zotero.translators.RIS.import.keepID", false); +// Retracted Items +pref("extensions.zotero.retractions.enabled", true); pref("extensions.zotero.retractions.recentItems", "[]"); diff --git a/test/tests/retractionsTest.js b/test/tests/retractionsTest.js index 358bd87090..6b0c712b16 100644 --- a/test/tests/retractionsTest.js +++ b/test/tests/retractionsTest.js @@ -62,18 +62,18 @@ describe("Retractions", function() { return item; } - describe("Notification Banner", function () { - function bannerShown() { - var container = win.document.getElementById('retracted-items-container'); - if (container.getAttribute('collapsed') == 'true') { - return false; - } - if (!container.hasAttribute('collapsed')) { - return true; - } - throw new Error("'collapsed' attribute not found"); + function bannerShown() { + var container = win.document.getElementById('retracted-items-container'); + if (container.getAttribute('collapsed') == 'true') { + return false; } - + if (!container.hasAttribute('collapsed')) { + return true; + } + throw new Error("'collapsed' attribute not found"); + } + + describe("Notification Banner", function () { it("should show banner when retracted item is added", async function () { var banner = win.document.getElementById('retracted-items-container'); assert.isFalse(bannerShown()); @@ -158,4 +158,38 @@ describe("Retractions", function() { assert.ok(zp.collectionsView.getRowIndexByID("R" + userLibraryID)); }); }); + + describe("retractions.enabled", function () { + beforeEach(function () { + Zotero.Prefs.clear('retractions.enabled'); + }); + + it("should hide virtual collection and banner when false", async function () { + var item = await createRetractedItem(); + await Zotero.Promise.delay(50); + var itemRetractionBox = win.document.getElementById('retraction-box'); + assert.isFalse(itemRetractionBox.hidden); + + var spies = [ + sinon.spy(Zotero.Retractions, '_removeAllEntries'), + sinon.spy(Zotero.Retractions, 'getData') + ]; + Zotero.Prefs.set('retractions.enabled', false); + + while (!spies[0].called || !spies[1].called) { + await Zotero.Promise.delay(50); + } + await spies[0].returnValues[0]; + await spies[1].returnValues[0] + spies.forEach(spy => spy.restore()); + + assert.isFalse(Zotero.Retractions.isRetracted(item)); + assert.isFalse(zp.collectionsView.getRowIndexByID("R" + userLibraryID)); + assert.isFalse(bannerShown()); + + assert.isTrue(itemRetractionBox.hidden); + + await item.eraseTx(); + }); + }); }); \ No newline at end of file