diff --git a/chrome/content/zotero/preferences/preferences_general.xul b/chrome/content/zotero/preferences/preferences_general.xul index fc0c087e8f..c908652c30 100644 --- a/chrome/content/zotero/preferences/preferences_general.xul +++ b/chrome/content/zotero/preferences/preferences_general.xul @@ -37,6 +37,7 @@ + @@ -123,6 +124,7 @@ label="&zotero.preferences.automaticSnapshots;" preference="pref-automaticSnapshots"/> + diff --git a/chrome/content/zotero/xpcom/attachments.js b/chrome/content/zotero/xpcom/attachments.js index c50cfdd0e3..10b74373d4 100644 --- a/chrome/content/zotero/xpcom/attachments.js +++ b/chrome/content/zotero/xpcom/attachments.js @@ -257,7 +257,7 @@ Zotero.Attachments = new function(){ * @param {String} [options.referrer] * @param {CookieSandbox} [options.cookieSandbox] * @param {Object} [options.saveOptions] - * @return {Promise} - A promise for the created attachment item + * @return {Promise} - A promise for the created attachment item */ this.importFromURL = Zotero.Promise.coroutine(function* (options) { var libraryID = options.libraryID; @@ -298,7 +298,7 @@ Zotero.Attachments = new function(){ if (channel.responseStatus < 200 || channel.responseStatus >= 400) { reject(new Error("Invalid response " + channel.responseStatus + " " + channel.responseStatusText + " for '" + url + "'")); - return; + return false; } } try { diff --git a/chrome/content/zotero/xpcom/collectionTreeView.js b/chrome/content/zotero/xpcom/collectionTreeView.js index 7cb521b663..0c18c10c5e 100644 --- a/chrome/content/zotero/xpcom/collectionTreeView.js +++ b/chrome/content/zotero/xpcom/collectionTreeView.js @@ -2242,19 +2242,20 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r } else if (dataType == 'text/x-moz-url' || dataType == 'application/x-moz-file') { var targetLibraryID = targetTreeRow.ref.libraryID; - if (targetTreeRow.isCollection()) { var parentCollectionID = targetTreeRow.ref.id; } else { var parentCollectionID = false; } + var addedItems = []; for (var i=0; i { + return item + && item.isFileAttachment() + && item.attachmentContentType == 'application/pdf'; + }); + if (!pdfs.length) { + return; + } + this.recognizeItems(pdfs); + let pane = Zotero.getActiveZoteroPane(); + if (pane) { + Zotero_RecognizePDF_Dialog.open(); + } + }; + /** * Returns all rows * @return {Array} diff --git a/chrome/content/zotero/xpcom/server_connector.js b/chrome/content/zotero/xpcom/server_connector.js index 5fd0b2fd76..c78f77b43d 100644 --- a/chrome/content/zotero/xpcom/server_connector.js +++ b/chrome/content/zotero/xpcom/server_connector.js @@ -649,7 +649,13 @@ Zotero.Server.Connector.SaveSnapshot.prototype = { contentType: "application/pdf", cookieSandbox }); - yield session.addItem(item); + if (item) { + yield session.addItem(item); + + // Automatically recognize PDF + Zotero.RecognizePDF.autoRecognizeItems([item]); + } + return 201; } catch (e) { diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js index 67f272a6d1..8ae802f58e 100644 --- a/chrome/content/zotero/zoteroPane.js +++ b/chrome/content/zotero/zoteroPane.js @@ -85,6 +85,7 @@ var ZoteroPane = new function() // Set key down handler document.getElementById('appcontent').addEventListener('keydown', ZoteroPane_Local.handleKeyDown, true); + // Hide or show the PDF recognizer button Zotero.RecognizePDF.addListener('empty', function (row) { document.getElementById('zotero-tb-recognize').hidden = true; }); @@ -3692,6 +3693,7 @@ var ZoteroPane = new function() files.push(file.path); } + var addedItems = []; var collection; var fileBaseName; if (parentItemID) { @@ -3713,6 +3715,8 @@ var ZoteroPane = new function() } for (let file of files) { + let item; + if (link) { // Rename linked file, with unique suffix if necessary try { @@ -3733,7 +3737,7 @@ var ZoteroPane = new function() Zotero.logError(e); } - let item = yield Zotero.Attachments.linkFromFile({ + item = yield Zotero.Attachments.linkFromFile({ file, parentItemID, collections: collection ? [collection] : undefined @@ -3746,7 +3750,7 @@ var ZoteroPane = new function() continue; } - yield Zotero.Attachments.importFromFile({ + item = yield Zotero.Attachments.importFromFile({ file, libraryID, fileBaseName, @@ -3754,6 +3758,13 @@ var ZoteroPane = new function() collections: collection ? [collection] : undefined }); } + + addedItems.push(item); + } + + // Automatically retrieve metadata for top-level PDFs + if (!parentItemID) { + Zotero.RecognizePDF.autoRecognizeItems(addedItems); } }); @@ -3917,6 +3928,9 @@ var ZoteroPane = new function() }); + /** + * @return {Zotero.Item|false} - The saved item, or false if item can't be saved + */ this.addItemFromURL = Zotero.Promise.coroutine(function* (url, itemType, saveSnapshot, row) { if (window.content && url == window.content.document.location.href) { return this.addItemFromPage(itemType, saveSnapshot, row); @@ -3932,8 +3946,8 @@ var ZoteroPane = new function() var processor = function (doc) { return ZoteroPane_Local.addItemFromDocument(doc, itemType, saveSnapshot, row) - .then(function () { - deferred.resolve() + .then(function (item) { + deferred.resolve(item) }); }; var done = function () {} @@ -3966,7 +3980,7 @@ var ZoteroPane = new function() if (!ZoteroPane_Local.canEdit(row)) { ZoteroPane_Local.displayCannotEditLibraryMessage(); - return; + return false; } if (row !== undefined) { @@ -3983,7 +3997,7 @@ var ZoteroPane = new function() if (!ZoteroPane_Local.canEditFiles(row)) { ZoteroPane_Local.displayCannotEditLibraryFilesMessage(); - return; + return false; } if (collectionTreeRow && collectionTreeRow.isCollection()) { @@ -4000,7 +4014,7 @@ var ZoteroPane = new function() contentType: mimeType }); this.selectItem(attachmentItem.id) - return; + return attachmentItem; } } @@ -4033,7 +4047,7 @@ var ZoteroPane = new function() } } - return item.id; + return item; } }); @@ -4521,6 +4535,12 @@ var ZoteroPane = new function() }; + this.recognizeSelected = function() { + Zotero.RecognizePDF.recognizeItems(ZoteroPane.getSelectedItems()); + Zotero_RecognizePDF_Dialog.open(); + }; + + this.createParentItemsFromSelected = Zotero.Promise.coroutine(function* () { if (!this.canEdit()) { this.displayCannotEditLibraryMessage(); @@ -4954,11 +4974,6 @@ var ZoteroPane = new function() if(_beforeReloadFunctions.indexOf(func) === -1) _beforeReloadFunctions.push(func); } - this.recognizeSelected = function() { - Zotero.RecognizePDF.recognizeItems(ZoteroPane.getSelectedItems()); - Zotero_RecognizePDF_Dialog.open(); - }; - /** * Implements nsIObserver for Zotero reload */ diff --git a/chrome/locale/en-US/zotero/preferences.dtd b/chrome/locale/en-US/zotero/preferences.dtd index 9bb38feeac..b8dd7afb3c 100644 --- a/chrome/locale/en-US/zotero/preferences.dtd +++ b/chrome/locale/en-US/zotero/preferences.dtd @@ -27,6 +27,7 @@ + diff --git a/defaults/preferences/zotero.js b/defaults/preferences/zotero.js index 38760ba7f4..9a6cc70c16 100644 --- a/defaults/preferences/zotero.js +++ b/defaults/preferences/zotero.js @@ -35,6 +35,7 @@ pref("extensions.zotero.automaticTags",true); pref("extensions.zotero.fontSize", "1.0"); pref("extensions.zotero.layout", "standard"); pref("extensions.zotero.recursiveCollections", false); +pref("extensions.zotero.autoRecognizeFiles", true); pref("extensions.zotero.renameAttachmentFiles.automatic", true); pref("extensions.zotero.renameAttachmentFiles.automatic.fileTypes", "application/pdf"); pref("extensions.zotero.attachmentRenameFormatString", '{%c - }{%y - }{%t{50}}'); diff --git a/test/content/support.js b/test/content/support.js index e1c9efadcd..cba1158c07 100644 --- a/test/content/support.js +++ b/test/content/support.js @@ -19,6 +19,16 @@ function waitForDOMEvent(target, event, capture) { return deferred.promise; } +async function waitForRecognizer() { + var win = await waitForWindow('chrome://zotero/content/recognizePDFDialog.xul') + // Wait for status to show as complete + var completeStr = Zotero.getString("recognizePDF.complete.label"); + while (win.document.getElementById("label").value != completeStr) { + await Zotero.Promise.delay(20); + } + return win; +} + /** * Open a chrome window and return a promise for the window * diff --git a/test/tests/itemTreeViewTest.js b/test/tests/itemTreeViewTest.js index ca485efb49..d7634dc2c4 100644 --- a/test/tests/itemTreeViewTest.js +++ b/test/tests/itemTreeViewTest.js @@ -695,11 +695,11 @@ describe("Zotero.ItemTreeView", function() { file.append(pdfFilename); pdfPath = file.path; httpd.registerFile("/" + pdfFilename, file); - - Zotero.Prefs.clear('renameAttachmentFiles.automatic'); }); - afterEach(() => { + beforeEach(() => { + // Don't run recognize on every file + Zotero.Prefs.set('autoRecognizeFiles', false); Zotero.Prefs.clear('renameAttachmentFiles.automatic'); }); @@ -707,6 +707,9 @@ describe("Zotero.ItemTreeView", function() { var defer = new Zotero.Promise.defer(); httpd.stop(() => defer.resolve()); yield defer.promise; + + Zotero.Prefs.clear('autoRecognizeFiles'); + Zotero.Prefs.clear('renameAttachmentFiles.automatic'); }); it("should move a child item from one item to another", function* () { @@ -879,6 +882,62 @@ describe("Zotero.ItemTreeView", function() { ); }); + it("should automatically retrieve metadata for top-level PDF if pref is enabled", async function () { + Zotero.Prefs.set('autoRecognizeFiles', true); + + var view = zp.itemsView; + + var promise = waitForItemEvent('add'); + var recognizerPromise = waitForRecognizer(); + + // Fake recognizer response + Zotero.HTTP.mock = sinon.FakeXMLHttpRequest; + var server = sinon.fakeServer.create(); + server.autoRespond = true; + setHTTPResponse( + server, + ZOTERO_CONFIG.RECOGNIZE_URL, + { + method: 'POST', + url: 'recognize', + status: 200, + headers: { + 'Content-Type': 'application/json' + }, + json: { + title: 'Test', + authors: [] + } + } + ); + + itemsView.drop(0, -1, { + dropEffect: 'copy', + effectAllowed: 'copy', + types: { + contains: function (type) { + return type == 'text/x-moz-url'; + } + }, + getData: function (type) { + if (type == 'text/x-moz-url') { + return pdfURL; + } + }, + mozItemCount: 1, + }) + + var itemIDs = await promise; + var item = Zotero.Items.get(itemIDs[0]); + + var progressWindow = await recognizerPromise; + progressWindow.close(); + Zotero.RecognizePDF.cancel(); + assert.isFalse(item.isTopLevelItem()); + + Zotero.HTTP.mock = null; + }); + it("should rename a stored child attachment using parent metadata if no existing file attachments and pref enabled", async function () { var view = zp.itemsView; var parentTitle = Zotero.Utilities.randomString(); diff --git a/test/tests/server_connectorTest.js b/test/tests/server_connectorTest.js index ab15218545..84975d7751 100644 --- a/test/tests/server_connectorTest.js +++ b/test/tests/server_connectorTest.js @@ -305,17 +305,36 @@ describe("Connector Server", function () { assert.equal(item.getField('title'), 'Title'); }); - it("should save a PDF to the current selected collection", function* () { - var collection = yield createDataObject('collection'); - yield waitForItemsLoad(win); + it("should save a PDF to the current selected collection and retrieve metadata", async function () { + var collection = await createDataObject('collection'); + await waitForItemsLoad(win); var file = getTestDataDirectory(); file.append('test.pdf'); httpd.registerFile("/test.pdf", file); - var ids; var promise = waitForItemEvent('add'); - yield Zotero.HTTP.request( + var recognizerPromise = waitForRecognizer(); + + var origRequest = Zotero.HTTP.request.bind(Zotero.HTTP); + var called = 0; + var stub = sinon.stub(Zotero.HTTP, 'request').callsFake(function (method, url, options) { + // Forward saveSnapshot request + if (url.endsWith('saveSnapshot')) { + return origRequest(...arguments); + } + + // Fake recognizer response + return Zotero.Promise.resolve({ + getResponseHeader: () => {}, + responseText: JSON.stringify({ + title: 'Test', + authors: [] + }) + }); + }); + + await Zotero.HTTP.request( 'POST', connectorServerPath + "/connector/saveSnapshot", { @@ -329,13 +348,20 @@ describe("Connector Server", function () { } ); - var ids = yield promise; + var ids = await promise; assert.lengthOf(ids, 1); var item = Zotero.Items.get(ids[0]); assert.isTrue(item.isImportedAttachment()); assert.equal(item.attachmentContentType, 'application/pdf'); assert.isTrue(collection.hasItem(item.id)); + + var progressWindow = await recognizerPromise; + progressWindow.close(); + Zotero.RecognizePDF.cancel(); + assert.isFalse(item.isTopLevelItem()); + + stub.restore(); }); it("should respond with 500 if a read-only library is selected", function* () {