diff --git a/chrome/content/zotero/xpcom/recognizePDF.js b/chrome/content/zotero/xpcom/recognizePDF.js
index b691a829b..87d20794d 100644
--- a/chrome/content/zotero/xpcom/recognizePDF.js
+++ b/chrome/content/zotero/xpcom/recognizePDF.js
@@ -26,12 +26,15 @@
Zotero.RecognizePDF = new function () {
const OFFLINE_RECHECK_DELAY = 60 * 1000;
const MAX_PAGES = 5;
+ const UNRECOGNIZE_TIMEOUT = 3600 * 1000;
this.ROW_QUEUED = 1;
this.ROW_PROCESSING = 2;
this.ROW_FAILED = 3;
this.ROW_SUCCEEDED = 4;
+ let _newItems = new WeakMap();
+
let _listeners = {};
let _rows = [];
let _queue = [];
@@ -130,6 +133,72 @@ Zotero.RecognizePDF = new function () {
}
};
+
+ this.canUnrecognize = function (item) {
+ var threshold = UNRECOGNIZE_TIMEOUT;
+ var added = _newItems.get(item);
+ // Item must have been recognized recently, must not have been modified since it was
+ // created, and must have only one attachment and no other children
+ if (!added
+ || Zotero.Date.sqlToDate(added, true) < new Date() - threshold
+ || item.dateModified != added
+ || item.numAttachments(true) != 1
+ || item.numChildren(true) != 1) {
+ _newItems.delete(item);
+ return false;
+ }
+
+ // Child attachment must be not be in trash and must be a PDF
+ var attachments = Zotero.Items.get(item.getAttachments());
+ if (!attachments.length || attachments[0].attachmentContentType != 'application/pdf') {
+ _newItems.delete(item);
+ return false;
+ }
+
+ return true;
+ };
+
+
+ this.unrecognize = async function (item) {
+ var attachment = Zotero.Items.get(item.getAttachments()[0]);
+ return Zotero.DB.executeTransaction(async function () {
+ let collections = item.getCollections();
+ attachment.parentItemID = null
+ attachment.setCollections(collections);
+ await attachment.save();
+
+ await item.erase();
+ }.bind(this));
+ };
+
+
+ this.report = async function (item) {
+ var attachment = Zotero.Items.get(item.getAttachments()[0]);
+ var filePath = await attachment.getFilePath();
+ if (!filePath || !await OS.File.exists(filePath)) {
+ throw new Error("File not found when reporting metadata");
+ }
+
+ var version = Zotero.version;
+ var json = await extractJSON(filePath, MAX_PAGES);
+ var metadata = item.toJSON();
+
+ var data = { version, json, metadata };
+ var uri = ZOTERO_CONFIG.RECOGNIZE_URL + 'report';
+ return Zotero.HTTP.request(
+ "POST",
+ uri,
+ {
+ successCodes: [200, 204],
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(data)
+ }
+ );
+ };
+
+
/**
* Add item for processing
* @param item
@@ -301,6 +370,7 @@ Zotero.RecognizePDF = new function () {
await attachment.saveTx();
}
+ _newItems.set(parentItem, parentItem.dateModified);
return parentItem;
}
diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js
index e41b56855..1bbde4e94 100644
--- a/chrome/content/zotero/zoteroPane.js
+++ b/chrome/content/zotero/zoteroPane.js
@@ -2733,6 +2733,8 @@ var ZoteroPane = new function()
'loadReport',
'sep4',
'recognizePDF',
+ 'unrecognize',
+ 'reportMetadata',
'createParent',
'renameAttachments',
'reindexItem'
@@ -2796,6 +2798,10 @@ var ZoteroPane = new function()
canRecognize = false;
}
+ if (canUnrecognize && !Zotero.RecognizePDF.canUnrecognize(item)) {
+ canUnrecognize = false;
+ }
+
// Show rename option only if all items are child attachments
if (canRename && (!item.isAttachment() || item.isTopLevelItem() || item.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL)) {
canRename = false;
@@ -2818,6 +2824,10 @@ var ZoteroPane = new function()
show.push(m.recognizePDF);
}
+ if (canUnrecognize) {
+ show.push(m.unrecognize);
+ }
+
if (canMarkRead) {
show.push(m.toggleRead);
if (markUnread) {
@@ -2844,7 +2854,7 @@ var ZoteroPane = new function()
}
// Add in attachment separator
- if (canCreateParent || canRecognize || canRename || canIndex) {
+ if (canCreateParent || canRecognize || canUnrecognize || canRename || canIndex) {
show.push(m.sep4);
}
@@ -2882,6 +2892,10 @@ var ZoteroPane = new function()
show.push(m.addNote, m.addAttachments, m.sep2);
}
+ if (Zotero.RecognizePDF.canUnrecognize(item)) {
+ show.push(m.sep4, m.unrecognize, m.reportMetadata);
+ }
+
if (item.isAttachment()) {
var showSep4 = false;
@@ -4556,6 +4570,45 @@ var ZoteroPane = new function()
};
+ this.unrecognizeSelected = async function () {
+ var items = ZoteroPane.getSelectedItems();
+ for (let item of items) {
+ await Zotero.RecognizePDF.unrecognize(item);
+ }
+ };
+
+
+ this.reportMetadataForSelected = async function () {
+ var success = false;
+ var items = ZoteroPane.getSelectedItems();
+ for (let item of items) {
+ try {
+ await Zotero.RecognizePDF.report(item);
+ // If at least one report was submitted, show as success
+ success = true;
+ }
+ catch (e) {
+ Zotero.logError(e);
+ }
+ }
+
+ if (success) {
+ Zotero.alert(
+ window,
+ Zotero.getString('general.submitted'),
+ Zotero.getString('general.thanksForHelpingImprove', Zotero.clientName)
+ );
+ }
+ else {
+ Zotero.alert(
+ window,
+ Zotero.getString('general.error'),
+ Zotero.getString('general.invalidResponseServer')
+ );
+ }
+ };
+
+
this.createParentItemsFromSelected = Zotero.Promise.coroutine(function* () {
if (!this.canEdit()) {
this.displayCannotEditLibraryMessage();
diff --git a/chrome/content/zotero/zoteroPane.xul b/chrome/content/zotero/zoteroPane.xul
index e02d0d624..5d3734db2 100644
--- a/chrome/content/zotero/zoteroPane.xul
+++ b/chrome/content/zotero/zoteroPane.xul
@@ -327,6 +327,8 @@
+
+
diff --git a/chrome/locale/en-US/zotero/zotero.dtd b/chrome/locale/en-US/zotero/zotero.dtd
index 120dec511..29b1a419f 100644
--- a/chrome/locale/en-US/zotero/zotero.dtd
+++ b/chrome/locale/en-US/zotero/zotero.dtd
@@ -99,6 +99,8 @@
+
+
diff --git a/chrome/locale/en-US/zotero/zotero.properties b/chrome/locale/en-US/zotero/zotero.properties
index dbbd18feb..e882d18ad 100644
--- a/chrome/locale/en-US/zotero/zotero.properties
+++ b/chrome/locale/en-US/zotero/zotero.properties
@@ -66,6 +66,8 @@ general.copyToClipboard = Copy to Clipboard
general.cancel = Cancel
general.clear = Clear
general.processing = Processing
+general.submitted = Submitted
+general.thanksForHelpingImprove = Thanks for helping to improve %S!
general.operationInProgress = A Zotero operation is currently in progress.
general.operationInProgress.waitUntilFinished = Please wait until it has finished.