Adds sync support for related items
Might fix (or break) other stuff, but who remembers?
This commit is contained in:
parent
19b08a604a
commit
33de40ad95
|
@ -407,11 +407,13 @@
|
||||||
<method name="seeAlsoClick">
|
<method name="seeAlsoClick">
|
||||||
<body>
|
<body>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
var seealsoList = this.item.getSeeAlso();
|
var relatedList = this.item.relatedItemsBidirectional;
|
||||||
if(seealsoList && seealsoList.length > 0)
|
if (relatedList.length > 0) {
|
||||||
this.id('seeAlsoPopup').showPopup(this.id('seeAlsoLabel'),-1,-1,'popup',0,0);
|
this.id('seeAlsoPopup').showPopup(this.id('seeAlsoLabel'),-1,-1,'popup',0,0);
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
this.id('seeAlso').add();
|
this.id('seeAlso').add();
|
||||||
|
}
|
||||||
]]>
|
]]>
|
||||||
</body>
|
</body>
|
||||||
</method>
|
</method>
|
||||||
|
|
|
@ -42,15 +42,12 @@
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
var r = "";
|
var r = "";
|
||||||
|
|
||||||
if(this.item)
|
if (this.item) {
|
||||||
{
|
var related = this.item.relatedItemsBidirectional;
|
||||||
var seealso = this.item.getSeeAlso();
|
if (related) {
|
||||||
if(seealso)
|
related = Zotero.Items.get(related);
|
||||||
{
|
for(var i = 0; i < related.length; i++) {
|
||||||
seealso = Zotero.Items.get(seealso);
|
r = r + related[i].getField('title') + ", ";
|
||||||
for(var i = 0; i < seealso.length; i++)
|
|
||||||
{
|
|
||||||
r = r + seealso[i].getField('title') + ", ";
|
|
||||||
}
|
}
|
||||||
r = r.substr(0,r.length-2);
|
r = r.substr(0,r.length-2);
|
||||||
}
|
}
|
||||||
|
@ -67,20 +64,16 @@
|
||||||
while(rows.hasChildNodes())
|
while(rows.hasChildNodes())
|
||||||
rows.removeChild(rows.firstChild);
|
rows.removeChild(rows.firstChild);
|
||||||
|
|
||||||
if(this.item)
|
if (this.item) {
|
||||||
{
|
var related = this.item.relatedItemsBidirectional;
|
||||||
var seealso = this.item.getSeeAlso();
|
if (related) {
|
||||||
|
related = Zotero.Items.get(related);
|
||||||
if(seealso)
|
for (var i = 0; i < related.length; i++) {
|
||||||
{
|
|
||||||
seealso = Zotero.Items.get(seealso);
|
|
||||||
for(var i = 0; i < seealso.length; i++)
|
|
||||||
{
|
|
||||||
var icon= document.createElement("image");
|
var icon= document.createElement("image");
|
||||||
var type = Zotero.ItemTypes.getName(seealso[i].getType());
|
var type = Zotero.ItemTypes.getName(related[i].itemTypeID);
|
||||||
if (type=='attachment')
|
if (type=='attachment')
|
||||||
{
|
{
|
||||||
switch (seealso[i].getAttachmentLinkMode())
|
switch (related[i].getAttachmentLinkMode())
|
||||||
{
|
{
|
||||||
case Zotero.Attachments.LINK_MODE_LINKED_URL:
|
case Zotero.Attachments.LINK_MODE_LINKED_URL:
|
||||||
type += '-web-link';
|
type += '-web-link';
|
||||||
|
@ -102,12 +95,13 @@
|
||||||
icon.setAttribute('src','chrome://zotero/skin/treeitem-' + type + '.png');
|
icon.setAttribute('src','chrome://zotero/skin/treeitem-' + type + '.png');
|
||||||
|
|
||||||
var label = document.createElement("label");
|
var label = document.createElement("label");
|
||||||
label.setAttribute('value', seealso[i].getField('title'));
|
label.setAttribute('value', related[i].getField('title'));
|
||||||
label.setAttribute('crop','end');
|
label.setAttribute('crop','end');
|
||||||
label.setAttribute('flex','1');
|
label.setAttribute('flex','1');
|
||||||
|
|
||||||
var box = document.createElement('box');
|
var box = document.createElement('box');
|
||||||
box.setAttribute('onclick',"this.parentNode.parentNode.parentNode.parentNode.parentNode.showItem('"+seealso[i].getID()+"')");
|
box.setAttribute('onclick',
|
||||||
|
"document.getBindingParent(this).showItem('" + related[i].id + "')");
|
||||||
box.setAttribute('class','zotero-clicky');
|
box.setAttribute('class','zotero-clicky');
|
||||||
box.setAttribute('flex','1');
|
box.setAttribute('flex','1');
|
||||||
box.appendChild(icon);
|
box.appendChild(icon);
|
||||||
|
@ -115,16 +109,17 @@
|
||||||
|
|
||||||
var remove = document.createElement("label");
|
var remove = document.createElement("label");
|
||||||
remove.setAttribute('value','-');
|
remove.setAttribute('value','-');
|
||||||
remove.setAttribute('onclick',"this.parentNode.parentNode.parentNode.parentNode.parentNode.remove('"+seealso[i].getID()+"');");
|
remove.setAttribute('onclick',
|
||||||
|
"document.getBindingParent(this).remove('" + related[i].id + "');");
|
||||||
remove.setAttribute('class','zotero-clicky');
|
remove.setAttribute('class','zotero-clicky');
|
||||||
|
|
||||||
var row = document.createElement("row");
|
var row = document.createElement("row");
|
||||||
row.appendChild(box);
|
row.appendChild(box);
|
||||||
row.appendChild(remove);
|
row.appendChild(remove);
|
||||||
row.setAttribute('id','seealso-'+seealso[i].getID());
|
row.setAttribute('id', 'seealso-' + related[i].id);
|
||||||
rows.appendChild(row);
|
rows.appendChild(row);
|
||||||
}
|
}
|
||||||
this.updateCount(seealso.length);
|
this.updateCount(related.length);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -146,8 +141,9 @@
|
||||||
{
|
{
|
||||||
for(var i = 0; i < io.dataOut.length; i++)
|
for(var i = 0; i < io.dataOut.length; i++)
|
||||||
{
|
{
|
||||||
this.item.addSeeAlso(io.dataOut[i]);
|
this.item.addRelatedItem(io.dataOut[i]);
|
||||||
}
|
}
|
||||||
|
this.item.save();
|
||||||
}
|
}
|
||||||
]]>
|
]]>
|
||||||
</body>
|
</body>
|
||||||
|
@ -156,12 +152,15 @@
|
||||||
<parameter name="id"/>
|
<parameter name="id"/>
|
||||||
<body>
|
<body>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
if(id)
|
if(id) {
|
||||||
{
|
// TODO: set attribute on reload to determine
|
||||||
this.item.removeSeeAlso(id);
|
// which of these is necessary
|
||||||
var rows = this.id('seeAlsoRows');
|
this.item.removeRelatedItem(id);
|
||||||
rows.removeChild(this.id('seealso-'+id));
|
this.item.save();
|
||||||
this.updateCount();
|
|
||||||
|
var item = Zotero.Items.get(id);
|
||||||
|
item.removeRelatedItem(this.item.id);
|
||||||
|
item.save();
|
||||||
}
|
}
|
||||||
]]>
|
]]>
|
||||||
</body>
|
</body>
|
||||||
|
@ -206,13 +205,8 @@
|
||||||
<parameter name="count"/>
|
<parameter name="count"/>
|
||||||
<body>
|
<body>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
if(count == null)
|
if (count == null) {
|
||||||
{
|
var count = this.item.relatedItemsBidirectional.length;
|
||||||
var seealso = this.item.getSeeAlso();
|
|
||||||
if(seealso)
|
|
||||||
count = seealso.length;
|
|
||||||
else
|
|
||||||
count = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var str = 'pane.item.related.count.';
|
var str = 'pane.item.related.count.';
|
||||||
|
|
|
@ -69,7 +69,9 @@ Zotero.Item.prototype._init = function () {
|
||||||
this._primaryDataLoaded = false;
|
this._primaryDataLoaded = false;
|
||||||
this._creatorsLoaded = false;
|
this._creatorsLoaded = false;
|
||||||
this._itemDataLoaded = false;
|
this._itemDataLoaded = false;
|
||||||
|
this._relatedItemsLoaded = false;
|
||||||
|
|
||||||
|
this._changed = false;
|
||||||
this._changedPrimaryData = false;
|
this._changedPrimaryData = false;
|
||||||
this._changedItemData = false;
|
this._changedItemData = false;
|
||||||
this._changedCreators = false;
|
this._changedCreators = false;
|
||||||
|
@ -87,6 +89,8 @@ Zotero.Item.prototype._init = function () {
|
||||||
this._attachmentMIMEType = null;
|
this._attachmentMIMEType = null;
|
||||||
this._attachmentCharset = null;
|
this._attachmentCharset = null;
|
||||||
this._attachmentPath = null;
|
this._attachmentPath = null;
|
||||||
|
|
||||||
|
this._relatedItems = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -100,6 +104,10 @@ Zotero.Item.prototype.__defineGetter__('firstCreator', function () { return this
|
||||||
//Zotero.Item.prototype.__defineGetter__('numNotes', function () { return this._itemID; });
|
//Zotero.Item.prototype.__defineGetter__('numNotes', function () { return this._itemID; });
|
||||||
//Zotero.Item.prototype.__defineGetter__('numAttachments', function () { return this._itemID; });
|
//Zotero.Item.prototype.__defineGetter__('numAttachments', function () { return this._itemID; });
|
||||||
|
|
||||||
|
Zotero.Item.prototype.__defineGetter__('relatedItems', function () { var ids = this._getRelatedItems(true); return ids ? ids : []; });
|
||||||
|
Zotero.Item.prototype.__defineSetter__('relatedItems', function (arr) { this._setRelatedItems(arr); });
|
||||||
|
Zotero.Item.prototype.__defineGetter__('relatedItemsReverse', function () { var ids = this._getRelatedItemsReverse(); return ids ? ids : []; });
|
||||||
|
Zotero.Item.prototype.__defineGetter__('relatedItemsBidirectional', function () { var ids = this._getRelatedItemsBidirectional(); return ids ? ids : []; });
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Deprecated -- use id property
|
* Deprecated -- use id property
|
||||||
|
@ -329,7 +337,8 @@ Zotero.Item.prototype.loadFromRow = function(row, reload) {
|
||||||
* Check if any data fields have changed since last save
|
* Check if any data fields have changed since last save
|
||||||
*/
|
*/
|
||||||
Zotero.Item.prototype.hasChanged = function() {
|
Zotero.Item.prototype.hasChanged = function() {
|
||||||
return !!(this._changedPrimaryData
|
return !!(this._changed
|
||||||
|
|| this._changedPrimaryData
|
||||||
|| this._changedCreators
|
|| this._changedCreators
|
||||||
|| this._changedItemData
|
|| this._changedItemData
|
||||||
|| this._changedNote
|
|| this._changedNote
|
||||||
|
@ -906,6 +915,69 @@ Zotero.Item.prototype.removeCreator = function(orderIndex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Zotero.Item.prototype.addRelatedItem = function (itemID) {
|
||||||
|
var parsedInt = parseInt(itemID);
|
||||||
|
if (parsedInt != itemID) {
|
||||||
|
throw ("itemID '" + itemID + "' not an integer in Zotero.Item.addRelatedItem()");
|
||||||
|
}
|
||||||
|
itemID = parsedInt;
|
||||||
|
|
||||||
|
if (itemID == this.id) {
|
||||||
|
Zotero.debug("Can't relate item to itself in Zotero.Item.addRelatedItem()", 2);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var current = this._getRelatedItems(true);
|
||||||
|
if (current && current.indexOf(itemID) != -1) {
|
||||||
|
Zotero.debug("Item " + this.id + " already related to item "
|
||||||
|
+ itemID + " in Zotero.Item.addItem()");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var item = Zotero.Items.get(itemID);
|
||||||
|
if (!item) {
|
||||||
|
throw ("Can't relate item to invalid item " + itemID
|
||||||
|
+ " in Zotero.Item.addRelatedItem()");
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
var otherCurrent = item.relatedItems;
|
||||||
|
if (otherCurrent.length && otherCurrent.indexOf(this.id) != -1) {
|
||||||
|
Zotero.debug("Other item " + itemID + " already related to item "
|
||||||
|
+ this.id + " in Zotero.Item.addItem()");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
this._prepFieldChange('relatedItems');
|
||||||
|
this._relatedItems.push(item);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Zotero.Item.prototype.removeRelatedItem = function (itemID) {
|
||||||
|
var parsedInt = parseInt(itemID);
|
||||||
|
if (parsedInt != itemID) {
|
||||||
|
throw ("itemID '" + itemID + "' not an integer in Zotero.Item.removeRelatedItem()");
|
||||||
|
}
|
||||||
|
itemID = parsedInt;
|
||||||
|
|
||||||
|
var current = this._getRelatedItems(true);
|
||||||
|
if (current) {
|
||||||
|
var index = current.indexOf(itemID);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current || index == -1) {
|
||||||
|
Zotero.debug("Item " + this.id + " isn't related to item "
|
||||||
|
+ itemID + " in Zotero.Item.removeRelatedItem()");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._prepFieldChange('relatedItems');
|
||||||
|
this._relatedItems.splice(index, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Save changes back to database
|
* Save changes back to database
|
||||||
*
|
*
|
||||||
|
@ -1232,6 +1304,59 @@ Zotero.Item.prototype.save = function() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Related items
|
||||||
|
if (this._changed.relatedItems) {
|
||||||
|
var removed = [];
|
||||||
|
var newids = [];
|
||||||
|
var currentIDs = this._getRelatedItems(true);
|
||||||
|
if (!currentIDs) {
|
||||||
|
currentIDs = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._previousData && this._previousData.related) {
|
||||||
|
for each(var id in this._previousData.related) {
|
||||||
|
if (currentIDs.indexOf(id) == -1) {
|
||||||
|
removed.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for each(var id in currentIDs) {
|
||||||
|
if (this._previousData && this._previousData.related &&
|
||||||
|
this._previousData.related.indexOf(id) != -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
newids.push(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removed.length) {
|
||||||
|
var sql = "DELETE FROM itemSeeAlso WHERE itemID=? "
|
||||||
|
+ "AND linkedItemID IN ("
|
||||||
|
+ removed.map(function () '?').join()
|
||||||
|
+ ")";
|
||||||
|
Zotero.DB.query(sql, [itemID].concat(removed));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newids.length) {
|
||||||
|
var sql = "INSERT INTO itemSeeAlso (itemID, linkedItemID) VALUES (?,?)";
|
||||||
|
var insertStatement = Zotero.DB.getStatement(sql);
|
||||||
|
|
||||||
|
for each(var linkedItemID in newids) {
|
||||||
|
insertStatement.bindInt32Parameter(0, itemID);
|
||||||
|
insertStatement.bindInt32Parameter(1, linkedItemID);
|
||||||
|
|
||||||
|
try {
|
||||||
|
insertStatement.execute();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
throw (e + ' [ERROR: ' + Zotero.DB.getLastErrorString() + ']');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Zotero.Notifier.trigger('modify', 'item', removed.concat(newids));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -1585,6 +1710,59 @@ Zotero.Item.prototype.save = function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Related items
|
||||||
|
if (this._changed.relatedItems) {
|
||||||
|
var removed = [];
|
||||||
|
var newids = [];
|
||||||
|
var currentIDs = this._getRelatedItems(true);
|
||||||
|
if (!currentIDs) {
|
||||||
|
currentIDs = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._previousData && this._previousData.related) {
|
||||||
|
for each(var id in this._previousData.related) {
|
||||||
|
if (currentIDs.indexOf(id) == -1) {
|
||||||
|
removed.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for each(var id in currentIDs) {
|
||||||
|
if (this._previousData && this._previousData.related &&
|
||||||
|
this._previousData.related.indexOf(id) != -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
newids.push(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removed.length) {
|
||||||
|
var sql = "DELETE FROM itemSeeAlso WHERE itemID=? "
|
||||||
|
+ "AND linkedItemID IN ("
|
||||||
|
+ removed.map(function () '?').join()
|
||||||
|
+ ")";
|
||||||
|
Zotero.DB.query(sql, [this.id].concat(removed));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newids.length) {
|
||||||
|
var sql = "INSERT INTO itemSeeAlso (itemID, linkedItemID) VALUES (?,?)";
|
||||||
|
var insertStatement = Zotero.DB.getStatement(sql);
|
||||||
|
|
||||||
|
for each(var linkedItemID in newids) {
|
||||||
|
insertStatement.bindInt32Parameter(0, this.id);
|
||||||
|
insertStatement.bindInt32Parameter(1, linkedItemID);
|
||||||
|
|
||||||
|
try {
|
||||||
|
insertStatement.execute();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
throw (e + ' [ERROR: ' + Zotero.DB.getLastErrorString() + ']');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Zotero.Notifier.trigger('modify', 'item', removed.concat(newids));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Zotero.History.commit();
|
//Zotero.History.commit();
|
||||||
|
@ -1625,7 +1803,6 @@ Zotero.Item.prototype.save = function() {
|
||||||
|
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
var id = this.id;
|
var id = this.id;
|
||||||
Zotero.debug('DISABLING ITEM');
|
|
||||||
this._disabled = true;
|
this._disabled = true;
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
@ -2568,116 +2745,6 @@ Zotero.Item.prototype.removeAllTags = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Methods dealing with See Also links
|
|
||||||
//
|
|
||||||
// save() is not required for See Also functions
|
|
||||||
//
|
|
||||||
Zotero.Item.prototype.addSeeAlso = function(itemID) {
|
|
||||||
if (itemID==this.id) {
|
|
||||||
Zotero.debug('Cannot add item as See Also of itself', 2);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Zotero.DB.beginTransaction();
|
|
||||||
|
|
||||||
var relatedItem = Zotero.Items.get(itemID);
|
|
||||||
|
|
||||||
if (!relatedItem) {
|
|
||||||
Zotero.DB.commitTransaction();
|
|
||||||
throw ("Cannot add invalid item " + itemID + " as See Also");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check both ways, using a UNION to take advantage of indexes
|
|
||||||
var sql = "SELECT (SELECT COUNT(*) FROM itemSeeAlso WHERE itemID=?1 AND "
|
|
||||||
+ "linkedItemID=?2) + (SELECT COUNT(*) FROM itemSeeAlso WHERE "
|
|
||||||
+ "linkedItemID=?1 AND itemID=?2)";
|
|
||||||
if (Zotero.DB.valueQuery(sql, [this.id, itemID])) {
|
|
||||||
Zotero.DB.commitTransaction();
|
|
||||||
Zotero.debug("Item " + itemID + " already linked", 2);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var notifierData = {};
|
|
||||||
notifierData[this.id] = { old: this.serialize() };
|
|
||||||
notifierData[relatedItem.id] = { old: relatedItem.serialize() };
|
|
||||||
|
|
||||||
var sql = "INSERT INTO itemSeeAlso VALUES (?,?)";
|
|
||||||
Zotero.DB.query(sql, [this.id, {int:itemID}]);
|
|
||||||
Zotero.DB.commitTransaction();
|
|
||||||
Zotero.Notifier.trigger('modify', 'item', [this.id, itemID], notifierData);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Zotero.Item.prototype.removeSeeAlso = function(itemID) {
|
|
||||||
if (!this.id) {
|
|
||||||
throw ('Cannot remove related item of unsaved item');
|
|
||||||
}
|
|
||||||
|
|
||||||
Zotero.DB.beginTransaction();
|
|
||||||
|
|
||||||
var relatedItem = Zotero.Items.get(itemID);
|
|
||||||
if (!relatedItem) {
|
|
||||||
Zotero.DB.commitTransaction();
|
|
||||||
throw ("Cannot remove invalid item " + itemID + " as See Also");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var notifierData = {};
|
|
||||||
notifierData[this.id] = { old: this.serialize() };
|
|
||||||
notifierData[relatedItem.id] = { old: relatedItem.serialize() };
|
|
||||||
|
|
||||||
var sql = "DELETE FROM itemSeeAlso WHERE itemID=? AND linkedItemID=?";
|
|
||||||
Zotero.DB.query(sql, [this.id, itemID]);
|
|
||||||
var sql = "DELETE FROM itemSeeAlso WHERE itemID=? AND linkedItemID=?";
|
|
||||||
Zotero.DB.query(sql, [itemID, this.id]);
|
|
||||||
Zotero.DB.commitTransaction();
|
|
||||||
Zotero.Notifier.trigger('modify', 'item', [this.id, itemID], notifierData);
|
|
||||||
}
|
|
||||||
|
|
||||||
Zotero.Item.prototype.removeAllRelated = function() {
|
|
||||||
if (!this.id) {
|
|
||||||
throw ('Cannot remove related items of unsaved item');
|
|
||||||
}
|
|
||||||
|
|
||||||
Zotero.DB.beginTransaction();
|
|
||||||
var relateds = this.getSeeAlso();
|
|
||||||
if (!relateds) {
|
|
||||||
Zotero.DB.commitTransaction();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var notifierData = {};
|
|
||||||
notifierData[this.id] = { old: this.serialize() };
|
|
||||||
|
|
||||||
for each(var id in relateds) {
|
|
||||||
var item = Zotero.Items.get(id);
|
|
||||||
if (item) {
|
|
||||||
notifierData[item.id] = { old: item.serialize() };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Zotero.DB.query("DELETE FROM itemSeeAlso WHERE itemID=?", this.id);
|
|
||||||
Zotero.DB.query("DELETE FROM itemSeeAlso WHERE linkedItemID=?", this.id);
|
|
||||||
Zotero.DB.commitTransaction();
|
|
||||||
|
|
||||||
var ids = [this.id].concat(relateds);
|
|
||||||
|
|
||||||
Zotero.Notifier.trigger('modify', 'item', ids, notifierData);
|
|
||||||
}
|
|
||||||
|
|
||||||
Zotero.Item.prototype.getSeeAlso = function() {
|
|
||||||
if (!this.id) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Check both ways, using a UNION to take advantage of indexes
|
|
||||||
var sql ="SELECT linkedItemID FROM itemSeeAlso WHERE itemID=?1 UNION "
|
|
||||||
+ "SELECT itemID FROM itemSeeAlso WHERE linkedItemID=?1";
|
|
||||||
return Zotero.DB.columnQuery(sql, this.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Zotero.Item.prototype.getImageSrc = function() {
|
Zotero.Item.prototype.getImageSrc = function() {
|
||||||
var itemType = Zotero.ItemTypes.getName(this.itemTypeID);
|
var itemType = Zotero.ItemTypes.getName(this.itemTypeID);
|
||||||
if (itemType == 'attachment') {
|
if (itemType == 'attachment') {
|
||||||
|
@ -2886,10 +2953,9 @@ Zotero.Item.prototype.clone = function(includePrimary) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj.seeAlso) {
|
if (obj.related) {
|
||||||
for each(var id in obj.seeAlso) {
|
// DEBUG: this will add reverse-only relateds too
|
||||||
newItem.addSeeAlso(id)
|
newItem.relatedItems = obj.related;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.DB.commitTransaction();
|
Zotero.DB.commitTransaction();
|
||||||
|
@ -3017,16 +3083,16 @@ Zotero.Item.prototype.erase = function(deleteChildren) {
|
||||||
Zotero.DB.query(sql);
|
Zotero.DB.query(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flag See Also links for notification
|
// Flag related items for notification
|
||||||
var relateds = this.getSeeAlso();
|
var relateds = this._getRelatedItemsBidirectional();
|
||||||
if (relateds) {
|
if (relateds) {
|
||||||
for each(var id in relateds) {
|
for each(var id in relateds) {
|
||||||
var i = Zotero.Items.get(id);
|
var relatedItem = Zotero.Items.get(id);
|
||||||
if (!changedItemsNotifierData[i.id]) {
|
if (changedItems.indexOf(id) != -1) {
|
||||||
changedItemsNotifierData[i.id] = { old: i.serialize() };
|
changedItemsNotifierData[id] = { old: relatedItem.serialize() };
|
||||||
|
changedItems.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
changedItems = changedItems.concat(relateds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear fulltext cache
|
// Clear fulltext cache
|
||||||
|
@ -3191,7 +3257,7 @@ Zotero.Item.prototype.toArray = function (mode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
arr.related = this.getSeeAlso();
|
arr.related = this._getRelatedItemsBidirectional();
|
||||||
if (!arr.related) {
|
if (!arr.related) {
|
||||||
arr.related = [];
|
arr.related = [];
|
||||||
}
|
}
|
||||||
|
@ -3319,10 +3385,10 @@ Zotero.Item.prototype.serialize = function(mode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
arr.related = this.getSeeAlso();
|
var related = this._getRelatedItems(true);
|
||||||
if (!arr.related) {
|
var reverse = this._getRelatedItemsReverse();
|
||||||
arr.related = [];
|
arr.related = related ? related : [];
|
||||||
}
|
arr.relatedReverse = reverse ? reverse : [];
|
||||||
|
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
@ -3394,6 +3460,196 @@ Zotero.Item.prototype._loadItemData = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Zotero.Item.prototype._loadRelatedItems = function() {
|
||||||
|
if (!this.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._primaryDataLoaded) {
|
||||||
|
this.loadPrimaryData(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
var sql = "SELECT linkedItemID FROM itemSeeAlso WHERE itemID=?";
|
||||||
|
var ids = Zotero.DB.columnQuery(sql, this.id);
|
||||||
|
|
||||||
|
this._relatedItems = [];
|
||||||
|
|
||||||
|
if (ids) {
|
||||||
|
for each(var id in ids) {
|
||||||
|
this._relatedItems.push(Zotero.Items.get(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._relatedItemsLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns related items this item point to
|
||||||
|
*
|
||||||
|
* @param bool asIDs Return as itemIDs
|
||||||
|
* @return array Array of itemIDs, or FALSE if none
|
||||||
|
*/
|
||||||
|
Zotero.Item.prototype._getRelatedItems = function (asIDs) {
|
||||||
|
if (!this._relatedItemsLoaded) {
|
||||||
|
this._loadRelatedItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._relatedItems.length == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return itemIDs
|
||||||
|
if (asIDs) {
|
||||||
|
var ids = [];
|
||||||
|
for each(var item in this._relatedItems) {
|
||||||
|
ids.push(item.id);
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return Zotero.Item objects
|
||||||
|
var objs = [];
|
||||||
|
for each(var item in this._relatedItems) {
|
||||||
|
objs.push(item);
|
||||||
|
}
|
||||||
|
return objs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns related items that point to this item
|
||||||
|
*
|
||||||
|
* @return array Array of itemIDs, or FALSE if none
|
||||||
|
*/
|
||||||
|
Zotero.Item.prototype._getRelatedItemsReverse = function () {
|
||||||
|
if (!this.id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sql = "SELECT itemID FROM itemSeeAlso WHERE linkedItemID=?";
|
||||||
|
return Zotero.DB.columnQuery(sql, this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns related items this item points to and that point to this item
|
||||||
|
*
|
||||||
|
* @return array|bool Array of itemIDs, or false if none
|
||||||
|
*/
|
||||||
|
Zotero.Item.prototype._getRelatedItemsBidirectional = function () {
|
||||||
|
var related = this._getRelatedItems(true);
|
||||||
|
var reverse = this._getRelatedItemsReverse();
|
||||||
|
if (reverse) {
|
||||||
|
if (!related) {
|
||||||
|
return reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
for each(var id in reverse) {
|
||||||
|
if (related.indexOf(id) == -1) {
|
||||||
|
related.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!related) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return related;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Zotero.Item.prototype._setRelatedItems = function (itemIDs) {
|
||||||
|
if (!this._relatedItemsLoaded) {
|
||||||
|
this._loadRelatedItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemIDs.constructor.name != 'Array') {
|
||||||
|
throw ('ids must be an array in Zotero.Items._setRelatedItems()');
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentIDs = this._getRelatedItems(true);
|
||||||
|
if (!currentIDs) {
|
||||||
|
currentIDs = [];
|
||||||
|
}
|
||||||
|
var oldIDs = []; // children being kept
|
||||||
|
var newIDs = []; // new children
|
||||||
|
|
||||||
|
if (itemIDs.length == 0) {
|
||||||
|
if (currentIDs.length == 0) {
|
||||||
|
Zotero.debug('No related items added', 4);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (var i in itemIDs) {
|
||||||
|
var id = itemIDs[i];
|
||||||
|
var parsedInt = parseInt(id);
|
||||||
|
if (parsedInt != id) {
|
||||||
|
throw ("itemID '" + id + "' not an integer in Zotero.Item.addRelatedItem()");
|
||||||
|
}
|
||||||
|
id = parsedInt;
|
||||||
|
|
||||||
|
if (id == this.id) {
|
||||||
|
Zotero.debug("Can't relate item to itself in Zotero.Item._setRelatedItems()", 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentIDs.indexOf(id) != -1) {
|
||||||
|
Zotero.debug("Item " + this.id + " is already related to item " + id);
|
||||||
|
oldIDs.push(id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var item = Zotero.Items.get(id);
|
||||||
|
if (!item) {
|
||||||
|
throw ("Can't relate item to invalid item " + id
|
||||||
|
+ " in Zotero.Item._setRelatedItems()");
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
var otherCurrent = item.relatedItems;
|
||||||
|
if (otherCurrent.length && otherCurrent.indexOf(this.id) != -1) {
|
||||||
|
Zotero.debug("Other item " + id + " already related to item "
|
||||||
|
+ this.id + " in Zotero.Item._setRelatedItems()");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
newIDs.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark as changed if new or removed ids
|
||||||
|
if (newIDs.length > 0 || oldIDs.length != currentIDs.length) {
|
||||||
|
this._prepFieldChange('relatedItems');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Zotero.debug('Related items not changed in Zotero.Item._setRelatedItems()', 4);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
newIDs = oldIDs.concat(newIDs);
|
||||||
|
this._relatedItems = [];
|
||||||
|
for each(var itemID in newIDs) {
|
||||||
|
this._relatedItems.push(Zotero.Items.get(itemID));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: use for stuff other than related items
|
||||||
|
Zotero.Item.prototype._prepFieldChange = function (field) {
|
||||||
|
if (!this._changed) {
|
||||||
|
this._changed = {};
|
||||||
|
}
|
||||||
|
this._changed[field] = true;
|
||||||
|
|
||||||
|
// Save a copy of the data before changing
|
||||||
|
if (this.id && this.exists() && !this._previousData) {
|
||||||
|
this._previousData = this.serialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Zotero.Item.prototype._generateKey = function () {
|
Zotero.Item.prototype._generateKey = function () {
|
||||||
return Zotero.ID.getKey();
|
return Zotero.ID.getKey();
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
Zotero.Items = new function() {
|
Zotero.Items = new function() {
|
||||||
// Privileged methods
|
// Privileged methods
|
||||||
this.get = get;
|
this.get = get;
|
||||||
|
this.exist = exist;
|
||||||
this.getAll = getAll;
|
this.getAll = getAll;
|
||||||
this.getUpdated = getUpdated;
|
this.getUpdated = getUpdated;
|
||||||
this.add = add;
|
this.add = add;
|
||||||
|
@ -100,6 +101,14 @@ Zotero.Items = new function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function exist(itemIDs) {
|
||||||
|
var sql = "SELECT itemID FROM items WHERE itemID IN ("
|
||||||
|
+ itemIDs.map(function () '?').join() + ")";
|
||||||
|
var exist = Zotero.DB.columnQuery(sql, itemIDs);
|
||||||
|
return exist ? exist : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns all items in the database
|
* Returns all items in the database
|
||||||
*
|
*
|
||||||
|
|
|
@ -167,64 +167,6 @@ Zotero.Tag.prototype.getLinkedItems = function (asIDs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Zotero.Tag.prototype._setLinkedItems = function (itemIDs) {
|
|
||||||
if (!this._linkedItemsLoaded) {
|
|
||||||
this._loadLinkedItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itemIDs.constructor.name != 'Array') {
|
|
||||||
throw ('ids must be an array in Zotero.Tag._setLinkedItems()');
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentIDs = this.getLinkedItems(true);
|
|
||||||
if (!currentIDs) {
|
|
||||||
currentIDs = [];
|
|
||||||
}
|
|
||||||
var oldIDs = []; // children being kept
|
|
||||||
var newIDs = []; // new children
|
|
||||||
|
|
||||||
if (itemIDs.length == 0) {
|
|
||||||
if (currentIDs.length == 0) {
|
|
||||||
Zotero.debug('No linked items added', 4);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (var i in itemIDs) {
|
|
||||||
var id = parseInt(itemIDs[i]);
|
|
||||||
if (isNaN(id)) {
|
|
||||||
throw ("Invalid itemID '" + itemIDs[i]
|
|
||||||
+ "' in Zotero.Tag._setLinkedItems()");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentIDs.indexOf(id) != -1) {
|
|
||||||
Zotero.debug("Item " + itemIDs[i]
|
|
||||||
+ " is already linked to tag " + this.id);
|
|
||||||
oldIDs.push(id);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
newIDs.push(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark as changed if new or removed ids
|
|
||||||
if (newIDs.length > 0 || oldIDs.length != currentIDs.length) {
|
|
||||||
this._prepFieldChange('linkedItems');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Zotero.debug('Linked items not changed in Zotero.Tag._setLinkedItems()', 4);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
newIDs = oldIDs.concat(newIDs);
|
|
||||||
|
|
||||||
var items = Zotero.Items.get(itemIDs);
|
|
||||||
this._linkedItems = items ? items : [];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Zotero.Tag.prototype.addItem = function (itemID) {
|
Zotero.Tag.prototype.addItem = function (itemID) {
|
||||||
var current = this.getLinkedItems(true);
|
var current = this.getLinkedItems(true);
|
||||||
if (current && current.indexOf(itemID) != -1) {
|
if (current && current.indexOf(itemID) != -1) {
|
||||||
|
@ -524,6 +466,64 @@ Zotero.Tag.prototype._loadLinkedItems = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Zotero.Tag.prototype._setLinkedItems = function (itemIDs) {
|
||||||
|
if (!this._linkedItemsLoaded) {
|
||||||
|
this._loadLinkedItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemIDs.constructor.name != 'Array') {
|
||||||
|
throw ('ids must be an array in Zotero.Tag._setLinkedItems()');
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentIDs = this.getLinkedItems(true);
|
||||||
|
if (!currentIDs) {
|
||||||
|
currentIDs = [];
|
||||||
|
}
|
||||||
|
var oldIDs = []; // children being kept
|
||||||
|
var newIDs = []; // new children
|
||||||
|
|
||||||
|
if (itemIDs.length == 0) {
|
||||||
|
if (currentIDs.length == 0) {
|
||||||
|
Zotero.debug('No linked items added', 4);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (var i in itemIDs) {
|
||||||
|
var id = parseInt(itemIDs[i]);
|
||||||
|
if (isNaN(id)) {
|
||||||
|
throw ("Invalid itemID '" + itemIDs[i]
|
||||||
|
+ "' in Zotero.Tag._setLinkedItems()");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentIDs.indexOf(id) != -1) {
|
||||||
|
Zotero.debug("Item " + itemIDs[i]
|
||||||
|
+ " is already linked to tag " + this.id);
|
||||||
|
oldIDs.push(id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
newIDs.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark as changed if new or removed ids
|
||||||
|
if (newIDs.length > 0 || oldIDs.length != currentIDs.length) {
|
||||||
|
this._prepFieldChange('linkedItems');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Zotero.debug('Linked items not changed in Zotero.Tag._setLinkedItems()', 4);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
newIDs = oldIDs.concat(newIDs);
|
||||||
|
|
||||||
|
var items = Zotero.Items.get(itemIDs);
|
||||||
|
this._linkedItems = items ? items : [];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Zotero.Tag.prototype._prepFieldChange = function (field) {
|
Zotero.Tag.prototype._prepFieldChange = function (field) {
|
||||||
if (!this._changed) {
|
if (!this._changed) {
|
||||||
this._changed = {};
|
this._changed = {};
|
||||||
|
|
|
@ -1067,6 +1067,7 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
this.buildUploadXML = buildUploadXML;
|
this.buildUploadXML = buildUploadXML;
|
||||||
this.itemToXML = itemToXML;
|
this.itemToXML = itemToXML;
|
||||||
this.xmlToItem = xmlToItem;
|
this.xmlToItem = xmlToItem;
|
||||||
|
this.removeMissingRelatedItems = removeMissingRelatedItems;
|
||||||
this.collectionToXML = collectionToXML;
|
this.collectionToXML = collectionToXML;
|
||||||
this.xmlToCollection = xmlToCollection;
|
this.xmlToCollection = xmlToCollection;
|
||||||
this.creatorToXML = creatorToXML;
|
this.creatorToXML = creatorToXML;
|
||||||
|
@ -1087,6 +1088,7 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var remoteCreatorStore = {};
|
var remoteCreatorStore = {};
|
||||||
|
var relatedItemsStore = {};
|
||||||
|
|
||||||
Zotero.DB.beginTransaction();
|
Zotero.DB.beginTransaction();
|
||||||
|
|
||||||
|
@ -1100,21 +1102,23 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.debug("Processing remotely changed " + types);
|
|
||||||
|
|
||||||
var toSaveParents = [];
|
var toSaveParents = [];
|
||||||
var toSaveChildren = [];
|
var toSaveChildren = [];
|
||||||
var toDeleteParents = [];
|
var toDeleteParents = [];
|
||||||
var toDeleteChildren = [];
|
var toDeleteChildren = [];
|
||||||
var toReconcile = [];
|
var toReconcile = [];
|
||||||
|
|
||||||
|
//
|
||||||
|
// Handle modified objects
|
||||||
|
//
|
||||||
|
Zotero.debug("Processing remotely changed " + types);
|
||||||
|
|
||||||
typeloop:
|
typeloop:
|
||||||
for each(var xmlNode in xml[types][type]) {
|
for each(var xmlNode in xml[types][type]) {
|
||||||
|
var localDelete = false;
|
||||||
|
|
||||||
// Get local object with same id
|
// Get local object with same id
|
||||||
var obj = Zotero[Types].get(parseInt(xmlNode.@id));
|
var obj = Zotero[Types].get(parseInt(xmlNode.@id));
|
||||||
|
|
||||||
// TODO: check local deleted items for possible conflict
|
|
||||||
|
|
||||||
if (obj) {
|
if (obj) {
|
||||||
// Key match -- same item
|
// Key match -- same item
|
||||||
if (obj.key == xmlNode.@key.toString()) {
|
if (obj.key == xmlNode.@key.toString()) {
|
||||||
|
@ -1130,6 +1134,24 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
// linked to a creator whose id changed)
|
// linked to a creator whose id changed)
|
||||||
|| uploadIDs.updated[types].indexOf(obj.id) != -1) {
|
|| uploadIDs.updated[types].indexOf(obj.id) != -1) {
|
||||||
|
|
||||||
|
// Merge and store related items, since CR doesn't
|
||||||
|
// affect related items
|
||||||
|
if (type == 'item') {
|
||||||
|
// TODO: skip conflict if only related items changed
|
||||||
|
|
||||||
|
var related = xmlNode.related.toString();
|
||||||
|
related = related ? related.split(' ') : [];
|
||||||
|
for each(var relID in obj.relatedItems) {
|
||||||
|
if (related.indexOf(relID) == -1) {
|
||||||
|
related.push(relID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (related.length) {
|
||||||
|
relatedItemsStore[obj.id] = related;
|
||||||
|
}
|
||||||
|
Zotero.Sync.Server.Data.removeMissingRelatedItems(xmlNode);
|
||||||
|
}
|
||||||
|
|
||||||
var remoteObj = Zotero.Sync.Server.Data['xmlTo' + Type](xmlNode);
|
var remoteObj = Zotero.Sync.Server.Data['xmlTo' + Type](xmlNode);
|
||||||
|
|
||||||
// Some types we don't bother to reconcile
|
// Some types we don't bother to reconcile
|
||||||
|
@ -1138,24 +1160,31 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
Zotero.Sync.addToUpdated(uploadIDs.updated.items, obj.id);
|
Zotero.Sync.addToUpdated(uploadIDs.updated.items, obj.id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
obj = Zotero.Sync.Server.Data['xmlTo' + Type](xmlNode, obj);
|
// Overwrite local below
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Mark other types for conflict resolution
|
// Mark other types for conflict resolution
|
||||||
else {
|
else {
|
||||||
/*
|
// Skip item if dateModified is the only modified
|
||||||
// For now, show item conflicts even if only
|
// field (and no linked creators changed)
|
||||||
// dateModified changed, since we need to handle
|
if (type == 'item') {
|
||||||
// creator conflicts there
|
|
||||||
if (type != 'item') {
|
|
||||||
// Skip if only dateModified changed
|
|
||||||
var diff = obj.diff(remoteObj, false, true);
|
var diff = obj.diff(remoteObj, false, true);
|
||||||
if (!diff) {
|
if (!diff) {
|
||||||
|
// Check if creators changed
|
||||||
|
var creatorsChanged = false;
|
||||||
|
var creators = obj.getCreators();
|
||||||
|
creators = creators.concat(remoteObj.getCreators());
|
||||||
|
for each(var creator in creators) {
|
||||||
|
if (remoteCreatorStore[obj.id]) {
|
||||||
|
creatorsChanged = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!creatorsChanged) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
}
|
||||||
|
|
||||||
// Will be handled by item CR for now
|
// Will be handled by item CR for now
|
||||||
if (type == 'creator') {
|
if (type == 'creator') {
|
||||||
|
@ -1178,17 +1207,14 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Local object hasn't been modified -- overwrite
|
|
||||||
else {
|
// Overwrite local below
|
||||||
obj = Zotero.Sync.Server.Data['xmlTo' + Type](xmlNode, obj);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key mismatch -- different objects with same id,
|
// Key mismatch -- different objects with same id,
|
||||||
// so change id of local object
|
// so change id of local object
|
||||||
else {
|
else {
|
||||||
var oldID = parseInt(xmlNode.@id);
|
var oldID = parseInt(xmlNode.@id);
|
||||||
|
|
||||||
var newID = Zotero.ID.get(types, true);
|
var newID = Zotero.ID.get(types, true);
|
||||||
|
|
||||||
Zotero.debug("Changing " + type + " " + oldID + " id to " + newID);
|
Zotero.debug("Changing " + type + " " + oldID + " id to " + newID);
|
||||||
|
@ -1222,6 +1248,9 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
// Add items linked to creators to updated array,
|
// Add items linked to creators to updated array,
|
||||||
// since their timestamps will be set to the
|
// since their timestamps will be set to the
|
||||||
// transaction timestamp
|
// transaction timestamp
|
||||||
|
//
|
||||||
|
// Note: Don't need to change collection children or
|
||||||
|
// related items, since they're stored as objects
|
||||||
if (type == 'creator') {
|
if (type == 'creator') {
|
||||||
var linkedItems = obj.getLinkedItems();
|
var linkedItems = obj.getLinkedItems();
|
||||||
if (linkedItems) {
|
if (linkedItems) {
|
||||||
|
@ -1229,22 +1258,18 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Note: Don't need to change collection children
|
|
||||||
// since they're stored as objects
|
|
||||||
|
|
||||||
uploadIDs.changed[types][oldID] = {
|
uploadIDs.changed[types][oldID] = {
|
||||||
oldID: oldID,
|
oldID: oldID,
|
||||||
newID: newID
|
newID: newID
|
||||||
};
|
};
|
||||||
|
|
||||||
// Process new item
|
obj = null;
|
||||||
obj = Zotero.Sync.Server.Data['xmlTo' + Type](xmlNode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Object doesn't exist
|
|
||||||
|
// Object doesn't exist locally
|
||||||
else {
|
else {
|
||||||
// Reconcile locally deleted objects
|
// Check if object has been deleted locally
|
||||||
for each(var pair in uploadIDs.deleted[types]) {
|
for each(var pair in uploadIDs.deleted[types]) {
|
||||||
if (pair.id != parseInt(xmlNode.@id) ||
|
if (pair.id != parseInt(xmlNode.@id) ||
|
||||||
pair.key != xmlNode.@key.toString()) {
|
pair.key != xmlNode.@key.toString()) {
|
||||||
|
@ -1258,24 +1283,31 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
throw ('Delete reconciliation unimplemented for ' + types);
|
throw ('Delete reconciliation unimplemented for ' + types);
|
||||||
}
|
}
|
||||||
|
|
||||||
var remoteObj = Zotero.Sync.Server.Data['xmlTo' + Type](xmlNode);
|
localDelete = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporarily remove and store related items that don't yet exist
|
||||||
|
if (type == 'item') {
|
||||||
|
var missing = Zotero.Sync.Server.Data.removeMissingRelatedItems(xmlNode);
|
||||||
|
if (missing.length) {
|
||||||
|
relatedItemsStore[xmlNode.@id] = missing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create or overwrite locally
|
||||||
|
obj = Zotero.Sync.Server.Data['xmlTo' + Type](xmlNode, obj);
|
||||||
|
|
||||||
|
if (localDelete) {
|
||||||
// TODO: order reconcile by parent/child?
|
// TODO: order reconcile by parent/child?
|
||||||
|
|
||||||
toReconcile.push([
|
toReconcile.push([
|
||||||
'deleted',
|
'deleted',
|
||||||
remoteObj
|
obj
|
||||||
]);
|
]);
|
||||||
|
|
||||||
continue typeloop;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create locally
|
|
||||||
obj = Zotero.Sync.Server.Data['xmlTo' + Type](xmlNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Child items have to be saved after parent items
|
// Child items have to be saved after parent items
|
||||||
if (type == 'item' && obj.getSource()) {
|
else if (type == 'item' && obj.getSource()) {
|
||||||
toSaveChildren.push(obj);
|
toSaveChildren.push(obj);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1348,6 +1380,7 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
for each(var obj in io.dataOut) {
|
for each(var obj in io.dataOut) {
|
||||||
// TODO: do we need to make sure item isn't already being saved?
|
// TODO: do we need to make sure item isn't already being saved?
|
||||||
|
|
||||||
|
// Handle items deleted during merge
|
||||||
if (obj.ref == 'deleted') {
|
if (obj.ref == 'deleted') {
|
||||||
// Deleted item was remote
|
// Deleted item was remote
|
||||||
if (obj.left != 'deleted') {
|
if (obj.left != 'deleted') {
|
||||||
|
@ -1358,6 +1391,10 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
toDeleteChildren.push(obj.id);
|
toDeleteChildren.push(obj.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (relatedItemsStore[obj.id]) {
|
||||||
|
delete relatedItemsStore[obj.id];
|
||||||
|
}
|
||||||
|
|
||||||
uploadIDs.deleted[types].push({
|
uploadIDs.deleted[types].push({
|
||||||
id: obj.id,
|
id: obj.id,
|
||||||
key: obj.left.key
|
key: obj.left.key
|
||||||
|
@ -1394,11 +1431,9 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == 'collection') {
|
||||||
// Sort collections in order of parent collections,
|
// Sort collections in order of parent collections,
|
||||||
// so referenced parent collections always exist when saving
|
// so referenced parent collections always exist when saving
|
||||||
if (type == 'collection') {
|
|
||||||
var collections = [];
|
|
||||||
|
|
||||||
var cmp = function (a, b) {
|
var cmp = function (a, b) {
|
||||||
var pA = a.parent;
|
var pA = a.parent;
|
||||||
var pB = b.parent;
|
var pB = b.parent;
|
||||||
|
@ -1408,35 +1443,45 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
return (pA < pB) ? -1 : 1;
|
return (pA < pB) ? -1 : 1;
|
||||||
};
|
};
|
||||||
toSaveParents.sort(cmp);
|
toSaveParents.sort(cmp);
|
||||||
}
|
|
||||||
|
|
||||||
Zotero.debug('Saving merged ' + types);
|
// Temporarily remove and store subcollections before saving
|
||||||
|
// since referenced collections may not exist yet
|
||||||
|
var collections = [];
|
||||||
for each(var obj in toSaveParents) {
|
for each(var obj in toSaveParents) {
|
||||||
// If collection, temporarily clear subcollections before
|
var colIDs = obj.getChildCollections(true);
|
||||||
// saving since referenced collections may not exist yet
|
if (!colIDs.length) {
|
||||||
if (type == 'collection') {
|
continue;
|
||||||
var childCollections = obj.getChildCollections(true);
|
}
|
||||||
if (childCollections) {
|
// TODO: use exist(), like related items above
|
||||||
obj.childCollections = [];
|
obj.childCollections = [];
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var id = obj.save();
|
|
||||||
|
|
||||||
// Store subcollections
|
|
||||||
if (type == 'collection') {
|
|
||||||
collections.push({
|
collections.push({
|
||||||
obj: obj,
|
obj: obj,
|
||||||
childCollections: childCollections
|
childCollections: colIDs
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save objects
|
||||||
|
Zotero.debug('Saving merged ' + types);
|
||||||
|
for each(var obj in toSaveParents) {
|
||||||
|
obj.save();
|
||||||
|
}
|
||||||
for each(var obj in toSaveChildren) {
|
for each(var obj in toSaveChildren) {
|
||||||
obj.save();
|
obj.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set subcollections
|
// Add back related items (which now exist)
|
||||||
if (type == 'collection') {
|
if (type == 'item') {
|
||||||
|
for (var itemID in relatedItemsStore) {
|
||||||
|
item = Zotero.Items.get(itemID);
|
||||||
|
for each(var id in relatedItemsStore[itemID]) {
|
||||||
|
item.addRelatedItem(id);
|
||||||
|
}
|
||||||
|
item.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add back subcollections
|
||||||
|
else if (type == 'collection') {
|
||||||
for each(var collection in collections) {
|
for each(var collection in collections) {
|
||||||
if (collection.collections) {
|
if (collection.collections) {
|
||||||
collection.obj.childCollections = collection.collections;
|
collection.obj.childCollections = collection.collections;
|
||||||
|
@ -1631,6 +1676,11 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
xml.creator += newCreator;
|
xml.creator += newCreator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Related items
|
||||||
|
if (item.related.length) {
|
||||||
|
xml.related = item.related.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1645,7 +1695,7 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
function xmlToItem(xmlItem, item, skipPrimary) {
|
function xmlToItem(xmlItem, item, skipPrimary) {
|
||||||
if (!item) {
|
if (!item) {
|
||||||
if (skipPrimary) {
|
if (skipPrimary) {
|
||||||
item = new Zotero.Item(null);
|
item = new Zotero.Item;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
item = new Zotero.Item(parseInt(xmlItem.@id));
|
item = new Zotero.Item(parseInt(xmlItem.@id));
|
||||||
|
@ -1740,10 +1790,31 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Related items
|
||||||
|
var related = xmlItem.related.toString();
|
||||||
|
item.relatedItems = related ? related.split(' ') : [];
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function removeMissingRelatedItems(xmlNode) {
|
||||||
|
var missing = [];
|
||||||
|
var related = xmlNode.related.toString();
|
||||||
|
var relIDs = related ? related.split(' ') : [];
|
||||||
|
if (relIDs.length) {
|
||||||
|
var exist = Zotero.Items.exist(relIDs);
|
||||||
|
for each(var id in relIDs) {
|
||||||
|
if (exist.indexOf(id) == -1) {
|
||||||
|
missing.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xmlNode.related = exist.join(' ');
|
||||||
|
}
|
||||||
|
return missing;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function collectionToXML(collection) {
|
function collectionToXML(collection) {
|
||||||
var xml = <collection/>;
|
var xml = <collection/>;
|
||||||
|
|
||||||
|
|
|
@ -1017,9 +1017,10 @@ Zotero.Translate.prototype._itemTagsAndSeeAlso = function(item, newItem) {
|
||||||
if(item.seeAlso) {
|
if(item.seeAlso) {
|
||||||
for each(var seeAlso in item.seeAlso) {
|
for each(var seeAlso in item.seeAlso) {
|
||||||
if(this._IDMap[seeAlso]) {
|
if(this._IDMap[seeAlso]) {
|
||||||
newItem.addSeeAlso(this._IDMap[seeAlso]);
|
newItem.addRelatedItem(this._IDMap[seeAlso]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
newItem.save();
|
||||||
}
|
}
|
||||||
if(item.tags) {
|
if(item.tags) {
|
||||||
var tagsToAdd = {};
|
var tagsToAdd = {};
|
||||||
|
@ -1407,9 +1408,10 @@ Zotero.Translate.prototype._itemDone = function(item, attachedTo) {
|
||||||
if(item.seeAlso) {
|
if(item.seeAlso) {
|
||||||
for each(var seeAlso in item.seeAlso) {
|
for each(var seeAlso in item.seeAlso) {
|
||||||
if(this._IDMap[seeAlso]) {
|
if(this._IDMap[seeAlso]) {
|
||||||
newItem.addSeeAlso(this._IDMap[seeAlso]);
|
newItem.addRelatedItem(this._IDMap[seeAlso]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
newItem.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle tags, if this is an import translation or automatic tagging is
|
// handle tags, if this is an import translation or automatic tagging is
|
||||||
|
|
Loading…
Reference in New Issue
Block a user