Add Zotero.Item.prototype.moveToLibrary()
Move an item and its attachments to another library. Attachments are removed as necessary if linked files or all files aren't supported in the target library.
This commit is contained in:
parent
bc141ce36b
commit
9e955bde99
3 changed files with 209 additions and 4 deletions
|
@ -1115,13 +1115,75 @@ Zotero.Attachments = new function(){
|
|||
|
||||
|
||||
/**
|
||||
* Copy attachment item, including files, to another library
|
||||
* Move attachment item, including file, to another library
|
||||
*/
|
||||
this.moveAttachmentToLibrary = async function (attachment, libraryID, parentItemID) {
|
||||
if (attachment.libraryID == libraryID) {
|
||||
throw new Error("Attachment is already in library " + libraryID);
|
||||
}
|
||||
|
||||
Zotero.DB.requireTransaction();
|
||||
|
||||
var newAttachment = attachment.clone(libraryID);
|
||||
if (attachment.isImportedAttachment()) {
|
||||
// Attachment path isn't copied over by clone() if libraryID is different
|
||||
newAttachment.attachmentPath = attachment.attachmentPath;
|
||||
}
|
||||
if (parentItemID) {
|
||||
newAttachment.parentID = parentItemID;
|
||||
}
|
||||
await newAttachment.save();
|
||||
|
||||
// Move files over if they exist
|
||||
var oldDir;
|
||||
var newDir;
|
||||
if (newAttachment.isImportedAttachment()) {
|
||||
oldDir = this.getStorageDirectory(attachment).path;
|
||||
if (await OS.File.exists(oldDir)) {
|
||||
newDir = this.getStorageDirectory(newAttachment).path;
|
||||
// Target directory shouldn't exist, but remove it if it does
|
||||
//
|
||||
// Testing for directories in OS.File, used by removeDir(), is broken on Travis,
|
||||
// so use nsIFile
|
||||
if (Zotero.automatedTest) {
|
||||
let nsIFile = Zotero.File.pathToFile(newDir);
|
||||
if (nsIFile.exists()) {
|
||||
nsIFile.remove(true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
await OS.File.removeDir(newDir, { ignoreAbsent: true });
|
||||
}
|
||||
await OS.File.move(oldDir, newDir);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await attachment.erase();
|
||||
}
|
||||
catch (e) {
|
||||
// Move files back if old item can't be deleted
|
||||
if (newAttachment.isImportedAttachment()) {
|
||||
try {
|
||||
await OS.File.move(newDir, oldDir);
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
return newAttachment.id;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Copy attachment item, including file, to another library
|
||||
*/
|
||||
this.copyAttachmentToLibrary = Zotero.Promise.coroutine(function* (attachment, libraryID, parentItemID) {
|
||||
var linkMode = attachment.attachmentLinkMode;
|
||||
|
||||
if (attachment.libraryID == libraryID) {
|
||||
throw ("Attachment is already in library " + libraryID);
|
||||
throw new Error("Attachment is already in library " + libraryID);
|
||||
}
|
||||
|
||||
Zotero.DB.requireTransaction();
|
||||
|
|
|
@ -3989,6 +3989,89 @@ Zotero.Item.prototype.clone = function (libraryID, options = {}) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {Zotero.Item} item
|
||||
* @param {Integer} libraryID
|
||||
* @return {Zotero.Item} - New item
|
||||
*/
|
||||
Zotero.Item.prototype.moveToLibrary = async function (libraryID, onSkippedAttachment) {
|
||||
if (!this.isEditable) {
|
||||
throw new Error("Can't move item in read-only library");
|
||||
}
|
||||
var library = Zotero.Libraries.get(libraryID);
|
||||
Zotero.debug("Moving item to " + library.name);
|
||||
if (!library.editable) {
|
||||
throw new Error("Can't move item to read-only library");
|
||||
}
|
||||
var filesEditable = library.filesEditable;
|
||||
var allowsLinkedFiles = library.allowsLinkedFiles;
|
||||
|
||||
var newItem = await Zotero.DB.executeTransaction(async function () {
|
||||
// Create new clone item in target library
|
||||
var newItem = this.clone(libraryID);
|
||||
var newItemID = await newItem.save({
|
||||
skipSelect: true
|
||||
});
|
||||
|
||||
if (this.isNote()) {
|
||||
// Delete old item
|
||||
await this.erase();
|
||||
return newItem;
|
||||
}
|
||||
|
||||
// For regular items, add child items
|
||||
|
||||
// Child notes
|
||||
var noteIDs = this.getNotes();
|
||||
var notes = Zotero.Items.get(noteIDs);
|
||||
for (let note of notes) {
|
||||
let newNote = note.clone(libraryID);
|
||||
newNote.parentID = newItemID;
|
||||
await newNote.save({
|
||||
skipSelect: true
|
||||
});
|
||||
}
|
||||
|
||||
// Child attachments
|
||||
var attachmentIDs = this.getAttachments();
|
||||
var attachments = Zotero.Items.get(attachmentIDs);
|
||||
for (let attachment of attachments) {
|
||||
let linkMode = attachment.attachmentLinkMode;
|
||||
|
||||
// Skip linked files if not allowed in destination
|
||||
if (!allowsLinkedFiles && linkMode == Zotero.Attachments.LINK_MODE_LINKED_FILE) {
|
||||
Zotero.debug("Target library doesn't support linked files -- skipping attachment");
|
||||
if (onSkippedAttachment) {
|
||||
await onSkippedAttachment(attachment);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip files if not allowed in destination
|
||||
if (!filesEditable && linkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) {
|
||||
Zotero.debug("Target library doesn't allow file editing -- skipping attachment");
|
||||
if (onSkippedAttachment) {
|
||||
await onSkippedAttachment(attachment);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
await Zotero.Attachments.moveAttachmentToLibrary(
|
||||
attachment, libraryID, newItemID
|
||||
);
|
||||
}
|
||||
|
||||
return newItem;
|
||||
}.bind(this));
|
||||
|
||||
// Delete old item. Do this outside of a transaction so we don't leave stranded files
|
||||
// in the target library if deleting fails.
|
||||
await this.eraseTx();
|
||||
|
||||
return newItem;
|
||||
};
|
||||
|
||||
|
||||
Zotero.Item.prototype._eraseData = Zotero.Promise.coroutine(function* (env) {
|
||||
Zotero.DB.requireTransaction();
|
||||
|
||||
|
|
|
@ -1258,6 +1258,66 @@ describe("Zotero.Item", function () {
|
|||
})
|
||||
})
|
||||
|
||||
describe("#moveToLibrary()", function () {
|
||||
it("should move items from My Library to a filesEditable group", async function () {
|
||||
var group = await createGroup();
|
||||
|
||||
var item = await createDataObject('item');
|
||||
var attachment1 = await importFileAttachment('test.png', { parentID: item.id });
|
||||
var file = getTestDataDirectory();
|
||||
file.append('test.png');
|
||||
var attachment2 = await Zotero.Attachments.linkFromFile({
|
||||
file,
|
||||
parentItemID: item.id
|
||||
});
|
||||
var note = await createDataObject('item', { itemType: 'note', parentID: item.id });
|
||||
|
||||
var originalIDs = [item.id, attachment1.id, attachment2.id, note.id];
|
||||
var originalAttachmentFile = attachment1.getFilePath();
|
||||
var originalAttachmentHash = await attachment1.attachmentHash
|
||||
|
||||
assert.isTrue(await OS.File.exists(originalAttachmentFile));
|
||||
|
||||
var newItem = await item.moveToLibrary(group.libraryID);
|
||||
|
||||
// Old items and file should be gone
|
||||
assert.isTrue(originalIDs.every(id => !Zotero.Items.get(id)));
|
||||
assert.isFalse(await OS.File.exists(originalAttachmentFile));
|
||||
|
||||
// New items and stored file should exist; linked file should be gone
|
||||
assert.equal(newItem.libraryID, group.libraryID);
|
||||
assert.lengthOf(newItem.getAttachments(), 1);
|
||||
var newAttachment = Zotero.Items.get(newItem.getAttachments()[0]);
|
||||
assert.equal(await newAttachment.attachmentHash, originalAttachmentHash);
|
||||
assert.lengthOf(newItem.getNotes(), 1);
|
||||
});
|
||||
|
||||
it("should move items from My Library to a non-filesEditable group", async function () {
|
||||
var group = await createGroup({
|
||||
filesEditable: false
|
||||
});
|
||||
|
||||
var item = await createDataObject('item');
|
||||
var attachment = await importFileAttachment('test.png', { parentID: item.id });
|
||||
|
||||
var originalIDs = [item.id, attachment.id];
|
||||
var originalAttachmentFile = attachment.getFilePath();
|
||||
var originalAttachmentHash = await attachment.attachmentHash
|
||||
|
||||
assert.isTrue(await OS.File.exists(originalAttachmentFile));
|
||||
|
||||
var newItem = await item.moveToLibrary(group.libraryID);
|
||||
|
||||
// Old items and file should be gone
|
||||
assert.isTrue(originalIDs.every(id => !Zotero.Items.get(id)));
|
||||
assert.isFalse(await OS.File.exists(originalAttachmentFile));
|
||||
|
||||
// Parent should exist, but attachment should not
|
||||
assert.equal(newItem.libraryID, group.libraryID);
|
||||
assert.lengthOf(newItem.getAttachments(), 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#toJSON()", function () {
|
||||
describe("default mode", function () {
|
||||
it("should output only fields with values", function* () {
|
||||
|
|
Loading…
Add table
Reference in a new issue