Fixes duplicates view for async DB
It's way too slow, though, since the whole list is regenerated after merging. Fixes #519 Also: - The arguments to Zotero.Item.prototype.clone() have changed, and it no longer takes an existing item or copies primary data. To create an in-memory copy of an item, use the new Zotero.Item.prototype.copy(). - Zotero.Item.prototype.getUsedFields() now gets in-memory fields rather than the fields in the database
This commit is contained in:
parent
15d28014ed
commit
5c94119c70
|
@ -71,7 +71,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone object so changes in merge pane don't affect it
|
// Clone object so changes in merge pane don't affect it
|
||||||
this._leftpane.ref = val.clone(true);
|
this._leftpane.ref = val.copy();
|
||||||
this._leftpane.original = val;
|
this._leftpane.original = val;
|
||||||
]]>
|
]]>
|
||||||
</setter>
|
</setter>
|
||||||
|
@ -97,7 +97,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone object so changes in merge pane don't affect it
|
// Clone object so changes in merge pane don't affect it
|
||||||
this._rightpane.ref = val.clone(true);
|
this._rightpane.ref = val.copy();
|
||||||
this._rightpane.original = val;
|
this._rightpane.original = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,16 +130,21 @@ var Zotero_Duplicates_Pane = new function () {
|
||||||
// Add master item's values to the beginning of each set of
|
// Add master item's values to the beginning of each set of
|
||||||
// alternative values so that they're still available if the item box
|
// alternative values so that they're still available if the item box
|
||||||
// modifies the item
|
// modifies the item
|
||||||
var diff = item.multiDiff(_otherItems, _ignoreFields);
|
Zotero.spawn(function* () {
|
||||||
if (diff) {
|
var diff = yield item.multiDiff(_otherItems, _ignoreFields);
|
||||||
var itemValues = item.serialize()
|
if (diff) {
|
||||||
for (var i in diff) {
|
let itemValues = yield item.toJSON();
|
||||||
diff[i].unshift(itemValues.fields[i]);
|
for (let i in diff) {
|
||||||
|
diff[i].unshift(itemValues[i] !== undefined ? itemValues[i] : '');
|
||||||
|
}
|
||||||
|
itembox.fieldAlternatives = diff;
|
||||||
}
|
}
|
||||||
itembox.fieldAlternatives = diff;
|
|
||||||
}
|
|
||||||
|
|
||||||
itembox.item = item.clone(true);
|
var newItem = yield item.copy();
|
||||||
|
yield newItem.loadItemData();
|
||||||
|
yield newItem.loadCreators();
|
||||||
|
itembox.item = newItem;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1067,22 +1067,15 @@ Zotero.Attachments = new function(){
|
||||||
throw ("Attachment is already in library " + libraryID);
|
throw ("Attachment is already in library " + libraryID);
|
||||||
}
|
}
|
||||||
|
|
||||||
var newAttachment = new Zotero.Item('attachment');
|
var newAttachment = attachment.clone(libraryID);
|
||||||
newAttachment.libraryID = libraryID;
|
|
||||||
// Link mode needs to be set when saving new attachment
|
|
||||||
newAttachment.attachmentLinkMode = linkMode;
|
|
||||||
if (attachment.isImportedAttachment()) {
|
if (attachment.isImportedAttachment()) {
|
||||||
// Attachment path isn't copied over by clone() if libraryID is different
|
// Attachment path isn't copied over by clone() if libraryID is different
|
||||||
newAttachment.attachmentPath = attachment.attachmentPath;
|
newAttachment.attachmentPath = attachment.attachmentPath;
|
||||||
}
|
}
|
||||||
// DEBUG: save here because clone() doesn't currently work on unsaved tagged items
|
|
||||||
var id = newAttachment.save();
|
|
||||||
newAttachment = Zotero.Items.get(id);
|
|
||||||
attachment.clone(false, newAttachment);
|
|
||||||
if (parentItemID) {
|
if (parentItemID) {
|
||||||
newAttachment.setSource(parentItemID);
|
newAttachment.parentID = parentItemID;
|
||||||
}
|
}
|
||||||
newAttachment.save();
|
yield newAttachment.save();
|
||||||
|
|
||||||
// Copy over files if they exist
|
// Copy over files if they exist
|
||||||
if (newAttachment.isImportedAttachment() && attachment.getFile()) {
|
if (newAttachment.isImportedAttachment() && attachment.getFile()) {
|
||||||
|
@ -1091,7 +1084,7 @@ Zotero.Attachments = new function(){
|
||||||
Zotero.File.copyDirectory(dir, newDir);
|
Zotero.File.copyDirectory(dir, newDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
newAttachment.addLinkedItem(attachment);
|
yield newAttachment.addLinkedItem(attachment);
|
||||||
return newAttachment.id;
|
return newAttachment.id;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1567,23 +1567,15 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r
|
||||||
return Zotero.Attachments.copyAttachmentToLibrary(item, targetLibraryID);
|
return Zotero.Attachments.copyAttachmentToLibrary(item, targetLibraryID);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new unsaved clone item in target library
|
// Create new clone item in target library
|
||||||
var newItem = new Zotero.Item(item.itemTypeID);
|
var newItem = item.clone(targetLibraryID, false, !Zotero.Prefs.get('groups.copyTags'));
|
||||||
newItem.libraryID = targetLibraryID;
|
var newItemID = yield newItem.save();
|
||||||
// DEBUG: save here because clone() doesn't currently work on unsaved tagged items
|
|
||||||
var id = yield newItem.save();
|
|
||||||
newItem = yield Zotero.Items.getAsync(id);
|
|
||||||
yield item.clone(false, newItem, false, !Zotero.Prefs.get('groups.copyTags'));
|
|
||||||
yield newItem.save();
|
|
||||||
//var id = newItem.save();
|
|
||||||
//var newItem = Zotero.Items.get(id);
|
|
||||||
|
|
||||||
// Record link
|
// Record link
|
||||||
yield newItem.addLinkedItem(item);
|
yield newItem.addLinkedItem(item);
|
||||||
var newID = id;
|
|
||||||
|
|
||||||
if (item.isNote()) {
|
if (item.isNote()) {
|
||||||
return newID;
|
return newItemID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For regular items, add child items if prefs and permissions allow
|
// For regular items, add child items if prefs and permissions allow
|
||||||
|
@ -1593,14 +1585,9 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r
|
||||||
var noteIDs = item.getNotes();
|
var noteIDs = item.getNotes();
|
||||||
var notes = yield Zotero.Items.getAsync(noteIDs);
|
var notes = yield Zotero.Items.getAsync(noteIDs);
|
||||||
for each(var note in notes) {
|
for each(var note in notes) {
|
||||||
var newNote = new Zotero.Item('note');
|
let newNote = note.clone(targetLibraryID);
|
||||||
newNote.libraryID = targetLibraryID;
|
newNote.parentID = newItemID;
|
||||||
// DEBUG: save here because clone() doesn't currently work on unsaved tagged items
|
yield newNote.save()
|
||||||
var id = yield newNote.save();
|
|
||||||
newNote = yield Zotero.Items.getAsync(id);
|
|
||||||
yield note.clone(false, newNote);
|
|
||||||
newNote.parentID = newItem.id;
|
|
||||||
yield newNote.save();
|
|
||||||
|
|
||||||
yield newNote.addLinkedItem(note);
|
yield newNote.addLinkedItem(note);
|
||||||
}
|
}
|
||||||
|
|
|
@ -421,73 +421,64 @@ Zotero.DataObjects = function (object, objectPlural, id, table) {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} data1 Serialized copy of first object
|
* @param {Object} data1 - JSON of first object
|
||||||
* @param {Object} data2 Serialized copy of second object
|
* @param {Object} data2 - JSON of second object
|
||||||
* @param {Array} diff Empty array to put diff data in
|
* @param {Array} diff - Empty array to put diff data in
|
||||||
* @param {Boolean} [includeMatches=false] Include all fields, even those
|
* @param {Boolean} [includeMatches=false] - Include all fields, even those
|
||||||
* that aren't different
|
* that aren't different
|
||||||
*/
|
*/
|
||||||
this.diff = function (data1, data2, diff, includeMatches) {
|
this.diff = function (data1, data2, diff, includeMatches) {
|
||||||
diff.push({}, {});
|
diff.push({}, {});
|
||||||
var numDiffs = 0;
|
var numDiffs = 0;
|
||||||
|
|
||||||
var subs = ['primary', 'fields'];
|
var skipFields = ['collectionKey', 'itemKey', 'searchKey'];
|
||||||
var skipFields = ['collectionID', 'creatorID', 'itemID', 'searchID', 'tagID', 'libraryID', 'key'];
|
|
||||||
|
|
||||||
for each(var sub in subs) {
|
for (var field in data1) {
|
||||||
diff[0][sub] = {};
|
if (skipFields.indexOf(field) != -1) {
|
||||||
diff[1][sub] = {};
|
continue;
|
||||||
for (var field in data1[sub]) {
|
|
||||||
if (skipFields.indexOf(field) != -1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data1[sub][field] && !data2[sub][field]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var changed = !data1[sub][field] || !data2[sub][field] ||
|
|
||||||
data1[sub][field] != data2[sub][field];
|
|
||||||
|
|
||||||
if (includeMatches || changed) {
|
|
||||||
diff[0][sub][field] = data1[sub][field] ?
|
|
||||||
data1[sub][field] : '';
|
|
||||||
diff[1][sub][field] = data2[sub][field] ?
|
|
||||||
data2[sub][field] : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changed) {
|
|
||||||
numDiffs++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEBUG: some of this is probably redundant
|
if (data1[field] === false && (data2[field] === false || data2[field] === undefined)) {
|
||||||
for (var field in data2[sub]) {
|
continue;
|
||||||
if (skipFields.indexOf(field) != -1) {
|
}
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (diff[0][sub][field] != undefined) {
|
var changed = data1[field] !== data2[field];
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data1[sub][field] && !data2[sub][field]) {
|
if (includeMatches || changed) {
|
||||||
continue;
|
diff[0][field] = data1[field] !== false ? data1[field] : '';
|
||||||
}
|
diff[1][field] = (data2[field] !== false && data2[field] !== undefined)
|
||||||
|
? data2[field] : '';
|
||||||
|
}
|
||||||
|
|
||||||
var changed = !data1[sub][field] || !data2[sub][field] ||
|
if (changed) {
|
||||||
data1[sub][field] != data2[sub][field];
|
numDiffs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (includeMatches || changed) {
|
// DEBUG: some of this is probably redundant
|
||||||
diff[0][sub][field] = data1[sub][field] ?
|
for (var field in data2) {
|
||||||
data1[sub][field] : '';
|
if (skipFields.indexOf(field) != -1) {
|
||||||
diff[1][sub][field] = data2[sub][field] ?
|
continue;
|
||||||
data2[sub][field] : '';
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (changed) {
|
if (diff[0][field] !== undefined) {
|
||||||
numDiffs++;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data2[field] === false && (data1[field] === false || data1[field] === undefined)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var changed = data1[field] !== data2[field];
|
||||||
|
|
||||||
|
if (includeMatches || changed) {
|
||||||
|
diff[0][field] = (data1[field] !== false && data1[field] !== undefined)
|
||||||
|
? data1[field] : '';
|
||||||
|
diff[1][field] = data2[field] !== false ? data2[field] : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
numDiffs++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -232,7 +232,7 @@ Zotero.Item.prototype.getField = function(field, unformatted, includeBaseMapped)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var value = value ? value : '';
|
value = value ? value : '';
|
||||||
|
|
||||||
if (!unformatted) {
|
if (!unformatted) {
|
||||||
// Multipart date fields
|
// Multipart date fields
|
||||||
|
@ -251,18 +251,11 @@ Zotero.Item.prototype.getField = function(field, unformatted, includeBaseMapped)
|
||||||
* @return {Integer{}|String[]}
|
* @return {Integer{}|String[]}
|
||||||
*/
|
*/
|
||||||
Zotero.Item.prototype.getUsedFields = Zotero.Promise.coroutine(function* (asNames) {
|
Zotero.Item.prototype.getUsedFields = Zotero.Promise.coroutine(function* (asNames) {
|
||||||
if (!this.id) {
|
this._requireData('itemData');
|
||||||
return [];
|
|
||||||
}
|
return Object.keys(this._itemData)
|
||||||
var sql = "SELECT fieldID FROM itemData WHERE itemID=?";
|
.filter(id => this._itemData[id] !== false)
|
||||||
if (asNames) {
|
.map(id => asNames ? Zotero.ItemFields.getName(id) : parseInt(id));
|
||||||
sql = "SELECT fieldName FROM fields WHERE fieldID IN (" + sql + ")";
|
|
||||||
}
|
|
||||||
var fields = yield Zotero.DB.columnQueryAsync(sql, this._id);
|
|
||||||
if (!fields) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return fields;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -1581,7 +1574,8 @@ Zotero.Item.prototype.save = Zotero.Promise.coroutine(function* (options) {
|
||||||
for (let i=0; i<toAdd.length; i++) {
|
for (let i=0; i<toAdd.length; i++) {
|
||||||
let tag = toAdd[i];
|
let tag = toAdd[i];
|
||||||
let tagID = yield Zotero.Tags.getIDFromName(this.libraryID, tag.tag, true);
|
let tagID = yield Zotero.Tags.getIDFromName(this.libraryID, tag.tag, true);
|
||||||
let sql = "INSERT INTO itemTags (itemID, tagID, type) VALUES (?, ?, ?)";
|
// "OR REPLACE" allows changing type
|
||||||
|
let sql = "INSERT OR REPLACE INTO itemTags (itemID, tagID, type) VALUES (?, ?, ?)";
|
||||||
yield Zotero.DB.queryAsync(sql, [this.id, tagID, tag.type ? tag.type : 0]);
|
yield Zotero.DB.queryAsync(sql, [this.id, tagID, tag.type ? tag.type : 0]);
|
||||||
Zotero.Notifier.trigger('add', 'item-tag', this.id + '-' + tag.tag);
|
Zotero.Notifier.trigger('add', 'item-tag', this.id + '-' + tag.tag);
|
||||||
}
|
}
|
||||||
|
@ -3248,31 +3242,35 @@ Zotero.Item.prototype.setTags = function (tags) {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a single manual tag to the item. If an automatic tag with the same name already exists,
|
* Add a single tag to the item. If type is 1 and an automatic tag with the same name already
|
||||||
* replace it with a manual one.
|
* exists, replace it with a manual one.
|
||||||
*
|
*
|
||||||
* A separate save() is required to update the database.
|
* A separate save() is required to update the database.
|
||||||
*
|
*
|
||||||
* @param {String} name
|
* @param {String} name
|
||||||
|
* @param {Number} [type=0]
|
||||||
*/
|
*/
|
||||||
Zotero.Item.prototype.addTag = function (name) {
|
Zotero.Item.prototype.addTag = function (name, type) {
|
||||||
|
type = type ? parseInt(type) : 0;
|
||||||
|
|
||||||
var changed = false;
|
var changed = false;
|
||||||
var tags = this.getTags();
|
var tags = this.getTags();
|
||||||
for (let i=0; i<tags.length; i++) {
|
for (let i=0; i<tags.length; i++) {
|
||||||
let tag = tags[i];
|
let tag = tags[i];
|
||||||
if (tag.tag === name) {
|
if (tag.tag === name) {
|
||||||
if (!tag.type) {
|
if (tag.type == type) {
|
||||||
Zotero.debug("Tag '" + name + "' already exists on item " + this.libraryKey);
|
Zotero.debug("Tag '" + name + "' already exists on item " + this.libraryKey);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
tag.type = 0;
|
tag.type = type;
|
||||||
changed = true;
|
changed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!changed) {
|
if (!changed) {
|
||||||
tags.push({
|
tags.push({
|
||||||
tag: name
|
tag: name,
|
||||||
|
type: type
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.setTags(tags);
|
this.setTags(tags);
|
||||||
|
@ -3706,24 +3704,25 @@ Zotero.Item.prototype.diff = function (item, includeMatches, ignoreFields) {
|
||||||
*
|
*
|
||||||
* Currently compares only item data, not primary fields
|
* Currently compares only item data, not primary fields
|
||||||
*/
|
*/
|
||||||
Zotero.Item.prototype.multiDiff = function (otherItems, ignoreFields) {
|
Zotero.Item.prototype.multiDiff = Zotero.Promise.coroutine(function* (otherItems, ignoreFields) {
|
||||||
var thisData = this.serialize();
|
var thisData = yield this.toJSON();
|
||||||
|
|
||||||
var alternatives = {};
|
var alternatives = {};
|
||||||
var hasDiffs = false;
|
var hasDiffs = false;
|
||||||
|
|
||||||
for each(var otherItem in otherItems) {
|
for (let i = 0; i < otherItems.length; i++) {
|
||||||
var diff = [];
|
let otherItem = otherItems[i];
|
||||||
var otherData = otherItem.serialize();
|
let diff = [];
|
||||||
var numDiffs = Zotero.Items.diff(thisData, otherData, diff);
|
let otherData = yield otherItem.toJSON();
|
||||||
|
let numDiffs = Zotero.Items.diff(thisData, otherData, diff);
|
||||||
|
|
||||||
if (numDiffs) {
|
if (numDiffs) {
|
||||||
for (var field in diff[1].fields) {
|
for (let field in diff[1]) {
|
||||||
if (ignoreFields && ignoreFields.indexOf(field) != -1) {
|
if (ignoreFields && ignoreFields.indexOf(field) != -1) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var value = diff[1].fields[field];
|
var value = diff[1][field];
|
||||||
|
|
||||||
if (!alternatives[field]) {
|
if (!alternatives[field]) {
|
||||||
hasDiffs = true;
|
hasDiffs = true;
|
||||||
|
@ -3742,143 +3741,41 @@ Zotero.Item.prototype.multiDiff = function (otherItems, ignoreFields) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return alternatives;
|
return alternatives;
|
||||||
}
|
});
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an unsaved copy of the item
|
* Returns an unsaved copy of the item
|
||||||
*
|
*
|
||||||
* @param {Boolean} [includePrimary=false]
|
* @param {Number} [libraryID] - libraryID of the new item, or the same as original if omitted
|
||||||
* @param {Zotero.Item} [newItem=null] Target item for clone (used to pass a saved
|
* @param {Boolean} [skipTags=false] - Skip tags
|
||||||
* item for duplicating items with tags)
|
|
||||||
* @param {Boolean} [unsaved=false] Skip properties that require a saved object (e.g., tags)
|
|
||||||
* @param {Boolean} [skipTags=false] Skip tags (implied by 'unsaved')
|
|
||||||
*/
|
*/
|
||||||
Zotero.Item.prototype.clone = function(includePrimary, newItem, unsaved, skipTags) {
|
Zotero.Item.prototype.clone = function(libraryID, skipTags) {
|
||||||
Zotero.debug('Cloning item ' + this.id);
|
Zotero.debug('Cloning item ' + this.id);
|
||||||
|
|
||||||
if (includePrimary && newItem) {
|
if (libraryID !== undefined && libraryID !== null && typeof libraryID !== 'number') {
|
||||||
throw ("includePrimary and newItem parameters are mutually exclusive in Zotero.Item.clone()");
|
throw new Error("libraryID must be null or an integer");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unsaved) {
|
this._requireData('primaryData');
|
||||||
skipTags = true;
|
|
||||||
|
if (libraryID === undefined || libraryID === null) {
|
||||||
|
libraryID = this.libraryID;
|
||||||
}
|
}
|
||||||
|
var sameLibrary = libraryID == this.libraryID;
|
||||||
|
|
||||||
Zotero.DB.beginTransaction();
|
var newItem = new Zotero.Item;
|
||||||
|
newItem.setType(this.itemTypeID);
|
||||||
|
|
||||||
// TODO: get rid of serialize() call
|
var fieldIDs = this.getUsedFields();
|
||||||
var obj = this.serialize();
|
for (let i = 0; i < fieldIDs.length; i++) {
|
||||||
|
let fieldID = fieldIDs[i];
|
||||||
var itemTypeID = this.itemTypeID;
|
newItem.setField(fieldID, this.getField(fieldID));
|
||||||
|
|
||||||
if (newItem) {
|
|
||||||
var sameLibrary = newItem.libraryID == this.libraryID;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var newItem = new Zotero.Item;
|
|
||||||
var sameLibrary = true;
|
|
||||||
|
|
||||||
if (includePrimary) {
|
|
||||||
newItem.id = this.id;
|
|
||||||
newItem.libraryID = this.libraryID;
|
|
||||||
newItem.key = this.key;
|
|
||||||
newItem.setType(itemTypeID);
|
|
||||||
for (var field in obj.primary) {
|
|
||||||
switch (field) {
|
|
||||||
case 'itemID':
|
|
||||||
case 'itemType':
|
|
||||||
case 'libraryID':
|
|
||||||
case 'key':
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
newItem.setField(field, obj.primary[field]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
newItem.setType(itemTypeID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var changedFields = {};
|
|
||||||
for (var field in obj.fields) {
|
|
||||||
var fieldID = Zotero.ItemFields.getID(field);
|
|
||||||
if (fieldID && Zotero.ItemFields.isValidForType(fieldID, itemTypeID)) {
|
|
||||||
newItem.setField(field, obj.fields[field]);
|
|
||||||
changedFields[field] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If modifying an existing item, clear other fields not in the cloned item
|
|
||||||
if (newItem) {
|
|
||||||
var previousFields = this.getUsedFields(true);
|
|
||||||
for each(var field in previousFields) {
|
|
||||||
if (!changedFields[field] && Zotero.ItemFields.isValidForType(field, itemTypeID)) {
|
|
||||||
newItem.setField(field, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regular item
|
// Regular item
|
||||||
if (this.isRegularItem()) {
|
if (this.isRegularItem()) {
|
||||||
if (includePrimary) {
|
newItem.setCreators(newItem.getCreators());
|
||||||
// newItem = loaded from db
|
|
||||||
// obj = in-memory
|
|
||||||
var max = Math.max(newItem.numCreators(), this.numCreators());
|
|
||||||
var deleteOffset = 0;
|
|
||||||
for (var i=0; i<max; i++) {
|
|
||||||
var newIndex = i - deleteOffset;
|
|
||||||
|
|
||||||
// Remove existing creators (loaded because we set the itemID
|
|
||||||
// above) not in the in-memory version
|
|
||||||
if (!obj.creators[i]) {
|
|
||||||
if (newItem.getCreator(newIndex)) {
|
|
||||||
newItem.removeCreator(newIndex);
|
|
||||||
deleteOffset++;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Add in-memory creators
|
|
||||||
newItem.setCreator(
|
|
||||||
newIndex, this.getCreator(i).ref, obj.creators[i].creatorTypeID
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// If overwriting an existing item, clear existing creators
|
|
||||||
if (newItem) {
|
|
||||||
for (var i=newItem.numCreators()-1; i>=0; i--) {
|
|
||||||
if (newItem.getCreator(i)) {
|
|
||||||
newItem.removeCreator(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var i = 0;
|
|
||||||
for (var c in obj.creators) {
|
|
||||||
var creator = this.getCreator(c).ref;
|
|
||||||
var creatorTypeID = this.getCreator(c).creatorTypeID;
|
|
||||||
|
|
||||||
if (!sameLibrary) {
|
|
||||||
var creatorDataID = Zotero.Creators.getDataID(this.getCreator(c).ref);
|
|
||||||
var creatorIDs = Zotero.Creators.getCreatorsWithData(creatorDataID, newItem.libraryID);
|
|
||||||
if (creatorIDs) {
|
|
||||||
// TODO: support multiple creators?
|
|
||||||
var creator = Zotero.Creators.get(creatorIDs[0]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var newCreator = new Zotero.Creator;
|
|
||||||
newCreator.libraryID = newItem.libraryID;
|
|
||||||
newCreator.setFields(creator);
|
|
||||||
var creator = newCreator;
|
|
||||||
}
|
|
||||||
|
|
||||||
var creatorTypeID = this.getCreator(c).creatorTypeID;
|
|
||||||
}
|
|
||||||
|
|
||||||
newItem.setCreator(i, creator, creatorTypeID);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
newItem.setNote(this.getNote());
|
newItem.setNote(this.getNote());
|
||||||
|
@ -3904,28 +3801,30 @@ Zotero.Item.prototype.clone = function(includePrimary, newItem, unsaved, skipTag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!skipTags && obj.tags) {
|
if (!skipTags) {
|
||||||
for each(var tag in obj.tags) {
|
newItem.setTags(this.getTags());
|
||||||
if (sameLibrary) {
|
|
||||||
newItem.addTagByID(tag.primary.tagID);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
newItem.addTag(tag.fields.name, tag.fields.type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj.related && sameLibrary) {
|
if (sameLibrary) {
|
||||||
// DEBUG: this will add reverse-only relateds too
|
// DEBUG: this will add reverse-only relateds too
|
||||||
newItem.relatedItems = obj.related;
|
newItem.setRelations(this.getRelations());
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.DB.commitTransaction();
|
|
||||||
|
|
||||||
return newItem;
|
return newItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {Promise<Zotero.Item>} - A copy of the item with primary data loaded
|
||||||
|
*/
|
||||||
|
Zotero.Item.prototype.copy = Zotero.Promise.coroutine(function* () {
|
||||||
|
var newItem = new Zotero.Item;
|
||||||
|
newItem.id = this.id;
|
||||||
|
yield newItem.loadPrimaryData();
|
||||||
|
return newItem;
|
||||||
|
});;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete item from database and clear from Zotero.Items internal array
|
* Delete item from database and clear from Zotero.Items internal array
|
||||||
*
|
*
|
||||||
|
|
|
@ -481,65 +481,76 @@ Zotero.Items = new function() {
|
||||||
|
|
||||||
|
|
||||||
this.merge = function (item, otherItems) {
|
this.merge = function (item, otherItems) {
|
||||||
Zotero.DB.beginTransaction();
|
return Zotero.DB.executeTransaction(function* () {
|
||||||
|
var otherItemIDs = [];
|
||||||
|
var itemURI = Zotero.URI.getItemURI(item);
|
||||||
|
|
||||||
var otherItemIDs = [];
|
yield item.loadTags();
|
||||||
var itemURI = Zotero.URI.getItemURI(item);
|
yield item.loadRelations();
|
||||||
|
|
||||||
for each(var otherItem in otherItems) {
|
for each(var otherItem in otherItems) {
|
||||||
// Move child items to master
|
yield otherItem.loadChildItems();
|
||||||
var ids = otherItem.getAttachments(true).concat(otherItem.getNotes(true));
|
yield otherItem.loadCollections();
|
||||||
for each(var id in ids) {
|
yield otherItem.loadTags();
|
||||||
var attachment = Zotero.Items.get(id);
|
yield otherItem.loadRelations();
|
||||||
|
|
||||||
// TODO: Skip identical children?
|
// Move child items to master
|
||||||
|
var ids = otherItem.getAttachments(true).concat(otherItem.getNotes(true));
|
||||||
|
for each(var id in ids) {
|
||||||
|
var attachment = yield Zotero.Items.getAsync(id);
|
||||||
|
|
||||||
attachment.parentID = item.id;
|
// TODO: Skip identical children?
|
||||||
attachment.save();
|
|
||||||
|
attachment.parentID = item.id;
|
||||||
|
yield attachment.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// All other operations are additive only and do not affect the,
|
||||||
|
// old item, which will be put in the trash
|
||||||
|
|
||||||
|
// Add collections to master
|
||||||
|
var collectionIDs = otherItem.getCollections();
|
||||||
|
for each(var collectionID in collectionIDs) {
|
||||||
|
let collection = yield Zotero.Collections.getAsync(collectionID);
|
||||||
|
yield collection.addItem(item.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add tags to master
|
||||||
|
var tags = otherItem.getTags();
|
||||||
|
for (let j = 0; j < tags.length; j++) {
|
||||||
|
item.addTag(tags[j].tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Related items
|
||||||
|
var relatedItems = otherItem.relatedItems;
|
||||||
|
for each(var relatedItemID in relatedItems) {
|
||||||
|
yield item.addRelatedItem(relatedItemID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relations
|
||||||
|
yield Zotero.Relations.copyURIs(
|
||||||
|
item.libraryID,
|
||||||
|
Zotero.URI.getItemURI(otherItem),
|
||||||
|
Zotero.URI.getItemURI(item)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add relation to track merge
|
||||||
|
var otherItemURI = Zotero.URI.getItemURI(otherItem);
|
||||||
|
yield Zotero.Relations.add(
|
||||||
|
item.libraryID,
|
||||||
|
otherItemURI,
|
||||||
|
Zotero.Relations.deletedItemPredicate,
|
||||||
|
itemURI
|
||||||
|
);
|
||||||
|
|
||||||
|
// Trash other item
|
||||||
|
otherItem.deleted = true;
|
||||||
|
yield otherItem.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
// All other operations are additive only and do not affect the,
|
yield item.save();
|
||||||
// old item, which will be put in the trash
|
});
|
||||||
|
};
|
||||||
// Add collections to master
|
|
||||||
var collectionIDs = otherItem.getCollections();
|
|
||||||
for each(var collectionID in collectionIDs) {
|
|
||||||
var collection = Zotero.Collections.get(collectionID);
|
|
||||||
collection.addItem(item.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add tags to master
|
|
||||||
var tags = otherItem.getTags();
|
|
||||||
for each(var tag in tags) {
|
|
||||||
item.addTagByID(tag.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Related items
|
|
||||||
var relatedItems = otherItem.relatedItems;
|
|
||||||
for each(var relatedItemID in relatedItems) {
|
|
||||||
item.addRelatedItem(relatedItemID);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Relations
|
|
||||||
Zotero.Relations.copyURIs(
|
|
||||||
item.libraryID,
|
|
||||||
Zotero.URI.getItemURI(otherItem),
|
|
||||||
Zotero.URI.getItemURI(item)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add relation to track merge
|
|
||||||
var otherItemURI = Zotero.URI.getItemURI(otherItem);
|
|
||||||
Zotero.Relations.add(item.libraryID, otherItemURI, Zotero.Relations.deletedItemPredicate, itemURI);
|
|
||||||
|
|
||||||
// Trash other item
|
|
||||||
otherItem.deleted = true;
|
|
||||||
otherItem.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
item.save();
|
|
||||||
|
|
||||||
Zotero.DB.commitTransaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.trash = function (ids) {
|
this.trash = function (ids) {
|
||||||
|
|
|
@ -79,11 +79,14 @@ Zotero.Relations = new function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
var toReturn = [];
|
var toReturn = [];
|
||||||
|
var loads = [];
|
||||||
for (let i=0; i<rows.length; i++) {
|
for (let i=0; i<rows.length; i++) {
|
||||||
var relation = new Zotero.Relation;
|
var relation = new Zotero.Relation;
|
||||||
relation.id = rows[i];
|
relation.id = rows[i];
|
||||||
|
loads.push(relation.load());
|
||||||
toReturn.push(relation);
|
toReturn.push(relation);
|
||||||
}
|
}
|
||||||
|
yield Zotero.Promise.all(loads);
|
||||||
return toReturn;
|
return toReturn;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -54,13 +54,15 @@ Zotero.Duplicates.prototype.getSearchObject = Zotero.Promise.coroutine(function*
|
||||||
+ "(id INTEGER PRIMARY KEY)";
|
+ "(id INTEGER PRIMARY KEY)";
|
||||||
yield Zotero.DB.queryAsync(sql);
|
yield Zotero.DB.queryAsync(sql);
|
||||||
|
|
||||||
this._findDuplicates();
|
yield this._findDuplicates();
|
||||||
var ids = this._sets.findAll(true);
|
var ids = this._sets.findAll(true);
|
||||||
|
|
||||||
|
Zotero.debug("Inserting rows into temp table");
|
||||||
sql = "INSERT INTO tmpDuplicates VALUES (?)";
|
sql = "INSERT INTO tmpDuplicates VALUES (?)";
|
||||||
for (let i=0; i<ids.length; i++) {
|
for (let i=0; i<ids.length; i++) {
|
||||||
yield Zotero.DB.queryAsync(sql, [ids[i]], { debug: false })
|
yield Zotero.DB.queryAsync(sql, [ids[i]], { debug: false })
|
||||||
}
|
}
|
||||||
|
Zotero.debug("Done");
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
var s = new Zotero.Search;
|
var s = new Zotero.Search;
|
||||||
|
@ -88,7 +90,7 @@ Zotero.Duplicates.prototype._getObjectFromID = function (id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Zotero.Duplicates.prototype._findDuplicates = function () {
|
Zotero.Duplicates.prototype._findDuplicates = Zotero.Promise.coroutine(function* () {
|
||||||
var start = Date.now();
|
var start = Date.now();
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -136,8 +138,8 @@ Zotero.Duplicates.prototype._findDuplicates = function () {
|
||||||
* set and the next start row would be a
|
* set and the next start row would be a
|
||||||
* different title.
|
* different title.
|
||||||
*/
|
*/
|
||||||
function processRows(compareRows, reprocessMatches) {
|
function processRows(rows, compareRows, reprocessMatches) {
|
||||||
if (!rows) {
|
if (!rows.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +184,7 @@ Zotero.Duplicates.prototype._findDuplicates = function () {
|
||||||
+ "JOIN itemDataValues USING (valueID) "
|
+ "JOIN itemDataValues USING (valueID) "
|
||||||
+ "WHERE libraryID=? AND itemTypeID=? AND fieldID=? "
|
+ "WHERE libraryID=? AND itemTypeID=? AND fieldID=? "
|
||||||
+ "AND itemID NOT IN (SELECT itemID FROM deletedItems)";
|
+ "AND itemID NOT IN (SELECT itemID FROM deletedItems)";
|
||||||
var rows = Zotero.DB.query(
|
var rows = yield Zotero.DB.queryAsync(
|
||||||
sql,
|
sql,
|
||||||
[
|
[
|
||||||
this._libraryID,
|
this._libraryID,
|
||||||
|
@ -191,29 +193,43 @@ Zotero.Duplicates.prototype._findDuplicates = function () {
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
var isbnCache = {};
|
var isbnCache = {};
|
||||||
if (rows) {
|
if (rows.length) {
|
||||||
for each(var row in rows) {
|
let newRows = [];
|
||||||
row.value = (row.value+'').replace(/[^\dX]+/ig, '').toUpperCase(); //ignore formatting
|
for (let i = 0; i < rows.length; i++) {
|
||||||
isbnCache[row.itemID] = row.value;
|
let row = rows[i];
|
||||||
|
// Ignore formatting
|
||||||
|
let newVal = (row.value + '').replace(/[^\dX]+/ig, '').toUpperCase();
|
||||||
|
isbnCache[row.itemID] = newVal;
|
||||||
|
newRows.push({
|
||||||
|
itemID: row.itemID,
|
||||||
|
value: newVal
|
||||||
|
});
|
||||||
}
|
}
|
||||||
rows.sort(sortByValue);
|
newRows.sort(sortByValue);
|
||||||
processRows();
|
processRows(newRows);
|
||||||
}
|
}
|
||||||
|
|
||||||
// DOI
|
// DOI
|
||||||
var sql = "SELECT itemID, value FROM items JOIN itemData USING (itemID) "
|
var sql = "SELECT itemID, value FROM items JOIN itemData USING (itemID) "
|
||||||
+ "JOIN itemDataValues USING (valueID) "
|
+ "JOIN itemDataValues USING (valueID) "
|
||||||
+ "WHERE libraryID=? AND fieldID=? AND REGEXP('^10\\.', value) "
|
+ "WHERE libraryID=? AND fieldID=? AND value LIKE '10\\.%' "
|
||||||
+ "AND itemID NOT IN (SELECT itemID FROM deletedItems)";
|
+ "AND itemID NOT IN (SELECT itemID FROM deletedItems)";
|
||||||
var rows = Zotero.DB.query(sql, [this._libraryID, Zotero.ItemFields.getID('DOI')]);
|
var rows = yield Zotero.DB.queryAsync(sql, [this._libraryID, Zotero.ItemFields.getID('DOI')]);
|
||||||
var doiCache = {};
|
var doiCache = {};
|
||||||
if (rows) {
|
if (rows.length) {
|
||||||
for each(var row in rows) {
|
let newRows = [];
|
||||||
row.value = (row.value+'').trim().toUpperCase(); //DOIs are case insensitive
|
for (let i = 0; i < rows.length; i++) {
|
||||||
doiCache[row.itemID] = row.value;
|
let row = rows[i];
|
||||||
|
// DOIs are case insensitive
|
||||||
|
let newVal = (row.value + '').trim().toUpperCase();
|
||||||
|
doiCache[row.itemID] = newVal;
|
||||||
|
newRows.push({
|
||||||
|
itemID: row.itemID,
|
||||||
|
value: newVal
|
||||||
|
});
|
||||||
}
|
}
|
||||||
rows.sort(sortByValue);
|
newRows.sort(sortByValue);
|
||||||
processRows();
|
processRows(newRows);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get years
|
// Get years
|
||||||
|
@ -228,33 +244,70 @@ Zotero.Duplicates.prototype._findDuplicates = function () {
|
||||||
+ "AND SUBSTR(value, 1, 4) != '0000' "
|
+ "AND SUBSTR(value, 1, 4) != '0000' "
|
||||||
+ "AND itemID NOT IN (SELECT itemID FROM deletedItems) "
|
+ "AND itemID NOT IN (SELECT itemID FROM deletedItems) "
|
||||||
+ "ORDER BY value";
|
+ "ORDER BY value";
|
||||||
var rows = Zotero.DB.query(sql, [this._libraryID].concat(dateFields));
|
var rows = yield Zotero.DB.queryAsync(sql, [this._libraryID].concat(dateFields));
|
||||||
var yearCache = {};
|
var yearCache = {};
|
||||||
if (rows) {
|
for (let i = 0; i < rows.length; i++) {
|
||||||
for each(var row in rows) {
|
let row = rows[i];
|
||||||
yearCache[row.itemID] = row.year;
|
yearCache[row.itemID] = row.year;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var creatorRowsCache = {};
|
|
||||||
|
|
||||||
// Match on normalized title
|
// Match on normalized title
|
||||||
|
var titleIDs = Zotero.ItemFields.getTypeFieldsFromBase('title');
|
||||||
|
titleIDs.push(Zotero.ItemFields.getID('title'));
|
||||||
var sql = "SELECT itemID, value FROM items JOIN itemData USING (itemID) "
|
var sql = "SELECT itemID, value FROM items JOIN itemData USING (itemID) "
|
||||||
+ "JOIN itemDataValues USING (valueID) "
|
+ "JOIN itemDataValues USING (valueID) "
|
||||||
+ "WHERE libraryID=? AND fieldID BETWEEN 110 AND 113 "
|
+ "WHERE libraryID=? AND fieldID IN "
|
||||||
|
+ "(" + titleIDs.join(', ') + ") "
|
||||||
+ "AND itemTypeID NOT IN (1, 14) "
|
+ "AND itemTypeID NOT IN (1, 14) "
|
||||||
+ "AND itemID NOT IN (SELECT itemID FROM deletedItems)";
|
+ "AND itemID NOT IN (SELECT itemID FROM deletedItems)";
|
||||||
var rows = Zotero.DB.query(sql, [this._libraryID]);
|
var rows = yield Zotero.DB.queryAsync(sql, [this._libraryID]);
|
||||||
if(rows) {
|
if (rows.length) {
|
||||||
//normalize all values ahead of time
|
//normalize all values ahead of time
|
||||||
rows = rows.map(function(row) {
|
rows = rows.map(function(row) {
|
||||||
row.value = normalizeString(row.value);
|
return {
|
||||||
return row;
|
itemID: row.itemID,
|
||||||
|
value: normalizeString(row.value)
|
||||||
|
};
|
||||||
});
|
});
|
||||||
//sort rows by normalized values
|
//sort rows by normalized values
|
||||||
rows.sort(sortByValue);
|
rows.sort(sortByValue);
|
||||||
|
|
||||||
processRows(function (a, b) {
|
// Get all creators and separate by itemID
|
||||||
|
//
|
||||||
|
// We won't need all of these, but otherwise we would have to make processRows()
|
||||||
|
// asynchronous, which would be too slow
|
||||||
|
let creatorRowsCache = {};
|
||||||
|
let sql = "SELECT itemID, lastName, firstName, fieldMode FROM items "
|
||||||
|
+ "JOIN itemCreators USING (itemID) "
|
||||||
|
+ "JOIN creators USING (creatorID) "
|
||||||
|
+ "WHERE libraryID=? AND itemTypeID NOT IN (1, 14) AND "
|
||||||
|
+ "itemID NOT IN (SELECT itemID FROM deletedItems)"
|
||||||
|
+ "ORDER BY itemID, orderIndex";
|
||||||
|
let creatorRows = yield Zotero.DB.queryAsync(sql, this._libraryID);
|
||||||
|
let lastItemID;
|
||||||
|
let itemCreators = [];
|
||||||
|
for (let i = 0; i < creatorRows.length; i++) {
|
||||||
|
let row = creatorRows[i];
|
||||||
|
if (lastItemID && row.itemID != lastItemID) {
|
||||||
|
if (itemCreators.length) {
|
||||||
|
creatorRowsCache[lastItemID] = itemCreators;
|
||||||
|
itemCreators = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
itemCreators.push({
|
||||||
|
lastName: normalizeString(row.lastName),
|
||||||
|
firstInitial: row.fieldMode == 0 ? normalizeString(row.firstName).charAt(0) : false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
lastItemID = row.itemID;
|
||||||
|
}
|
||||||
|
// Add final item creators
|
||||||
|
if (itemCreators.length) {
|
||||||
|
creatorRowsCache[lastItemID] = itemCreators;
|
||||||
|
}
|
||||||
|
|
||||||
|
processRows(rows, function (a, b) {
|
||||||
var aTitle = a.value;
|
var aTitle = a.value;
|
||||||
var bTitle = b.value;
|
var bTitle = b.value;
|
||||||
|
|
||||||
|
@ -293,25 +346,9 @@ Zotero.Duplicates.prototype._findDuplicates = function () {
|
||||||
if (typeof creatorRowsCache[a.itemID] != 'undefined') {
|
if (typeof creatorRowsCache[a.itemID] != 'undefined') {
|
||||||
aCreatorRows = creatorRowsCache[a.itemID];
|
aCreatorRows = creatorRowsCache[a.itemID];
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
var sql = "SELECT lastName, firstName, fieldMode FROM itemCreators "
|
|
||||||
+ "JOIN creators USING (creatorID) "
|
|
||||||
+ "WHERE itemID=? ORDER BY orderIndex LIMIT 10";
|
|
||||||
aCreatorRows = Zotero.DB.query(sql, a.itemID);
|
|
||||||
creatorRowsCache[a.itemID] = aCreatorRows;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for at least one match on last name + first initial of first name
|
|
||||||
if (typeof creatorRowsCache[b.itemID] != 'undefined') {
|
if (typeof creatorRowsCache[b.itemID] != 'undefined') {
|
||||||
bCreatorRows = creatorRowsCache[b.itemID];
|
bCreatorRows = creatorRowsCache[b.itemID];
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
var sql = "SELECT lastName, firstName, fieldMode FROM itemCreators "
|
|
||||||
+ "JOIN creators USING (creatorID) "
|
|
||||||
+ "WHERE itemID=? ORDER BY orderIndex LIMIT 10";
|
|
||||||
bCreatorRows = Zotero.DB.query(sql, b.itemID);
|
|
||||||
creatorRowsCache[b.itemID] = bCreatorRows;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match if no creators
|
// Match if no creators
|
||||||
if (!aCreatorRows && !bCreatorRows) {
|
if (!aCreatorRows && !bCreatorRows) {
|
||||||
|
@ -322,13 +359,15 @@ Zotero.Duplicates.prototype._findDuplicates = function () {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for each(var aCreatorRow in aCreatorRows) {
|
for (let i = 0; i < aCreatorRows.length; i++) {
|
||||||
var aLastName = normalizeString(aCreatorRow.lastName);
|
let aCreatorRow = aCreatorRows[i];
|
||||||
var aFirstInitial = aCreatorRow.fieldMode == 0 ? normalizeString(aCreatorRow.firstName).charAt(0) : false;
|
let aLastName = aCreatorRow.lastName;
|
||||||
|
let aFirstInitial = aCreatorRow.firstInitial;
|
||||||
|
|
||||||
for each(var bCreatorRow in bCreatorRows) {
|
for (let j = 0; j < bCreatorRows.length; j++) {
|
||||||
var bLastName = normalizeString(bCreatorRow.lastName);
|
let bCreatorRow = bCreatorRows[j];
|
||||||
var bFirstInitial = bCreatorRow.fieldMode == 0 ? normalizeString(bCreatorRow.firstName).charAt(0) : false;
|
let bLastName = bCreatorRow.lastName;
|
||||||
|
let bFirstInitial = bCreatorRow.firstInitial;
|
||||||
|
|
||||||
if (aLastName === bLastName && aFirstInitial === bFirstInitial) {
|
if (aLastName === bLastName && aFirstInitial === bFirstInitial) {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -348,12 +387,12 @@ Zotero.Duplicates.prototype._findDuplicates = function () {
|
||||||
+ "WHERE libraryID=? AND fieldID=? "
|
+ "WHERE libraryID=? AND fieldID=? "
|
||||||
+ "AND itemID NOT IN (SELECT itemID FROM deletedItems) "
|
+ "AND itemID NOT IN (SELECT itemID FROM deletedItems) "
|
||||||
+ "ORDER BY value";
|
+ "ORDER BY value";
|
||||||
var rows = Zotero.DB.query(sql, [this._libraryID, Zotero.ItemFields.getID(field)]);
|
var rows = yield Zotero.DB.queryAsync(sql, [this._libraryID, Zotero.ItemFields.getID(field)]);
|
||||||
processRows();
|
processRows(rows);
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
Zotero.debug("Found duplicates in " + (Date.now() - start) + " ms");
|
Zotero.debug("Found duplicates in " + (Date.now() - start) + " ms");
|
||||||
}
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -869,7 +869,7 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
|
||||||
// Mirror ZoteroPane.onTreeMouseDown behavior
|
// Mirror ZoteroPane.onTreeMouseDown behavior
|
||||||
var itemID = this._rows[previousRow].ref.id;
|
var itemID = this._rows[previousRow].ref.id;
|
||||||
var setItemIDs = collectionTreeRow.ref.getSetItemsByItemID(itemID);
|
var setItemIDs = collectionTreeRow.ref.getSetItemsByItemID(itemID);
|
||||||
yield this.selectItems(setItemIDs);
|
this.selectItems(setItemIDs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -1387,7 +1387,7 @@ var ZoteroPane = new function()
|
||||||
this.itemsView._itemSelectedPromiseResolver.reject(e);
|
this.itemsView._itemSelectedPromiseResolver.reject(e);
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}.bind(this));;
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user