diff --git a/chrome/content/zotero/collectionTree.jsx b/chrome/content/zotero/collectionTree.jsx index de84ca5269..16d2cd1122 100644 --- a/chrome/content/zotero/collectionTree.jsx +++ b/chrome/content/zotero/collectionTree.jsx @@ -1540,7 +1540,8 @@ var CollectionTree = class CollectionTree extends LibraryTree { tags: Zotero.Prefs.get('groups.copyTags'), childNotes: Zotero.Prefs.get('groups.copyChildNotes'), childLinks: Zotero.Prefs.get('groups.copyChildLinks'), - childFileAttachments: Zotero.Prefs.get('groups.copyChildFileAttachments') + childFileAttachments: Zotero.Prefs.get('groups.copyChildFileAttachments'), + annotations: Zotero.Prefs.get('groups.copyAnnotations'), }; var copyItem = async function (item, targetLibraryID, options) { var targetLibraryType = Zotero.Libraries.get(targetLibraryID).libraryType; @@ -1612,7 +1613,12 @@ var CollectionTree = class CollectionTree extends LibraryTree { return false; } - return Zotero.Attachments.copyAttachmentToLibrary(item, targetLibraryID); + let newAttachment = Zotero.Attachments.copyAttachmentToLibrary(item, targetLibraryID); + if (options.annotations) { + await Zotero.Items.copyChildItems(item, newAttachment); + } + + return newAttachment.id; } // Create new clone item in target library @@ -1675,7 +1681,13 @@ var CollectionTree = class CollectionTree extends LibraryTree { continue; } } - await Zotero.Attachments.copyAttachmentToLibrary(attachment, targetLibraryID, newItemID); + let newAttachment = await Zotero.Attachments.copyAttachmentToLibrary( + attachment, targetLibraryID, newItemID + ); + + if (options.annotations) { + await Zotero.Items.copyChildItems(attachment, newAttachment); + } } } @@ -1804,6 +1816,7 @@ var CollectionTree = class CollectionTree extends LibraryTree { copyOptions.childNotes = io.includeNotes; copyOptions.childFileAttachments = io.includeFiles; copyOptions.childLinks = true; + copyOptions.annotations = false; ['keepRights', 'license', 'licenseName'].forEach(function (field) { copyOptions[field] = io[field]; }); diff --git a/chrome/content/zotero/preferences/preferences_general.xul b/chrome/content/zotero/preferences/preferences_general.xul index 4e5c402420..289aaf2fab 100644 --- a/chrome/content/zotero/preferences/preferences_general.xul +++ b/chrome/content/zotero/preferences/preferences_general.xul @@ -42,6 +42,7 @@ + @@ -96,6 +97,7 @@ + diff --git a/chrome/content/zotero/xpcom/attachments.js b/chrome/content/zotero/xpcom/attachments.js index 433cfc11e2..196cef2b69 100644 --- a/chrome/content/zotero/xpcom/attachments.js +++ b/chrome/content/zotero/xpcom/attachments.js @@ -2682,6 +2682,8 @@ Zotero.Attachments = new function(){ /** * Copy attachment item, including file, to another library + * + * @return {Zotero.Item} - The new attachment */ this.copyAttachmentToLibrary = Zotero.Promise.coroutine(function* (attachment, libraryID, parentItemID) { if (attachment.libraryID == libraryID) { @@ -2708,7 +2710,7 @@ Zotero.Attachments = new function(){ } yield newAttachment.addLinkedItem(attachment); - return newAttachment.id; + return newAttachment; }); diff --git a/chrome/content/zotero/xpcom/data/items.js b/chrome/content/zotero/xpcom/data/items.js index 85906cd5fb..3dc94a5b33 100644 --- a/chrome/content/zotero/xpcom/data/items.js +++ b/chrome/content/zotero/xpcom/data/items.js @@ -882,6 +882,36 @@ Zotero.Items = function() { }); + /** + * Copy child items from one item to another (e.g., in another library) + * + * Requires a transaction + */ + this.copyChildItems = async function (fromItem, toItem) { + Zotero.DB.requireTransaction(); + + var fromGroup = fromItem.library.isGroup; + + // Annotations on files + if (fromItem.isFileAttachment()) { + let annotations = fromItem.getAnnotations(); + for (let annotation of annotations) { + let newAnnotation = annotation.clone(toItem.libraryID); + newAnnotation.parentItemID = toItem.id; + // If there's no explicit author and we're copying from a group, set the author + // to the creating user + if (!annotation.annotationAuthorName && fromGroup) { + newAnnotation.annotationAuthorName = + Zotero.Users.getName(annotation.createdByUserID); + } + await newAnnotation.save(); + } + } + + // TODO: Other things as necessary + }; + + /** * Move child items from one item to another * diff --git a/chrome/locale/en-US/zotero/preferences.dtd b/chrome/locale/en-US/zotero/preferences.dtd index 540f8297d7..fb20dd2fa7 100644 --- a/chrome/locale/en-US/zotero/preferences.dtd +++ b/chrome/locale/en-US/zotero/preferences.dtd @@ -30,6 +30,7 @@ + diff --git a/defaults/preferences/zotero.js b/defaults/preferences/zotero.js index 8e011d74e5..832db2deb6 100644 --- a/defaults/preferences/zotero.js +++ b/defaults/preferences/zotero.js @@ -51,6 +51,7 @@ pref("extensions.zotero.reopenPanesOnRestart", true); pref("extensions.zotero.groups.copyChildLinks", true); pref("extensions.zotero.groups.copyChildFileAttachments", true); +pref("extensions.zotero.groups.copyAnnotations", true); pref("extensions.zotero.groups.copyChildNotes", true); pref("extensions.zotero.groups.copyTags", true); diff --git a/test/tests/collectionTreeTest.js b/test/tests/collectionTreeTest.js index 717db9ea17..8d095813bd 100644 --- a/test/tests/collectionTreeTest.js +++ b/test/tests/collectionTreeTest.js @@ -814,21 +814,22 @@ describe("Zotero.CollectionTree", function() { }); }); - it("should copy an item with an attachment to a group", function* () { - var group = yield createGroup(); + it("should copy an item with a PDF attachment containing annotations to a group", async function () { + var group = await createGroup(); - var item = yield createDataObject('item', false, { skipSelect: true }); + var item = await createDataObject('item', false, { skipSelect: true }); var file = getTestDataDirectory(); - file.append('test.png'); - var attachment = yield Zotero.Attachments.importFromFile({ - file: file, + file.append('test.pdf'); + var attachment = await Zotero.Attachments.importFromFile({ + file, parentItemID: item.id }); + var annotation = await createAnnotation('highlight', attachment); - var ids = (yield onDrop('item', 'L' + group.libraryID, [item.id])).ids; + var ids = (await onDrop('item', 'L' + group.libraryID, [item.id])).ids; - yield cv.selectLibrary(group.libraryID); - yield waitForItemsLoad(win); + await cv.selectLibrary(group.libraryID); + await waitForItemsLoad(win); // Check parent var itemsView = win.ZoteroPane.itemsView; @@ -837,7 +838,7 @@ describe("Zotero.CollectionTree", function() { assert.equal(treeRow.ref.libraryID, group.libraryID); assert.equal(treeRow.ref.id, ids[0]); // New item should link back to original - var linked = yield item.getLinkedItem(group.libraryID); + var linked = await item.getLinkedItem(group.libraryID); assert.equal(linked.id, treeRow.ref.id); // Check attachment @@ -847,11 +848,44 @@ describe("Zotero.CollectionTree", function() { treeRow = itemsView.getRow(1); assert.equal(treeRow.ref.id, ids[1]); // New attachment should link back to original - linked = yield attachment.getLinkedItem(group.libraryID); + linked = await attachment.getLinkedItem(group.libraryID); assert.equal(linked.id, treeRow.ref.id); + // Check annotation + var groupAttachment = Zotero.Items.get(treeRow.ref.id); + var annotations = groupAttachment.getAnnotations(); + assert.lengthOf(annotations, 1); + return group.eraseTx(); - }) + }); + + it("should copy a group item with a PDF attachment containing annotations to the personal library", async function () { + var group = await createGroup(); + await cv.selectLibrary(group.libraryID); + + var groupItem = await createDataObject('item', { libraryID: group.libraryID }); + var file = getTestDataDirectory(); + file.append('test.pdf'); + var attachment = await Zotero.Attachments.importFromFile({ + file, + parentItemID: groupItem.id + }); + var annotation = await createAnnotation('highlight', attachment); + await Zotero.Users.setName(12345, 'Name'); + annotation.createdByUserID = 12345; + + var ids = (await onDrop('item', 'L1', [groupItem.id])).ids; + var newItem = Zotero.Items.get(ids[0]); + + var newAttachment = Zotero.Items.get(newItem.getAttachments())[0]; + + // Check annotation + var annotations = newAttachment.getAnnotations(); + assert.lengthOf(annotations, 1); + assert.equal(annotations[0].annotationAuthorName, 'Name'); + + return group.eraseTx(); + }); it("should not copy an item or its attachment to a group twice", function* () { var group = yield getGroup();