Deasyncification 🔙 😢
While trying to get translation and citing working with asynchronously generated data, we realized that drag-and-drop support was going to be...problematic. Firefox only supports synchronous methods for providing drag data (unlike, it seems, the DataTransferItem interface supported by Chrome), which means that we'd need to preload all relevant data on item selection (bounded by export.quickCopy.dragLimit) and keep the translate/cite methods synchronous (or maintain two separate versions). What we're trying instead is doing what I said in #518 we weren't going to do: loading most object data on startup and leaving many more functions synchronous. Essentially, this takes the various load*() methods described in #518, moves them to startup, and makes them operate on entire libraries rather than individual objects. The obvious downside here (other than undoing much of the work of the last many months) is that it increases startup time, potentially quite a lot for larger libraries. On my laptop, with a 3,000-item library, this adds about 3 seconds to startup time. I haven't yet tested with larger libraries. But I'm hoping that we can optimize this further to reduce that delay. Among other things, this is loading data for all libraries, when it should be able to load data only for the library being viewed. But this is also fundamentally just doing some SELECT queries and storing the results, so it really shouldn't need to be that slow (though performance may be bounded a bit here by XPCOM overhead). If we can make this fast enough, it means that third-party plugins should be able to remain much closer to their current designs. (Some things, including saving, will still need to be made asynchronous.)
This commit is contained in:
parent
d871e2540b
commit
daf4a8fe4d
57 changed files with 1648 additions and 1607 deletions
|
@ -417,48 +417,41 @@
|
|||
</method>
|
||||
<method name="updateTagsSummary">
|
||||
<body><![CDATA[
|
||||
Zotero.spawn(function* () {
|
||||
var v = yield this.id('tags').summary;
|
||||
var v = this.id('tags').summary;
|
||||
|
||||
if (!v || v == "") {
|
||||
v = "[" + Zotero.getString('pane.item.noteEditor.clickHere') + "]";
|
||||
}
|
||||
if (!v || v == "") {
|
||||
v = "[" + Zotero.getString('pane.item.noteEditor.clickHere') + "]";
|
||||
}
|
||||
|
||||
this.id('tagsLabel').value = Zotero.getString('itemFields.tags')
|
||||
+ Zotero.getString('punctuation.colon');
|
||||
this.id('tagsClick').value = v;
|
||||
}, this);
|
||||
this.id('tagsLabel').value = Zotero.getString('itemFields.tags')
|
||||
+ Zotero.getString('punctuation.colon');
|
||||
this.id('tagsClick').value = v;
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="relatedClick">
|
||||
<body><![CDATA[
|
||||
Zotero.spawn(function* () {
|
||||
yield this.item.loadRelations();
|
||||
var relatedList = this.item.relatedItems;
|
||||
if (relatedList.length > 0) {
|
||||
var x = this.boxObject.screenX;
|
||||
var y = this.boxObject.screenY;
|
||||
this.id('relatedPopup').openPopupAtScreen(x, y, false);
|
||||
}
|
||||
else {
|
||||
this.id('related').add();
|
||||
}
|
||||
}, this);
|
||||
var relatedList = this.item.relatedItems;
|
||||
if (relatedList.length > 0) {
|
||||
var x = this.boxObject.screenX;
|
||||
var y = this.boxObject.screenY;
|
||||
this.id('relatedPopup').openPopupAtScreen(x, y, false);
|
||||
}
|
||||
else {
|
||||
this.id('related').add();
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="updateRelatedSummary">
|
||||
<body><![CDATA[
|
||||
Zotero.spawn(function* () {
|
||||
var v = yield this.id('related').summary;
|
||||
var v = this.id('related').summary;
|
||||
|
||||
if (!v || v == "") {
|
||||
v = "[" + Zotero.getString('pane.item.noteEditor.clickHere') + "]";
|
||||
}
|
||||
if (!v || v == "") {
|
||||
v = "[" + Zotero.getString('pane.item.noteEditor.clickHere') + "]";
|
||||
}
|
||||
|
||||
this.id('relatedLabel').value = Zotero.getString('itemFields.related')
|
||||
+ Zotero.getString('punctuation.colon');
|
||||
this.id('relatedClick').value = v;
|
||||
}, this)
|
||||
this.id('relatedLabel').value = Zotero.getString('itemFields.related')
|
||||
+ Zotero.getString('punctuation.colon');
|
||||
this.id('relatedClick').value = v;
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="parentClick">
|
||||
|
|
|
@ -74,25 +74,20 @@
|
|||
<property name="summary">
|
||||
<getter>
|
||||
<![CDATA[
|
||||
return Zotero.spawn(function* () {
|
||||
var r = "";
|
||||
var r = "";
|
||||
|
||||
if (this.item) {
|
||||
yield this.item.loadRelations()
|
||||
.tap(() => Zotero.Promise.check(this.item));
|
||||
var keys = this.item.relatedItems;
|
||||
if (keys.length) {
|
||||
let items = yield Zotero.Items.getAsync(keys)
|
||||
.tap(() => Zotero.Promise.check(this.item));
|
||||
for (let item of items) {
|
||||
r = r + item.getDisplayTitle() + ", ";
|
||||
}
|
||||
r = r.substr(0,r.length-2);
|
||||
if (this.item) {
|
||||
var keys = this.item.relatedItems;
|
||||
if (keys.length) {
|
||||
for (let key of keys) {
|
||||
let item = Zotero.Items.getByLibraryAndKey(this.item.libraryID, key);
|
||||
r = r + item.getDisplayTitle() + ", ";
|
||||
}
|
||||
r = r.substr(0,r.length-2);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}, this);
|
||||
return r;
|
||||
]]>
|
||||
</getter>
|
||||
</property>
|
||||
|
@ -129,89 +124,79 @@
|
|||
</method>
|
||||
|
||||
<method name="refresh">
|
||||
<body>
|
||||
<![CDATA[
|
||||
return Zotero.spawn(function* () {
|
||||
var addButton = this.id('addButton');
|
||||
addButton.hidden = !this.editable;
|
||||
<body><![CDATA[
|
||||
var addButton = this.id('addButton');
|
||||
addButton.hidden = !this.editable;
|
||||
|
||||
var rows = this.id('relatedRows');
|
||||
while(rows.hasChildNodes())
|
||||
rows.removeChild(rows.firstChild);
|
||||
var rows = this.id('relatedRows');
|
||||
while(rows.hasChildNodes())
|
||||
rows.removeChild(rows.firstChild);
|
||||
|
||||
if (this.item) {
|
||||
yield this.item.loadRelations()
|
||||
.tap(() => Zotero.Promise.check(this.item));
|
||||
var relatedKeys = this.item.relatedItems;
|
||||
for (var i = 0; i < relatedKeys.length; i++) {
|
||||
let key = relatedKeys[i];
|
||||
let relatedItem =
|
||||
yield Zotero.Items.getByLibraryAndKeyAsync(
|
||||
this.item.libraryID, key
|
||||
)
|
||||
.tap(() => Zotero.Promise.check(this.item));
|
||||
let id = relatedItem.id;
|
||||
yield relatedItem.loadItemData()
|
||||
.tap(() => Zotero.Promise.check(this.item));
|
||||
let icon = document.createElement("image");
|
||||
icon.className = "zotero-box-icon";
|
||||
let type = Zotero.ItemTypes.getName(relatedItem.itemTypeID);
|
||||
if (type=='attachment')
|
||||
{
|
||||
switch (relatedItem.attaachmentLinkMode) {
|
||||
case Zotero.Attachments.LINK_MODE_LINKED_URL:
|
||||
type += '-web-link';
|
||||
break;
|
||||
if (this.item) {
|
||||
var relatedKeys = this.item.relatedItems;
|
||||
for (var i = 0; i < relatedKeys.length; i++) {
|
||||
let key = relatedKeys[i];
|
||||
let relatedItem = Zotero.Items.getByLibraryAndKey(
|
||||
this.item.libraryID, key
|
||||
);
|
||||
let id = relatedItem.id;
|
||||
let icon = document.createElement("image");
|
||||
icon.className = "zotero-box-icon";
|
||||
let type = Zotero.ItemTypes.getName(relatedItem.itemTypeID);
|
||||
if (type=='attachment')
|
||||
{
|
||||
switch (relatedItem.attaachmentLinkMode) {
|
||||
case Zotero.Attachments.LINK_MODE_LINKED_URL:
|
||||
type += '-web-link';
|
||||
break;
|
||||
|
||||
case Zotero.Attachments.LINK_MODE_IMPORTED_URL:
|
||||
type += '-snapshot';
|
||||
break;
|
||||
case Zotero.Attachments.LINK_MODE_IMPORTED_URL:
|
||||
type += '-snapshot';
|
||||
break;
|
||||
|
||||
case Zotero.Attachments.LINK_MODE_LINKED_FILE:
|
||||
type += '-link';
|
||||
break;
|
||||
case Zotero.Attachments.LINK_MODE_LINKED_FILE:
|
||||
type += '-link';
|
||||
break;
|
||||
|
||||
case Zotero.Attachments.LINK_MODE_IMPORTED_FILE:
|
||||
type += '-file';
|
||||
break;
|
||||
}
|
||||
case Zotero.Attachments.LINK_MODE_IMPORTED_FILE:
|
||||
type += '-file';
|
||||
break;
|
||||
}
|
||||
icon.setAttribute('src','chrome://zotero/skin/treeitem-' + type + '.png');
|
||||
|
||||
var label = document.createElement("label");
|
||||
label.className = "zotero-box-label";
|
||||
label.setAttribute('value', relatedItem.getDisplayTitle());
|
||||
label.setAttribute('crop','end');
|
||||
label.setAttribute('flex','1');
|
||||
|
||||
var box = document.createElement('box');
|
||||
box.setAttribute('onclick',
|
||||
"document.getBindingParent(this).showItem('" + id + "')");
|
||||
box.setAttribute('class','zotero-clicky');
|
||||
box.setAttribute('flex','1');
|
||||
box.appendChild(icon);
|
||||
box.appendChild(label);
|
||||
|
||||
if (this.editable) {
|
||||
var remove = document.createElement("label");
|
||||
remove.setAttribute('value','-');
|
||||
remove.setAttribute('onclick',
|
||||
"document.getBindingParent(this).remove('" + id + "');");
|
||||
remove.setAttribute('class','zotero-clicky zotero-clicky-minus');
|
||||
}
|
||||
|
||||
var row = document.createElement("row");
|
||||
row.appendChild(box);
|
||||
if (this.editable) {
|
||||
row.appendChild(remove);
|
||||
}
|
||||
rows.appendChild(row);
|
||||
}
|
||||
this.updateCount(relatedKeys.length);
|
||||
icon.setAttribute('src','chrome://zotero/skin/treeitem-' + type + '.png');
|
||||
|
||||
var label = document.createElement("label");
|
||||
label.className = "zotero-box-label";
|
||||
label.setAttribute('value', relatedItem.getDisplayTitle());
|
||||
label.setAttribute('crop','end');
|
||||
label.setAttribute('flex','1');
|
||||
|
||||
var box = document.createElement('box');
|
||||
box.setAttribute('onclick',
|
||||
"document.getBindingParent(this).showItem('" + id + "')");
|
||||
box.setAttribute('class','zotero-clicky');
|
||||
box.setAttribute('flex','1');
|
||||
box.appendChild(icon);
|
||||
box.appendChild(label);
|
||||
|
||||
if (this.editable) {
|
||||
var remove = document.createElement("label");
|
||||
remove.setAttribute('value','-');
|
||||
remove.setAttribute('onclick',
|
||||
"document.getBindingParent(this).remove('" + id + "');");
|
||||
remove.setAttribute('class','zotero-clicky zotero-clicky-minus');
|
||||
}
|
||||
|
||||
var row = document.createElement("row");
|
||||
row.appendChild(box);
|
||||
if (this.editable) {
|
||||
row.appendChild(remove);
|
||||
}
|
||||
rows.appendChild(row);
|
||||
}
|
||||
}, this);
|
||||
]]>
|
||||
</body>
|
||||
this.updateCount(relatedKeys.length);
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="add">
|
||||
<body><![CDATA[
|
||||
|
@ -238,13 +223,11 @@
|
|||
}
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
for (let relItem of relItems) {
|
||||
yield this.item.loadRelations();
|
||||
if (this.item.addRelatedItem(relItem)) {
|
||||
yield this.item.save({
|
||||
skipDateModifiedUpdate: true
|
||||
});
|
||||
}
|
||||
yield relItem.loadRelations();
|
||||
if (relItem.addRelatedItem(this.item)) {
|
||||
yield relItem.save({
|
||||
skipDateModifiedUpdate: true
|
||||
|
@ -267,7 +250,6 @@
|
|||
skipDateModifiedUpdate: true
|
||||
});
|
||||
}
|
||||
yield item.loadRelations();
|
||||
if (item.removeRelatedItem(this.item)) {
|
||||
yield item.save({
|
||||
skipDateModifiedUpdate: true
|
||||
|
|
|
@ -91,24 +91,20 @@
|
|||
|
||||
<property name="summary">
|
||||
<getter><![CDATA[
|
||||
return Zotero.spawn(function* () {
|
||||
var r = "";
|
||||
var r = "";
|
||||
|
||||
if (this.item) {
|
||||
yield this.item.loadTags()
|
||||
.tap(() => Zotero.Promise.check(this.mode));
|
||||
var tags = this.item.getTags();
|
||||
if (tags) {
|
||||
for(var i = 0; i < tags.length; i++)
|
||||
{
|
||||
r = r + tags[i].tag + ", ";
|
||||
}
|
||||
r = r.substr(0,r.length-2);
|
||||
if (this.item) {
|
||||
var tags = this.item.getTags();
|
||||
if (tags) {
|
||||
for(var i = 0; i < tags.length; i++)
|
||||
{
|
||||
r = r + tags[i].tag + ", ";
|
||||
}
|
||||
r = r.substr(0,r.length-2);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}, this);
|
||||
return r;
|
||||
]]></getter>
|
||||
</property>
|
||||
|
||||
|
@ -211,9 +207,6 @@
|
|||
return Zotero.spawn(function* () {
|
||||
Zotero.debug('Reloading tags box');
|
||||
|
||||
yield this.item.loadTags()
|
||||
.tap(() => Zotero.Promise.check(this.mode));
|
||||
|
||||
// Cancel field focusing while we're updating
|
||||
this._reloading = true;
|
||||
|
||||
|
|
|
@ -131,9 +131,9 @@ var Zotero_Duplicates_Pane = new function () {
|
|||
// alternative values so that they're still available if the item box
|
||||
// modifies the item
|
||||
Zotero.spawn(function* () {
|
||||
var diff = yield item.multiDiff(_otherItems, _ignoreFields);
|
||||
var diff = item.multiDiff(_otherItems, _ignoreFields);
|
||||
if (diff) {
|
||||
let itemValues = yield item.toJSON();
|
||||
let itemValues = item.toJSON();
|
||||
for (let i in diff) {
|
||||
diff[i].unshift(itemValues[i] !== undefined ? itemValues[i] : '');
|
||||
}
|
||||
|
@ -141,8 +141,6 @@ var Zotero_Duplicates_Pane = new function () {
|
|||
}
|
||||
|
||||
var newItem = yield item.copy();
|
||||
yield newItem.loadItemData();
|
||||
yield newItem.loadCreators();
|
||||
itembox.item = newItem;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -94,13 +94,11 @@ var ZoteroItemPane = new function() {
|
|||
_notesList.removeChild(_notesList.firstChild);
|
||||
}
|
||||
|
||||
yield item.loadChildItems();
|
||||
let notes = yield Zotero.Items.getAsync(item.getNotes());
|
||||
if (notes.length) {
|
||||
for (var i = 0; i < notes.length; i++) {
|
||||
let note = notes[i];
|
||||
let id = notes[i].id;
|
||||
yield note.loadItemData();
|
||||
|
||||
var icon = document.createElement('image');
|
||||
icon.className = "zotero-box-icon";
|
||||
|
@ -148,7 +146,6 @@ var ZoteroItemPane = new function() {
|
|||
box.mode = 'edit';
|
||||
}
|
||||
|
||||
yield Zotero.Promise.all([item.loadItemData(), item.loadCreators()]);
|
||||
box.item = item;
|
||||
});
|
||||
|
||||
|
|
|
@ -400,7 +400,6 @@ var Zotero_LocateMenu = new function() {
|
|||
}
|
||||
|
||||
if(item.isRegularItem()) {
|
||||
yield item.loadChildItems();
|
||||
var attachments = item.getAttachments();
|
||||
if(attachments) {
|
||||
// look through url fields for non-file:/// attachments
|
||||
|
|
|
@ -395,7 +395,6 @@ var Zotero_RecognizePDF = new function() {
|
|||
}
|
||||
|
||||
// put new item in same collections as the old one
|
||||
yield item.loadCollections();
|
||||
let itemCollections = item.getCollections();
|
||||
for (let i = 0; i < itemCollections.length; i++) {
|
||||
let collection = yield Zotero.Collections.getAsync(itemCollections[i]);
|
||||
|
|
|
@ -56,7 +56,6 @@ Zotero.API = {
|
|||
if (!col) {
|
||||
throw new Error('Invalid collection ID or key');
|
||||
}
|
||||
yield col.loadChildItems();
|
||||
results = col.getChildItems();
|
||||
break;
|
||||
|
||||
|
|
|
@ -1090,8 +1090,7 @@ Zotero.Attachments = new function(){
|
|||
|
||||
Zotero.DB.requireTransaction();
|
||||
|
||||
attachment.loadItemData();
|
||||
var newAttachment = yield attachment.clone(libraryID);
|
||||
var newAttachment = attachment.clone(libraryID);
|
||||
if (attachment.isImportedAttachment()) {
|
||||
// Attachment path isn't copied over by clone() if libraryID is different
|
||||
newAttachment.attachmentPath = attachment.attachmentPath;
|
||||
|
|
|
@ -529,7 +529,6 @@ Zotero.Cite.System.prototype = {
|
|||
throw "Zotero.Cite.System.retrieveItem called on non-item "+item;
|
||||
}
|
||||
|
||||
throw new Error("Unimplemented");
|
||||
var cslItem = Zotero.Utilities.itemToCSLJSON(zoteroItem);
|
||||
|
||||
// TEMP: citeproc-js currently expects the id property to be the item DB id
|
||||
|
|
|
@ -295,6 +295,7 @@ Zotero.CollectionTreeRow.prototype.getSearchObject = Zotero.Promise.coroutine(fu
|
|||
|
||||
// Create the outer (filter) search
|
||||
var s2 = new Zotero.Search();
|
||||
|
||||
if (this.isTrash()) {
|
||||
s2.addCondition('deleted', 'true');
|
||||
}
|
||||
|
|
|
@ -526,7 +526,7 @@ Zotero.CollectionTreeView.prototype._addSortedRow = Zotero.Promise.coroutine(fun
|
|||
);
|
||||
}
|
||||
else if (objectType == 'search') {
|
||||
let search = yield Zotero.Searches.getAsync(id);
|
||||
let search = Zotero.Searches.get(id);
|
||||
let libraryID = search.libraryID;
|
||||
let startRow = this._rowMap['L' + libraryID];
|
||||
|
||||
|
@ -545,7 +545,6 @@ Zotero.CollectionTreeView.prototype._addSortedRow = Zotero.Promise.coroutine(fun
|
|||
var inSearches = false;
|
||||
for (let i = startRow; i < this.rowCount; i++) {
|
||||
let treeRow = this.getRow(i);
|
||||
Zotero.debug(treeRow.id);
|
||||
beforeRow = i;
|
||||
|
||||
// If we've reached something other than collections, stop
|
||||
|
@ -1504,10 +1503,6 @@ Zotero.CollectionTreeView.prototype.canDropCheckAsync = Zotero.Promise.coroutine
|
|||
}
|
||||
|
||||
if (dataType == 'zotero/item') {
|
||||
if (treeRow.isCollection()) {
|
||||
yield treeRow.ref.loadChildItems();
|
||||
}
|
||||
|
||||
var ids = data;
|
||||
var items = Zotero.Items.get(ids);
|
||||
var skip = true;
|
||||
|
@ -1627,7 +1622,6 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r
|
|||
// If linked item is in the trash, undelete it and remove it from collections
|
||||
// (since it shouldn't be restored to previous collections)
|
||||
if (linkedItem.deleted) {
|
||||
yield linkedItem.loadCollections();
|
||||
linkedItem.setCollections();
|
||||
linkedItem.deleted = false;
|
||||
yield linkedItem.save({
|
||||
|
@ -1693,7 +1687,7 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r
|
|||
}
|
||||
|
||||
// Create new clone item in target library
|
||||
var newItem = yield item.clone(targetLibraryID, false, !options.tags);
|
||||
var newItem = item.clone(targetLibraryID, false, !options.tags);
|
||||
|
||||
// Set Rights field for My Publications
|
||||
if (options.license) {
|
||||
|
@ -1717,11 +1711,10 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r
|
|||
|
||||
// Child notes
|
||||
if (options.childNotes) {
|
||||
yield item.loadChildItems();
|
||||
var noteIDs = item.getNotes();
|
||||
var notes = yield Zotero.Items.getAsync(noteIDs);
|
||||
var notes = Zotero.Items.get(noteIDs);
|
||||
for each(var note in notes) {
|
||||
let newNote = yield note.clone(targetLibraryID);
|
||||
let newNote = note.clone(targetLibraryID);
|
||||
newNote.parentID = newItemID;
|
||||
yield newNote.save({
|
||||
skipSelect: true
|
||||
|
@ -1733,9 +1726,8 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r
|
|||
|
||||
// Child attachments
|
||||
if (options.childLinks || options.childFileAttachments) {
|
||||
yield item.loadChildItems();
|
||||
var attachmentIDs = item.getAttachments();
|
||||
var attachments = yield Zotero.Items.getAsync(attachmentIDs);
|
||||
var attachments = Zotero.Items.get(attachmentIDs);
|
||||
for each(var attachment in attachments) {
|
||||
var linkMode = attachment.attachmentLinkMode;
|
||||
|
||||
|
@ -1864,8 +1856,8 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r
|
|||
|
||||
if (targetTreeRow.isPublications()) {
|
||||
let items = yield Zotero.Items.getAsync(ids);
|
||||
let io = yield this._treebox.treeBody.ownerDocument.defaultView.ZoteroPane
|
||||
.showPublicationsWizard(items);
|
||||
let io = this._treebox.treeBody.ownerDocument.defaultView
|
||||
.ZoteroPane.showPublicationsWizard(items);
|
||||
if (!io) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -28,11 +28,8 @@ Zotero.Collection = function(params = {}) {
|
|||
|
||||
this._name = null;
|
||||
|
||||
this._hasChildCollections = null;
|
||||
this._childCollections = [];
|
||||
|
||||
this._hasChildItems = false;
|
||||
this._childItems = [];
|
||||
this._childCollections = new Set();
|
||||
this._childItems = new Set();
|
||||
|
||||
Zotero.Utilities.assignProps(this, params, ['name', 'libraryID', 'parentID',
|
||||
'parentKey', 'lastSync']);
|
||||
|
@ -162,19 +159,13 @@ Zotero.Collection.prototype.loadFromRow = function(row) {
|
|||
|
||||
|
||||
Zotero.Collection.prototype.hasChildCollections = function() {
|
||||
if (this._hasChildCollections !== null) {
|
||||
return this._hasChildCollections;
|
||||
}
|
||||
this._requireData('primaryData');
|
||||
return false;
|
||||
this._requireData('childCollections');
|
||||
return this._childCollections.size > 0;
|
||||
}
|
||||
|
||||
Zotero.Collection.prototype.hasChildItems = function() {
|
||||
if (this._hasChildItems !== null) {
|
||||
return this._hasChildItems;
|
||||
}
|
||||
this._requireData('primaryData');
|
||||
return false;
|
||||
this._requireData('childItems');
|
||||
return this._childItems.size > 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -189,19 +180,11 @@ Zotero.Collection.prototype.getChildCollections = function (asIDs) {
|
|||
|
||||
// Return collectionIDs
|
||||
if (asIDs) {
|
||||
var ids = [];
|
||||
for each(var col in this._childCollections) {
|
||||
ids.push(col.id);
|
||||
}
|
||||
return ids;
|
||||
return this._childCollections.values();
|
||||
}
|
||||
|
||||
// Return Zotero.Collection objects
|
||||
var objs = [];
|
||||
for each(var col in this._childCollections) {
|
||||
objs.push(col);
|
||||
}
|
||||
return objs;
|
||||
return Array.from(this._childCollections).map(id => this.ObjectsClass.get(id));
|
||||
}
|
||||
|
||||
|
||||
|
@ -215,13 +198,14 @@ Zotero.Collection.prototype.getChildCollections = function (asIDs) {
|
|||
Zotero.Collection.prototype.getChildItems = function (asIDs, includeDeleted) {
|
||||
this._requireData('childItems');
|
||||
|
||||
if (this._childItems.length == 0) {
|
||||
if (this._childItems.size == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Remove deleted items if necessary
|
||||
var childItems = [];
|
||||
for each(var item in this._childItems) {
|
||||
for (let itemID of this._childItems) {
|
||||
let item = this.ChildObjects.get(itemID);
|
||||
if (includeDeleted || !item.deleted) {
|
||||
childItems.push(item);
|
||||
}
|
||||
|
@ -229,19 +213,11 @@ Zotero.Collection.prototype.getChildItems = function (asIDs, includeDeleted) {
|
|||
|
||||
// Return itemIDs
|
||||
if (asIDs) {
|
||||
var ids = [];
|
||||
for each(var item in childItems) {
|
||||
ids.push(item.id);
|
||||
}
|
||||
return ids;
|
||||
return childItems.map(item => item.id);
|
||||
}
|
||||
|
||||
// Return Zotero.Item objects
|
||||
var objs = [];
|
||||
for each(var item in childItems) {
|
||||
objs.push(item);
|
||||
}
|
||||
return objs;
|
||||
return childItems.slice();
|
||||
}
|
||||
|
||||
Zotero.Collection.prototype._initSave = Zotero.Promise.coroutine(function* (env) {
|
||||
|
@ -388,7 +364,6 @@ Zotero.Collection.prototype.addItems = Zotero.Promise.coroutine(function* (itemI
|
|||
return;
|
||||
}
|
||||
|
||||
yield this.loadChildItems();
|
||||
var current = this.getChildItems(true);
|
||||
|
||||
Zotero.DB.requireTransaction();
|
||||
|
@ -400,15 +375,14 @@ Zotero.Collection.prototype.addItems = Zotero.Promise.coroutine(function* (itemI
|
|||
continue;
|
||||
}
|
||||
|
||||
let item = yield this.ChildObjects.getAsync(itemID);
|
||||
yield item.loadCollections();
|
||||
let item = this.ChildObjects.get(itemID);
|
||||
item.addToCollection(this.id);
|
||||
yield item.save({
|
||||
skipDateModifiedUpdate: true
|
||||
});
|
||||
}
|
||||
|
||||
yield this.loadChildItems(true);
|
||||
yield this._loadDataType('childItems');
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -434,7 +408,6 @@ Zotero.Collection.prototype.removeItems = Zotero.Promise.coroutine(function* (it
|
|||
return;
|
||||
}
|
||||
|
||||
yield this.loadChildItems();
|
||||
var current = this.getChildItems(true);
|
||||
|
||||
return Zotero.DB.executeTransaction(function* () {
|
||||
|
@ -447,7 +420,6 @@ Zotero.Collection.prototype.removeItems = Zotero.Promise.coroutine(function* (it
|
|||
}
|
||||
|
||||
let item = yield this.ChildObjects.getAsync(itemID);
|
||||
yield item.loadCollections();
|
||||
item.removeFromCollection(this.id);
|
||||
yield item.save({
|
||||
skipDateModifiedUpdate: true
|
||||
|
@ -455,7 +427,7 @@ Zotero.Collection.prototype.removeItems = Zotero.Promise.coroutine(function* (it
|
|||
}
|
||||
}.bind(this));
|
||||
|
||||
yield this.loadChildItems(true);
|
||||
yield this._loadDataType('childItems');
|
||||
});
|
||||
|
||||
|
||||
|
@ -464,13 +436,7 @@ Zotero.Collection.prototype.removeItems = Zotero.Promise.coroutine(function* (it
|
|||
**/
|
||||
Zotero.Collection.prototype.hasItem = function(itemID) {
|
||||
this._requireData('childItems');
|
||||
|
||||
for (let i=0; i<this._childItems.length; i++) {
|
||||
if (this._childItems[i].id == itemID) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return this._childItems.has(itemID);
|
||||
}
|
||||
|
||||
|
||||
|
@ -692,8 +658,8 @@ Zotero.Collection.prototype.fromJSON = function (json) {
|
|||
}
|
||||
|
||||
|
||||
Zotero.Collection.prototype.toResponseJSON = Zotero.Promise.coroutine(function* (options = {}) {
|
||||
var json = yield this.constructor._super.prototype.toResponseJSON.apply(this, options);
|
||||
Zotero.Collection.prototype.toResponseJSON = function (options = {}) {
|
||||
var json = this.constructor._super.prototype.toResponseJSON.apply(this, options);
|
||||
|
||||
// TODO: library block?
|
||||
|
||||
|
@ -713,10 +679,10 @@ Zotero.Collection.prototype.toResponseJSON = Zotero.Promise.coroutine(function*
|
|||
json.meta.numChildren = this.numChildren();
|
||||
}
|
||||
return json;
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
Zotero.Collection.prototype.toJSON = Zotero.Promise.coroutine(function* (options = {}) {
|
||||
Zotero.Collection.prototype.toJSON = function (options = {}) {
|
||||
var env = this._preToJSON(options);
|
||||
var mode = env.mode;
|
||||
|
||||
|
@ -729,7 +695,7 @@ Zotero.Collection.prototype.toJSON = Zotero.Promise.coroutine(function* (options
|
|||
obj.relations = {}; // TEMP
|
||||
|
||||
return this._postToJSON(env);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -865,75 +831,6 @@ Zotero.Collection.prototype.addLinkedCollection = Zotero.Promise.coroutine(funct
|
|||
//
|
||||
// Private methods
|
||||
//
|
||||
Zotero.Collection.prototype.reloadHasChildCollections = Zotero.Promise.coroutine(function* () {
|
||||
var sql = "SELECT COUNT(*) FROM collections WHERE parentCollectionID=?";
|
||||
this._hasChildCollections = !!(yield Zotero.DB.valueQueryAsync(sql, this.id));
|
||||
});
|
||||
|
||||
|
||||
Zotero.Collection.prototype.loadChildCollections = Zotero.Promise.coroutine(function* (reload) {
|
||||
if (this._loaded.childCollections && !reload) {
|
||||
return;
|
||||
}
|
||||
|
||||
var sql = "SELECT collectionID FROM collections WHERE parentCollectionID=?";
|
||||
var ids = yield Zotero.DB.columnQueryAsync(sql, this.id);
|
||||
|
||||
this._childCollections = [];
|
||||
|
||||
if (ids.length) {
|
||||
for each(var id in ids) {
|
||||
var col = yield this.ObjectsClass.getAsync(id);
|
||||
if (!col) {
|
||||
throw new Error('Collection ' + id + ' not found');
|
||||
}
|
||||
this._childCollections.push(col);
|
||||
}
|
||||
this._hasChildCollections = true;
|
||||
}
|
||||
else {
|
||||
this._hasChildCollections = false;
|
||||
}
|
||||
|
||||
this._loaded.childCollections = true;
|
||||
this._clearChanged('childCollections');
|
||||
});
|
||||
|
||||
|
||||
Zotero.Collection.prototype.reloadHasChildItems = Zotero.Promise.coroutine(function* () {
|
||||
var sql = "SELECT COUNT(*) FROM collectionItems WHERE collectionID=?";
|
||||
this._hasChildItems = !!(yield Zotero.DB.valueQueryAsync(sql, this.id));
|
||||
});
|
||||
|
||||
|
||||
Zotero.Collection.prototype.loadChildItems = Zotero.Promise.coroutine(function* (reload) {
|
||||
if (this._loaded.childItems && !reload) {
|
||||
return;
|
||||
}
|
||||
|
||||
var sql = "SELECT itemID FROM collectionItems WHERE collectionID=? "
|
||||
// DEBUG: Fix for child items created via context menu on parent within
|
||||
// a collection being added to the current collection
|
||||
+ "AND itemID NOT IN "
|
||||
+ "(SELECT itemID FROM itemNotes WHERE parentItemID IS NOT NULL) "
|
||||
+ "AND itemID NOT IN "
|
||||
+ "(SELECT itemID FROM itemAttachments WHERE parentItemID IS NOT NULL)";
|
||||
var ids = yield Zotero.DB.columnQueryAsync(sql, this.id);
|
||||
|
||||
this._childItems = [];
|
||||
|
||||
if (ids.length) {
|
||||
var items = yield this.ChildObjects.getAsync(ids);
|
||||
if (items) {
|
||||
this._childItems = items;
|
||||
}
|
||||
}
|
||||
|
||||
this._loaded.childItems = true;
|
||||
this._clearChanged('childItems');
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Add a collection to the cached child collections list if loaded
|
||||
*/
|
||||
|
@ -941,8 +838,7 @@ Zotero.Collection.prototype._registerChildCollection = function (collectionID) {
|
|||
if (this._loaded.childCollections) {
|
||||
let collection = this.ObjectsClass.get(collectionID);
|
||||
if (collection) {
|
||||
this._hasChildCollections = true;
|
||||
this._childCollections.push(collection);
|
||||
this._childCollections.add(collectionID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -953,13 +849,7 @@ Zotero.Collection.prototype._registerChildCollection = function (collectionID) {
|
|||
*/
|
||||
Zotero.Collection.prototype._unregisterChildCollection = function (collectionID) {
|
||||
if (this._loaded.childCollections) {
|
||||
for (let i = 0; i < this._childCollections.length; i++) {
|
||||
if (this._childCollections[i].id == collectionID) {
|
||||
this._childCollections.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._hasChildCollections = this._childCollections.length > 0;
|
||||
this._childCollections.delete(collectionID);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -971,8 +861,7 @@ Zotero.Collection.prototype._registerChildItem = function (itemID) {
|
|||
if (this._loaded.childItems) {
|
||||
let item = this.ChildObjects.get(itemID);
|
||||
if (item) {
|
||||
this._hasChildItems = true;
|
||||
this._childItems.push(item);
|
||||
this._childItems.add(itemID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -983,12 +872,6 @@ Zotero.Collection.prototype._registerChildItem = function (itemID) {
|
|||
*/
|
||||
Zotero.Collection.prototype._unregisterChildItem = function (itemID) {
|
||||
if (this._loaded.childItems) {
|
||||
for (let i = 0; i < this._childItems.length; i++) {
|
||||
if (this._childItems[i].id == itemID) {
|
||||
this._childItems.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._hasChildItems = this._childItems.length > 0;
|
||||
this._childItems.delete(itemID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,8 +85,7 @@ Zotero.Collections = function() {
|
|||
let children;
|
||||
|
||||
if (parentID) {
|
||||
let parent = yield Zotero.Collections.getAsync(parentID);
|
||||
yield parent.loadChildCollections();
|
||||
let parent = Zotero.Collections.get(parentID);
|
||||
children = parent.getChildCollections();
|
||||
} else if (libraryID) {
|
||||
let sql = "SELECT collectionID AS id FROM collections "
|
||||
|
@ -156,6 +155,103 @@ Zotero.Collections = function() {
|
|||
}
|
||||
|
||||
|
||||
this._loadChildCollections = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
|
||||
var sql = "SELECT C1.collectionID, C2.collectionID AS childCollectionID "
|
||||
+ "FROM collections C1 LEFT JOIN collections C2 ON (C1.collectionID=C2.parentCollectionID) "
|
||||
+ "WHERE C1.libraryID=?"
|
||||
+ (ids.length ? " AND C1.collectionID IN (" + ids.map(id => parseInt(id)).join(", ") + ")" : "");
|
||||
var params = [libraryID];
|
||||
var lastID;
|
||||
var rows = [];
|
||||
var setRows = function (collectionID, rows) {
|
||||
var collection = this._objectCache[collectionID];
|
||||
if (!collection) {
|
||||
throw new Error("Collection " + collectionID + " not found");
|
||||
}
|
||||
|
||||
collection._childCollections = new Set(rows);
|
||||
collection._loaded.childCollections = true;
|
||||
collection._clearChanged('childCollections');
|
||||
}.bind(this);
|
||||
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
let collectionID = row.getResultByIndex(0);
|
||||
|
||||
if (lastID && collectionID !== lastID) {
|
||||
setRows(lastID, rows);
|
||||
rows = [];
|
||||
}
|
||||
|
||||
lastID = collectionID;
|
||||
|
||||
let childCollectionID = row.getResultByIndex(1);
|
||||
// No child collections
|
||||
if (childCollectionID === null) {
|
||||
return;
|
||||
}
|
||||
rows.push(childCollectionID);
|
||||
}
|
||||
}
|
||||
);
|
||||
if (lastID) {
|
||||
setRows(lastID, rows);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this._loadChildItems = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
|
||||
var sql = "SELECT collectionID, itemID FROM collections "
|
||||
+ "LEFT JOIN collectionItems USING (collectionID) "
|
||||
+ "WHERE libraryID=?" + idSQL;
|
||||
var params = [libraryID];
|
||||
var lastID;
|
||||
var rows = [];
|
||||
var setRows = function (collectionID, rows) {
|
||||
var collection = this._objectCache[collectionID];
|
||||
if (!collection) {
|
||||
throw new Error("Collection " + collectionID + " not found");
|
||||
}
|
||||
|
||||
collection._childItems = new Set(rows);
|
||||
collection._loaded.childItems = true;
|
||||
collection._clearChanged('childItems');
|
||||
}.bind(this);
|
||||
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
let collectionID = row.getResultByIndex(0);
|
||||
|
||||
if (lastID && collectionID !== lastID) {
|
||||
setRows(lastID, rows);
|
||||
rows = [];
|
||||
}
|
||||
|
||||
lastID = collectionID;
|
||||
|
||||
let itemID = row.getResultByIndex(1);
|
||||
// No child items
|
||||
if (itemID === null) {
|
||||
return;
|
||||
}
|
||||
rows.push(itemID);
|
||||
}
|
||||
}
|
||||
);
|
||||
if (lastID) {
|
||||
setRows(lastID, rows);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this.registerChildCollection = function (collectionID, childCollectionID) {
|
||||
if (this._objectCache[collectionID]) {
|
||||
this._objectCache[collectionID]._registerChildCollection(childCollectionID);
|
||||
|
|
|
@ -30,29 +30,35 @@ Zotero.Creators = new function() {
|
|||
|
||||
var _cache = {};
|
||||
|
||||
this.init = Zotero.Promise.coroutine(function* (libraryID) {
|
||||
var sql = "SELECT * FROM creators";
|
||||
var rows = yield Zotero.DB.queryAsync(sql);
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
_cache[row.creatorID] = this.cleanData({
|
||||
// Avoid "DB column 'name' not found" warnings from the DB row Proxy
|
||||
firstName: row.firstName,
|
||||
lastName: row.lastName,
|
||||
fieldMode: row.fieldMode
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Returns creator data in internal format for a given creatorID
|
||||
*/
|
||||
this.getAsync = Zotero.Promise.coroutine(function* (creatorID) {
|
||||
this.get = function (creatorID) {
|
||||
if (!creatorID) {
|
||||
throw new Error("creatorID not provided");
|
||||
}
|
||||
|
||||
if (_cache[creatorID]) {
|
||||
return this.cleanData(_cache[creatorID]);
|
||||
}
|
||||
|
||||
var sql = "SELECT * FROM creators WHERE creatorID=?";
|
||||
var row = yield Zotero.DB.rowQueryAsync(sql, creatorID);
|
||||
if (!row) {
|
||||
if (!_cache[creatorID]) {
|
||||
throw new Error("Creator " + creatorID + " not found");
|
||||
}
|
||||
return _cache[creatorID] = this.cleanData({
|
||||
firstName: row.firstName, // avoid "DB column 'name' not found" warnings from the DB row Proxy
|
||||
lastName: row.lastName,
|
||||
fieldMode: row.fieldMode
|
||||
});
|
||||
});
|
||||
|
||||
// Return copy of data
|
||||
return this.cleanData(_cache[creatorID]);
|
||||
};
|
||||
|
||||
|
||||
this.getItemsWithCreator = function (creatorID) {
|
||||
|
@ -87,12 +93,10 @@ Zotero.Creators = new function() {
|
|||
id = yield Zotero.ID.get('creators');
|
||||
let sql = "INSERT INTO creators (creatorID, firstName, lastName, fieldMode) "
|
||||
+ "VALUES (?, ?, ?, ?)";
|
||||
let insertID = yield Zotero.DB.queryAsync(
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql, [id, data.firstName, data.lastName, data.fieldMode]
|
||||
);
|
||||
if (!id) {
|
||||
id = insertID;
|
||||
}
|
||||
_cache[id] = data;
|
||||
}
|
||||
return id;
|
||||
});
|
||||
|
|
|
@ -401,7 +401,7 @@ Zotero.DataObject.prototype.setRelations = function (newRelations) {
|
|||
|
||||
// Relations are stored internally as a flat array with individual predicate-object pairs,
|
||||
// so convert the incoming relations to that
|
||||
var newRelationsFlat = this._flattenRelations(newRelations);
|
||||
var newRelationsFlat = this.ObjectsClass.flattenRelations(newRelations);
|
||||
|
||||
var changed = false;
|
||||
if (oldRelations.length != newRelationsFlat.length) {
|
||||
|
@ -457,8 +457,6 @@ Zotero.DataObject.prototype._getLinkedObject = Zotero.Promise.coroutine(function
|
|||
throw new Error(this._ObjectType + " is already in library " + libraryID);
|
||||
}
|
||||
|
||||
yield this.loadRelations();
|
||||
|
||||
var predicate = Zotero.Relations.linkedObjectPredicate;
|
||||
var libraryObjectPrefix = Zotero.URI.getLibraryURI(libraryID)
|
||||
+ "/" + this._objectTypePlural + "/";
|
||||
|
@ -514,8 +512,6 @@ Zotero.DataObject.prototype._addLinkedObject = Zotero.Promise.coroutine(function
|
|||
throw new Error("Can't add linked " + this._objectType + " in same library");
|
||||
}
|
||||
|
||||
yield this.loadRelations();
|
||||
|
||||
var predicate = Zotero.Relations.linkedObjectPredicate;
|
||||
var thisURI = Zotero.URI['get' + this._ObjectType + 'URI'](this);
|
||||
var objectURI = Zotero.URI['get' + this._ObjectType + 'URI'](object);
|
||||
|
@ -539,7 +535,6 @@ Zotero.DataObject.prototype._addLinkedObject = Zotero.Promise.coroutine(function
|
|||
});
|
||||
}
|
||||
else {
|
||||
yield object.loadRelations();
|
||||
object.addRelation(predicate, thisURI);
|
||||
yield object.save({
|
||||
skipDateModifiedUpdate: true,
|
||||
|
@ -551,9 +546,11 @@ Zotero.DataObject.prototype._addLinkedObject = Zotero.Promise.coroutine(function
|
|||
});
|
||||
|
||||
|
||||
/*
|
||||
* Build object from database
|
||||
*/
|
||||
//
|
||||
// Bulk data loading functions
|
||||
//
|
||||
// These are called by Zotero.DataObjects.prototype._loadDataType().
|
||||
//
|
||||
Zotero.DataObject.prototype.loadPrimaryData = Zotero.Promise.coroutine(function* (reload, failOnMissing) {
|
||||
if (this._loaded.primaryData && !reload) return;
|
||||
|
||||
|
@ -610,65 +607,6 @@ Zotero.DataObject.prototype.loadPrimaryData = Zotero.Promise.coroutine(function*
|
|||
});
|
||||
|
||||
|
||||
Zotero.DataObject.prototype.loadRelations = Zotero.Promise.coroutine(function* (reload) {
|
||||
if (!this.ObjectsClass._relationsTable) {
|
||||
throw new Error("Relations not supported for " + this._objectTypePlural);
|
||||
}
|
||||
|
||||
if (this._loaded.relations && !reload) {
|
||||
return;
|
||||
}
|
||||
|
||||
Zotero.debug("Loading relations for " + this._objectType + " " + this.libraryKey);
|
||||
|
||||
this._requireData('primaryData');
|
||||
|
||||
var sql = "SELECT predicate, object FROM " + this.ObjectsClass._relationsTable + " "
|
||||
+ "JOIN relationPredicates USING (predicateID) "
|
||||
+ "WHERE " + this.ObjectsClass.idColumn + "=?";
|
||||
var rows = yield Zotero.DB.queryAsync(sql, this.id);
|
||||
|
||||
var relations = {};
|
||||
function addRel(predicate, object) {
|
||||
if (!relations[predicate]) {
|
||||
relations[predicate] = [];
|
||||
}
|
||||
relations[predicate].push(object);
|
||||
}
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
addRel(row.predicate, row.object);
|
||||
}
|
||||
|
||||
/*if (this._objectType == 'item') {
|
||||
let getURI = Zotero.URI["get" + this._ObjectType + "URI"].bind(Zotero.URI);
|
||||
let objectURI = getURI(this);
|
||||
|
||||
// Related items are bidirectional, so include any pointing to this object
|
||||
let objects = yield Zotero.Relations.getByPredicateAndObject(
|
||||
Zotero.Relations.relatedItemPredicate, objectURI
|
||||
);
|
||||
for (let i = 0; i < objects.length; i++) {
|
||||
addRel(Zotero.Relations.relatedItemPredicate, getURI(objects[i]));
|
||||
}
|
||||
|
||||
// Also include any owl:sameAs relations pointing to this object
|
||||
objects = yield Zotero.Relations.getByPredicateAndObject(
|
||||
Zotero.Relations.linkedObjectPredicate, objectURI
|
||||
);
|
||||
for (let i = 0; i < objects.length; i++) {
|
||||
addRel(Zotero.Relations.linkedObjectPredicate, getURI(objects[i]));
|
||||
}
|
||||
}*/
|
||||
|
||||
// Relations are stored as predicate-object pairs
|
||||
this._relations = this._flattenRelations(relations);
|
||||
this._loaded.relations = true;
|
||||
this._clearChanged('relations');
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Reloads loaded, changed data
|
||||
*
|
||||
|
@ -735,7 +673,7 @@ Zotero.DataObject.prototype._requireData = function (dataType) {
|
|||
* @param {Boolean} reload
|
||||
*/
|
||||
Zotero.DataObject.prototype._loadDataType = function (dataType, reload) {
|
||||
return this["load" + dataType[0].toUpperCase() + dataType.substr(1)](reload);
|
||||
return this._ObjectsClass._loadDataType(dataType, this.libraryID, [this.id]);
|
||||
}
|
||||
|
||||
Zotero.DataObject.prototype.loadAllData = function (reload) {
|
||||
|
@ -868,6 +806,16 @@ Zotero.DataObject.prototype.save = Zotero.Promise.coroutine(function* (options)
|
|||
Zotero.debug('Updating database with new ' + this._objectType + ' data', 4);
|
||||
}
|
||||
|
||||
if (env.options.skipAll) {
|
||||
[
|
||||
'skipDateModifiedUpdate',
|
||||
'skipClientDateModifiedUpdate',
|
||||
'skipSyncedUpdate',
|
||||
'skipEditCheck',
|
||||
'skipSelect'
|
||||
].forEach(x => env.options[x] = true);
|
||||
}
|
||||
|
||||
try {
|
||||
if (Zotero.DataObject.prototype._finalizeSave == this._finalizeSave) {
|
||||
throw new Error("_finalizeSave not implemented for Zotero." + this._ObjectType);
|
||||
|
@ -1214,16 +1162,16 @@ Zotero.DataObject.prototype._finalizeErase = Zotero.Promise.coroutine(function*
|
|||
});
|
||||
|
||||
|
||||
Zotero.DataObject.prototype.toResponseJSON = Zotero.Promise.coroutine(function* (options) {
|
||||
Zotero.DataObject.prototype.toResponseJSON = function (options) {
|
||||
// TODO: library block?
|
||||
|
||||
return {
|
||||
key: this.key,
|
||||
version: this.version,
|
||||
meta: {},
|
||||
data: yield this.toJSON(options)
|
||||
data: this.toJSON(options)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Zotero.DataObject.prototype._preToJSON = function (options) {
|
||||
|
@ -1272,32 +1220,3 @@ Zotero.DataObject.prototype._disabledCheck = function () {
|
|||
+ "use Zotero." + this._ObjectTypePlural + ".getAsync()");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Flatten API JSON relations object into an array of unique predicate-object pairs
|
||||
*
|
||||
* @param {Object} relations - Relations object in API JSON format, with predicates as keys
|
||||
* and arrays of URIs as objects
|
||||
* @return {Array[]} - Predicate-object pairs
|
||||
*/
|
||||
Zotero.DataObject.prototype._flattenRelations = function (relations) {
|
||||
var relationsFlat = [];
|
||||
for (let predicate in relations) {
|
||||
let object = relations[predicate];
|
||||
if (Array.isArray(object)) {
|
||||
object = Zotero.Utilities.arrayUnique(object);
|
||||
for (let i = 0; i < object.length; i++) {
|
||||
relationsFlat.push([predicate, object[i]]);
|
||||
}
|
||||
}
|
||||
else if (typeof object == 'string') {
|
||||
relationsFlat.push([predicate, object]);
|
||||
}
|
||||
else {
|
||||
Zotero.debug(object, 1);
|
||||
throw new Error("Invalid relation value");
|
||||
}
|
||||
}
|
||||
return relationsFlat;
|
||||
}
|
||||
|
|
|
@ -336,6 +336,253 @@ Zotero.DataObjects.prototype.getNewer = Zotero.Promise.method(function (libraryI
|
|||
});
|
||||
|
||||
|
||||
/**
|
||||
* Loads data for a given data type
|
||||
* @param {String} dataType
|
||||
* @param {Integer} libraryID
|
||||
* @param {Integer[]} [ids]
|
||||
*/
|
||||
Zotero.DataObjects.prototype._loadDataType = Zotero.Promise.coroutine(function* (dataType, libraryID, ids) {
|
||||
var funcName = "_load" + dataType[0].toUpperCase() + dataType.substr(1)
|
||||
// Single data types need an 's' (e.g., 'note' -> 'loadNotes()')
|
||||
+ ((dataType.endsWith('s') || dataType.endsWith('Data') ? '' : 's'));
|
||||
if (!this[funcName]) {
|
||||
throw new Error(`Zotero.${this._ZDO_Objects}.${funcName} is not a function`);
|
||||
}
|
||||
|
||||
if (ids && ids.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var t = new Date;
|
||||
var libraryName = Zotero.Libraries.get(libraryID).name;
|
||||
|
||||
var idSQL = "";
|
||||
if (ids) {
|
||||
idSQL = " AND " + this.idColumn + " IN (" + ids.map(id => parseInt(id)).join(", ") + ")";
|
||||
}
|
||||
|
||||
Zotero.debug("Loading " + dataType
|
||||
+ (ids
|
||||
? " for " + ids.length + " " + (ids.length == 1 ? this._ZDO_object : this._ZDO_objects)
|
||||
: '')
|
||||
+ " in " + libraryName);
|
||||
|
||||
yield this[funcName](libraryID, ids ? ids : [], idSQL);
|
||||
|
||||
Zotero.debug(`Loaded ${dataType} in ${libraryName} in ${new Date() - t} ms`);
|
||||
});
|
||||
|
||||
Zotero.DataObjects.prototype.loadAllData = Zotero.Promise.coroutine(function* (libraryID, ids) {
|
||||
var t = new Date();
|
||||
var libraryName = Zotero.Libraries.get(libraryID).name;
|
||||
|
||||
Zotero.debug("Loading all data"
|
||||
+ (ids ? " for " + ids.length + " " + this._ZDO_objects : '')
|
||||
+ " in " + libraryName);
|
||||
|
||||
let dataTypes = this.ObjectClass.prototype._dataTypes;
|
||||
for (let i = 0; i < dataTypes.length; i++) {
|
||||
yield this._loadDataType(dataTypes[i], libraryID, ids);
|
||||
}
|
||||
|
||||
Zotero.debug(`Loaded all data in ${libraryName} in ${new Date() - t} ms`);
|
||||
});
|
||||
|
||||
|
||||
Zotero.DataObjects.prototype._loadPrimaryData = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL, options) {
|
||||
var loaded = {};
|
||||
|
||||
// If library isn't an integer (presumably false or null), skip it
|
||||
if (parseInt(libraryID) != libraryID) {
|
||||
libraryID = false;
|
||||
}
|
||||
|
||||
var sql = this.primaryDataSQL;
|
||||
var params = [];
|
||||
if (libraryID !== false) {
|
||||
sql += ' AND O.libraryID=?';
|
||||
params.push(libraryID);
|
||||
}
|
||||
if (ids.length) {
|
||||
sql += ' AND O.' + this._ZDO_id + ' IN (' + ids.join(',') + ')';
|
||||
}
|
||||
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
onRow: function (row) {
|
||||
var id = row.getResultByName(this._ZDO_id);
|
||||
var columns = Object.keys(this._primaryDataSQLParts);
|
||||
var rowObj = {};
|
||||
for (let i=0; i<columns.length; i++) {
|
||||
rowObj[columns[i]] = row.getResultByIndex(i);
|
||||
}
|
||||
var obj;
|
||||
|
||||
// Existing object -- reload in place
|
||||
if (this._objectCache[id]) {
|
||||
this._objectCache[id].loadFromRow(rowObj, true);
|
||||
obj = this._objectCache[id];
|
||||
}
|
||||
// Object doesn't exist -- create new object and stuff in cache
|
||||
else {
|
||||
obj = this._getObjectForRow(rowObj);
|
||||
obj.loadFromRow(rowObj, true);
|
||||
if (!options || !options.noCache) {
|
||||
this.registerObject(obj);
|
||||
}
|
||||
}
|
||||
loaded[id] = obj;
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
|
||||
if (!ids) {
|
||||
this._loadedLibraries[libraryID] = true;
|
||||
|
||||
// If loading all objects, remove cached objects that no longer exist
|
||||
for (let i in this._objectCache) {
|
||||
let obj = this._objectCache[i];
|
||||
if (libraryID !== false && obj.libraryID !== libraryID) {
|
||||
continue;
|
||||
}
|
||||
if (!loaded[obj.id]) {
|
||||
this.unload(obj.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._postLoad) {
|
||||
this._postLoad(libraryID, ids);
|
||||
}
|
||||
}
|
||||
|
||||
return loaded;
|
||||
});
|
||||
|
||||
|
||||
Zotero.DataObjects.prototype._loadRelations = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
|
||||
if (!this._relationsTable) {
|
||||
throw new Error("Relations not supported for " + this._ZDO_objects);
|
||||
}
|
||||
|
||||
var sql = "SELECT " + this.idColumn + ", predicate, object "
|
||||
+ `FROM ${this.table} LEFT JOIN ${this._relationsTable} USING (${this.idColumn}) `
|
||||
+ "LEFT JOIN relationPredicates USING (predicateID) "
|
||||
+ "WHERE libraryID=?" + idSQL;
|
||||
var params = [libraryID];
|
||||
|
||||
var lastID;
|
||||
var rows = [];
|
||||
var setRows = function (id, rows) {
|
||||
var obj = this._objectCache[id];
|
||||
if (!obj) {
|
||||
throw new Error(this._ZDO_Object + " " + id + " not found");
|
||||
}
|
||||
|
||||
var relations = {};
|
||||
function addRel(predicate, object) {
|
||||
if (!relations[predicate]) {
|
||||
relations[predicate] = [];
|
||||
}
|
||||
relations[predicate].push(object);
|
||||
}
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
addRel(row.predicate, row.object);
|
||||
}
|
||||
|
||||
/*if (this._objectType == 'item') {
|
||||
let getURI = Zotero.URI["get" + this._ObjectType + "URI"].bind(Zotero.URI);
|
||||
let objectURI = getURI(this);
|
||||
|
||||
// Related items are bidirectional, so include any pointing to this object
|
||||
let objects = yield Zotero.Relations.getByPredicateAndObject(
|
||||
Zotero.Relations.relatedItemPredicate, objectURI
|
||||
);
|
||||
for (let i = 0; i < objects.length; i++) {
|
||||
addRel(Zotero.Relations.relatedItemPredicate, getURI(objects[i]));
|
||||
}
|
||||
|
||||
// Also include any owl:sameAs relations pointing to this object
|
||||
objects = yield Zotero.Relations.getByPredicateAndObject(
|
||||
Zotero.Relations.linkedObjectPredicate, objectURI
|
||||
);
|
||||
for (let i = 0; i < objects.length; i++) {
|
||||
addRel(Zotero.Relations.linkedObjectPredicate, getURI(objects[i]));
|
||||
}
|
||||
}*/
|
||||
|
||||
// Relations are stored as predicate-object pairs
|
||||
obj._relations = this.flattenRelations(relations);
|
||||
obj._loaded.relations = true;
|
||||
obj._clearChanged('relations');
|
||||
}.bind(this);
|
||||
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
let id = row.getResultByIndex(0);
|
||||
|
||||
if (lastID && id !== lastID) {
|
||||
setRows(lastID, rows);
|
||||
rows = [];
|
||||
}
|
||||
|
||||
lastID = id;
|
||||
let predicate = row.getResultByIndex(1);
|
||||
// No relations
|
||||
if (predicate === null) {
|
||||
return;
|
||||
}
|
||||
rows.push({
|
||||
predicate,
|
||||
object: row.getResultByIndex(2)
|
||||
});
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
|
||||
if (lastID) {
|
||||
setRows(lastID, rows);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Flatten API JSON relations object into an array of unique predicate-object pairs
|
||||
*
|
||||
* @param {Object} relations - Relations object in API JSON format, with predicates as keys
|
||||
* and arrays of URIs as objects
|
||||
* @return {Array[]} - Predicate-object pairs
|
||||
*/
|
||||
Zotero.DataObjects.prototype.flattenRelations = function (relations) {
|
||||
var relationsFlat = [];
|
||||
for (let predicate in relations) {
|
||||
let object = relations[predicate];
|
||||
if (Array.isArray(object)) {
|
||||
object = Zotero.Utilities.arrayUnique(object);
|
||||
for (let i = 0; i < object.length; i++) {
|
||||
relationsFlat.push([predicate, object[i]]);
|
||||
}
|
||||
}
|
||||
else if (typeof object == 'string') {
|
||||
relationsFlat.push([predicate, object]);
|
||||
}
|
||||
else {
|
||||
Zotero.debug(object, 1);
|
||||
throw new Error("Invalid relation value");
|
||||
}
|
||||
}
|
||||
return relationsFlat;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reload loaded data of loaded objects
|
||||
*
|
||||
|
@ -557,9 +804,7 @@ Zotero.DataObjects.prototype.erase = Zotero.Promise.coroutine(function* (ids, op
|
|||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// TEMP: remove
|
||||
Zotero.DataObjects.prototype._load = Zotero.Promise.coroutine(function* (libraryID, ids, options) {
|
||||
var loaded = {};
|
||||
|
||||
|
@ -641,6 +886,8 @@ Zotero.DataObjects.prototype._load = Zotero.Promise.coroutine(function* (library
|
|||
return loaded;
|
||||
});
|
||||
|
||||
|
||||
|
||||
Zotero.DataObjects.prototype._getObjectForRow = function(row) {
|
||||
return new Zotero[this._ZDO_Object];
|
||||
};
|
||||
|
|
|
@ -50,6 +50,9 @@ Zotero.Item = function(itemTypeOrID) {
|
|||
this._attachmentLinkMode = null;
|
||||
this._attachmentContentType = null;
|
||||
this._attachmentPath = null;
|
||||
this._attachmentSyncState = 0;
|
||||
this._attachmentSyncedModificationTime = null;
|
||||
this._attachmentSyncedHash = null;
|
||||
|
||||
// loadCreators
|
||||
this._creators = [];
|
||||
|
@ -90,9 +93,9 @@ Zotero.defineProperty(Zotero.Item.prototype, 'ContainerObjectsClass', {
|
|||
});
|
||||
|
||||
Zotero.Item.prototype._dataTypes = Zotero.Item._super.prototype._dataTypes.concat([
|
||||
'creators',
|
||||
'itemData',
|
||||
'note',
|
||||
'creators',
|
||||
'childItems',
|
||||
// 'relatedItems', // TODO: remove
|
||||
'tags',
|
||||
|
@ -327,12 +330,14 @@ Zotero.Item.prototype._parseRowData = function(row) {
|
|||
//Zotero.debug("Setting field '" + col + "' to '" + val + "' for item " + this.id);
|
||||
|
||||
switch (col) {
|
||||
// Skip
|
||||
// Unchanged
|
||||
case 'libraryID':
|
||||
case 'itemTypeID':
|
||||
case 'attachmentSyncState':
|
||||
case 'attachmentSyncedHash':
|
||||
case 'attachmentSyncedModificationTime':
|
||||
break;
|
||||
|
||||
// Unchanged
|
||||
case 'itemID':
|
||||
col = 'id';
|
||||
break;
|
||||
|
@ -1318,9 +1323,7 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
|
|||
this.libraryID, parentItemKey
|
||||
);
|
||||
for (let i=0; i<changedCollections.length; i++) {
|
||||
yield parentItem.loadCollections();
|
||||
parentItem.addToCollection(changedCollections[i]);
|
||||
yield this.loadCollections();
|
||||
this.removeFromCollection(changedCollections[i]);
|
||||
|
||||
Zotero.Notifier.queue(
|
||||
|
@ -1452,14 +1455,19 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
|
|||
}
|
||||
|
||||
if (this._changed.attachmentData) {
|
||||
let sql = "REPLACE INTO itemAttachments (itemID, parentItemID, linkMode, "
|
||||
+ "contentType, charsetID, path) VALUES (?,?,?,?,?,?)";
|
||||
let sql = "REPLACE INTO itemAttachments "
|
||||
+ "(itemID, parentItemID, linkMode, contentType, charsetID, path, "
|
||||
+ "syncState, storageModTime, storageHash) "
|
||||
+ "VALUES (?,?,?,?,?,?,?,?,?)";
|
||||
let linkMode = this.attachmentLinkMode;
|
||||
let contentType = this.attachmentContentType;
|
||||
let charsetID = this.attachmentCharset
|
||||
? Zotero.CharacterSets.getID(this.attachmentCharset)
|
||||
: null;
|
||||
let path = this.attachmentPath;
|
||||
let syncState = this.attachmentSyncState;
|
||||
let storageModTime = this.attachmentSyncedModificationTime;
|
||||
let storageHash = this.attachmentSyncedHash;
|
||||
|
||||
if (linkMode == Zotero.Attachments.LINK_MODE_LINKED_FILE && libraryType != 'user') {
|
||||
throw new Error("Linked files can only be added to user library");
|
||||
|
@ -1471,7 +1479,10 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
|
|||
{ int: linkMode },
|
||||
contentType ? { string: contentType } : null,
|
||||
charsetID ? { int: charsetID } : null,
|
||||
path ? { string: path } : null
|
||||
path ? { string: path } : null,
|
||||
syncState !== undefined ? syncState : 0,
|
||||
storageModTime !== undefined ? storageModTime : null,
|
||||
storageHash || null
|
||||
];
|
||||
yield Zotero.DB.queryAsync(sql, params);
|
||||
|
||||
|
@ -2296,10 +2307,9 @@ Zotero.Item.prototype.renameAttachmentFile = Zotero.Promise.coroutine(function*
|
|||
|
||||
yield this.relinkAttachmentFile(destPath);
|
||||
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.Sync.Storage.Local.setSyncedHash(this.id, null, false);
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(this.id, "to_upload");
|
||||
}.bind(this));
|
||||
this.attachmentSyncedHash = null;
|
||||
this.attachmentSyncState = "to_upload";
|
||||
yield this.saveTx({ skipAll: true });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -2680,7 +2690,11 @@ Zotero.defineProperty(Zotero.Item.prototype, 'attachmentSyncState', {
|
|||
},
|
||||
set: function(val) {
|
||||
if (!this.isAttachment()) {
|
||||
throw ("attachmentSyncState can only be set for attachment items");
|
||||
throw new Error("attachmentSyncState can only be set for attachment items");
|
||||
}
|
||||
|
||||
if (typeof val == 'string') {
|
||||
val = Zotero.Sync.Storage.Local["SYNC_STATE_" + val.toUpperCase()];
|
||||
}
|
||||
|
||||
switch (this.attachmentLinkMode) {
|
||||
|
@ -2689,8 +2703,7 @@ Zotero.defineProperty(Zotero.Item.prototype, 'attachmentSyncState', {
|
|||
break;
|
||||
|
||||
default:
|
||||
throw ("attachmentSyncState can only be set for snapshots and "
|
||||
+ "imported files");
|
||||
throw new Error("attachmentSyncState can only be set for stored files");
|
||||
}
|
||||
|
||||
switch (val) {
|
||||
|
@ -2703,8 +2716,7 @@ Zotero.defineProperty(Zotero.Item.prototype, 'attachmentSyncState', {
|
|||
break;
|
||||
|
||||
default:
|
||||
throw ("Invalid sync state '" + val
|
||||
+ "' in Zotero.Item.attachmentSyncState setter");
|
||||
throw new Error("Invalid sync state '" + val + "'");
|
||||
}
|
||||
|
||||
if (val == this.attachmentSyncState) {
|
||||
|
@ -2720,6 +2732,85 @@ Zotero.defineProperty(Zotero.Item.prototype, 'attachmentSyncState', {
|
|||
});
|
||||
|
||||
|
||||
Zotero.defineProperty(Zotero.Item.prototype, 'attachmentSyncedModificationTime', {
|
||||
get: function () {
|
||||
if (!this.isFileAttachment()) {
|
||||
return undefined;
|
||||
}
|
||||
return this._attachmentSyncedModificationTime;
|
||||
},
|
||||
set: function (val) {
|
||||
if (!this.isAttachment()) {
|
||||
throw ("attachmentSyncedModificationTime can only be set for attachment items");
|
||||
}
|
||||
|
||||
switch (this.attachmentLinkMode) {
|
||||
case Zotero.Attachments.LINK_MODE_IMPORTED_URL:
|
||||
case Zotero.Attachments.LINK_MODE_IMPORTED_FILE:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("attachmentSyncedModificationTime can only be set for stored files");
|
||||
}
|
||||
|
||||
if (typeof val != 'number') {
|
||||
throw new Error("attachmentSyncedModificationTime must be a number");
|
||||
}
|
||||
if (parseInt(val) != val || val < 0) {
|
||||
throw new Error("attachmentSyncedModificationTime must be a timestamp in milliseconds");
|
||||
}
|
||||
|
||||
if (val == this._attachmentSyncedModificationTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._changed.attachmentData) {
|
||||
this._changed.attachmentData = {};
|
||||
}
|
||||
this._changed.attachmentData.syncedModificationTime = true;
|
||||
this._attachmentSyncedModificationTime = val;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Zotero.defineProperty(Zotero.Item.prototype, 'attachmentSyncedHash', {
|
||||
get: function () {
|
||||
if (!this.isFileAttachment()) {
|
||||
return undefined;
|
||||
}
|
||||
return this._attachmentSyncedHash;
|
||||
},
|
||||
set: function (val) {
|
||||
if (!this.isAttachment()) {
|
||||
throw ("attachmentSyncedHash can only be set for attachment items");
|
||||
}
|
||||
|
||||
switch (this.attachmentLinkMode) {
|
||||
case Zotero.Attachments.LINK_MODE_IMPORTED_URL:
|
||||
case Zotero.Attachments.LINK_MODE_IMPORTED_FILE:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("attachmentSyncedHash can only be set for stored files");
|
||||
}
|
||||
|
||||
if (val !== null && val.length != 32) {
|
||||
throw new Error("Invalid attachment hash '" + val + "'");
|
||||
}
|
||||
|
||||
if (val == this._attachmentSyncedHash) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._changed.attachmentData) {
|
||||
this._changed.attachmentData = {};
|
||||
}
|
||||
this._changed.attachmentData.syncedHash = true;
|
||||
this._attachmentSyncedHash = val;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Modification time of an attachment file
|
||||
*
|
||||
|
@ -2784,6 +2875,7 @@ Zotero.defineProperty(Zotero.Item.prototype, 'attachmentHash', {
|
|||
});
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return plain text of attachment content
|
||||
*
|
||||
|
@ -3337,7 +3429,6 @@ Zotero.Item.prototype.getImageSrcWithTags = Zotero.Promise.coroutine(function* (
|
|||
var uri = this.getImageSrc();
|
||||
|
||||
// TODO: Optimize this. Maybe load color/item associations in batch in cacheFields?
|
||||
yield this.loadTags();
|
||||
var tags = this.getTags();
|
||||
if (!tags.length) {
|
||||
return uri;
|
||||
|
@ -3512,8 +3603,8 @@ Zotero.Item.prototype.diff = function (item, includeMatches, ignoreFields) {
|
|||
*
|
||||
* Currently compares only item data, not primary fields
|
||||
*/
|
||||
Zotero.Item.prototype.multiDiff = Zotero.Promise.coroutine(function* (otherItems, ignoreFields) {
|
||||
var thisData = yield this.toJSON();
|
||||
Zotero.Item.prototype.multiDiff = function (otherItems, ignoreFields) {
|
||||
var thisData = this.toJSON();
|
||||
|
||||
var alternatives = {};
|
||||
var hasDiffs = false;
|
||||
|
@ -3521,7 +3612,7 @@ Zotero.Item.prototype.multiDiff = Zotero.Promise.coroutine(function* (otherItems
|
|||
for (let i = 0; i < otherItems.length; i++) {
|
||||
let otherItem = otherItems[i];
|
||||
let diff = [];
|
||||
let otherData = yield otherItem.toJSON();
|
||||
let otherData = otherItem.toJSON();
|
||||
let numDiffs = this.ObjectsClass.diff(thisData, otherData, diff);
|
||||
|
||||
if (numDiffs) {
|
||||
|
@ -3549,7 +3640,7 @@ Zotero.Item.prototype.multiDiff = Zotero.Promise.coroutine(function* (otherItems
|
|||
}
|
||||
|
||||
return alternatives;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
|
@ -3561,15 +3652,13 @@ Zotero.Item.prototype.multiDiff = Zotero.Promise.coroutine(function* (otherItems
|
|||
* @param {Boolean} [skipTags=false] - Skip tags
|
||||
* @return {Promise<Zotero.Item>}
|
||||
*/
|
||||
Zotero.Item.prototype.clone = Zotero.Promise.coroutine(function* (libraryID, skipTags) {
|
||||
Zotero.Item.prototype.clone = function (libraryID, skipTags) {
|
||||
Zotero.debug('Cloning item ' + this.id);
|
||||
|
||||
if (libraryID !== undefined && libraryID !== null && typeof libraryID !== 'number') {
|
||||
throw new Error("libraryID must be null or an integer");
|
||||
}
|
||||
|
||||
yield this.loadPrimaryData();
|
||||
|
||||
if (libraryID === undefined || libraryID === null) {
|
||||
libraryID = this.libraryID;
|
||||
}
|
||||
|
@ -3579,7 +3668,6 @@ Zotero.Item.prototype.clone = Zotero.Promise.coroutine(function* (libraryID, ski
|
|||
newItem.libraryID = libraryID;
|
||||
newItem.setType(this.itemTypeID);
|
||||
|
||||
yield this.loadItemData();
|
||||
var fieldIDs = this.getUsedFields();
|
||||
for (let i = 0; i < fieldIDs.length; i++) {
|
||||
let fieldID = fieldIDs[i];
|
||||
|
@ -3588,11 +3676,9 @@ Zotero.Item.prototype.clone = Zotero.Promise.coroutine(function* (libraryID, ski
|
|||
|
||||
// Regular item
|
||||
if (this.isRegularItem()) {
|
||||
yield this.loadCreators();
|
||||
newItem.setCreators(this.getCreators());
|
||||
}
|
||||
else {
|
||||
yield this.loadNote();
|
||||
newItem.setNote(this.getNote());
|
||||
if (sameLibrary) {
|
||||
var parent = this.parentKey;
|
||||
|
@ -3614,18 +3700,16 @@ Zotero.Item.prototype.clone = Zotero.Promise.coroutine(function* (libraryID, ski
|
|||
}
|
||||
|
||||
if (!skipTags) {
|
||||
yield this.loadTags();
|
||||
newItem.setTags(this.getTags());
|
||||
}
|
||||
|
||||
if (sameLibrary) {
|
||||
// DEBUG: this will add reverse-only relateds too
|
||||
yield this.loadRelations();
|
||||
newItem.setRelations(this.getRelations());
|
||||
}
|
||||
|
||||
return newItem;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -3721,8 +3805,6 @@ Zotero.Item.prototype.isCollection = function() {
|
|||
|
||||
/**
|
||||
* Populate the object's data from an API JSON data object
|
||||
*
|
||||
* If this object is identified (has an id or library/key), loadAllData() must have been called.
|
||||
*/
|
||||
Zotero.Item.prototype.fromJSON = function (json) {
|
||||
if (!json.itemType && !this._itemTypeID) {
|
||||
|
@ -3867,7 +3949,7 @@ Zotero.Item.prototype.fromJSON = function (json) {
|
|||
/**
|
||||
* @param {Object} options
|
||||
*/
|
||||
Zotero.Item.prototype.toJSON = Zotero.Promise.coroutine(function* (options = {}) {
|
||||
Zotero.Item.prototype.toJSON = function (options = {}) {
|
||||
var env = this._preToJSON(options);
|
||||
var mode = env.mode;
|
||||
|
||||
|
@ -3877,7 +3959,6 @@ Zotero.Item.prototype.toJSON = Zotero.Promise.coroutine(function* (options = {})
|
|||
obj.itemType = Zotero.ItemTypes.getName(this.itemTypeID);
|
||||
|
||||
// Fields
|
||||
yield this.loadItemData();
|
||||
for (let i in this._itemData) {
|
||||
let val = this.getField(i) + '';
|
||||
if (val !== '' || mode == 'full') {
|
||||
|
@ -3887,7 +3968,6 @@ Zotero.Item.prototype.toJSON = Zotero.Promise.coroutine(function* (options = {})
|
|||
|
||||
// Creators
|
||||
if (this.isRegularItem()) {
|
||||
yield this.loadCreators()
|
||||
obj.creators = this.getCreatorsJSON();
|
||||
}
|
||||
else {
|
||||
|
@ -3912,18 +3992,18 @@ Zotero.Item.prototype.toJSON = Zotero.Promise.coroutine(function* (options = {})
|
|||
|
||||
if (this.isFileAttachment()) {
|
||||
if (options.syncedStorageProperties) {
|
||||
obj.mtime = yield Zotero.Sync.Storage.Local.getSyncedModificationTime(this.id);
|
||||
obj.md5 = yield Zotero.Sync.Storage.Local.getSyncedHash(this.id);
|
||||
obj.mtime = this.attachmentSyncedModificationTime;
|
||||
obj.md5 = this.attachmentSyncedHash;
|
||||
}
|
||||
else {
|
||||
obj.mtime = (yield this.attachmentModificationTime) || null;
|
||||
obj.md5 = (yield this.attachmentHash) || null;
|
||||
// TEMP
|
||||
//obj.mtime = (yield this.attachmentModificationTime) || null;
|
||||
//obj.md5 = (yield this.attachmentHash) || null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Notes and embedded attachment notes
|
||||
yield this.loadNote();
|
||||
let note = this.getNote();
|
||||
if (note !== "" || mode == 'full' || (mode == 'new' && this.isNote())) {
|
||||
obj.note = note;
|
||||
|
@ -3932,7 +4012,6 @@ Zotero.Item.prototype.toJSON = Zotero.Promise.coroutine(function* (options = {})
|
|||
|
||||
// Tags
|
||||
obj.tags = [];
|
||||
yield this.loadTags()
|
||||
var tags = this.getTags();
|
||||
for (let i=0; i<tags.length; i++) {
|
||||
obj.tags.push(tags[i]);
|
||||
|
@ -3940,14 +4019,12 @@ Zotero.Item.prototype.toJSON = Zotero.Promise.coroutine(function* (options = {})
|
|||
|
||||
// Collections
|
||||
if (this.isTopLevelItem()) {
|
||||
yield this.loadCollections();
|
||||
obj.collections = this.getCollections().map(function (id) {
|
||||
return this.ContainerObjectsClass.getLibraryAndKeyFromID(id).key;
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
// Relations
|
||||
yield this.loadRelations();
|
||||
obj.relations = this.getRelations()
|
||||
|
||||
// Deleted
|
||||
|
@ -3961,11 +4038,11 @@ Zotero.Item.prototype.toJSON = Zotero.Promise.coroutine(function* (options = {})
|
|||
if (obj.accessDate) obj.accessDate = Zotero.Date.sqlToISO8601(obj.accessDate);
|
||||
|
||||
return this._postToJSON(env);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Zotero.Item.prototype.toResponseJSON = Zotero.Promise.coroutine(function* (options = {}) {
|
||||
var json = yield this.constructor._super.prototype.toResponseJSON.apply(this, options);
|
||||
Zotero.Item.prototype.toResponseJSON = function (options = {}) {
|
||||
var json = this.constructor._super.prototype.toResponseJSON.apply(this, options);
|
||||
|
||||
// creatorSummary
|
||||
var firstCreator = this.getField('firstCreator');
|
||||
|
@ -3983,7 +4060,7 @@ Zotero.Item.prototype.toResponseJSON = Zotero.Promise.coroutine(function* (optio
|
|||
json.meta.numChildren = this.numChildren();
|
||||
}
|
||||
return json;
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -3995,98 +4072,6 @@ Zotero.Item.prototype.toResponseJSON = Zotero.Promise.coroutine(function* (optio
|
|||
/*
|
||||
* Load in the field data from the database
|
||||
*/
|
||||
Zotero.Item.prototype.loadItemData = Zotero.Promise.coroutine(function* (reload) {
|
||||
if (this._loaded.itemData && !reload) {
|
||||
return;
|
||||
}
|
||||
|
||||
Zotero.debug("Loading item data for item " + this.libraryKey);
|
||||
|
||||
if (!this.id) {
|
||||
throw new Error('ItemID not set for object before attempting to load data');
|
||||
}
|
||||
|
||||
if (!this.isNote()) {
|
||||
var sql = "SELECT fieldID, value FROM itemData NATURAL JOIN itemDataValues WHERE itemID=?";
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
this.id,
|
||||
{
|
||||
onRow: function (row) {
|
||||
this.setField(row.getResultByIndex(0), row.getResultByIndex(1), true);
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
|
||||
// Mark nonexistent fields as loaded
|
||||
let itemTypeFields = Zotero.ItemFields.getItemTypeFields(this.itemTypeID);
|
||||
for (let i=0; i<itemTypeFields.length; i++) {
|
||||
let fieldID = itemTypeFields[i];
|
||||
if (this._itemData[fieldID] === null) {
|
||||
this._itemData[fieldID] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isNote() || this.isAttachment()) {
|
||||
var sql = "SELECT title FROM itemNotes WHERE itemID=?";
|
||||
var row = yield Zotero.DB.rowQueryAsync(sql, this.id);
|
||||
if (row) {
|
||||
let title = row.title;
|
||||
this._noteTitle = title !== false ? title : '';
|
||||
}
|
||||
}
|
||||
|
||||
this._loaded.itemData = true;
|
||||
this._clearChanged('itemData');
|
||||
yield this.loadDisplayTitle(reload);
|
||||
});
|
||||
|
||||
|
||||
Zotero.Item.prototype.loadNote = Zotero.Promise.coroutine(function* (reload) {
|
||||
if (this._loaded.note && !reload) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isNote() && !this.isAttachment()) {
|
||||
throw new Error("Can only load note for note or attachment item");
|
||||
}
|
||||
|
||||
Zotero.debug("Loading note data for item " + this.libraryKey);
|
||||
|
||||
var sql = "SELECT note FROM itemNotes WHERE itemID=?";
|
||||
var row = yield Zotero.DB.rowQueryAsync(sql, this.id);
|
||||
if (row) {
|
||||
let note = row.note;
|
||||
|
||||
// Convert non-HTML notes on-the-fly
|
||||
if (note !== "") {
|
||||
if (!note.substr(0, 36).match(/^<div class="zotero-note znv[0-9]+">/)) {
|
||||
note = Zotero.Utilities.htmlSpecialChars(note);
|
||||
note = Zotero.Notes.notePrefix + '<p>'
|
||||
+ note.replace(/\n/g, '</p><p>')
|
||||
.replace(/\t/g, ' ')
|
||||
.replace(/ /g, ' ')
|
||||
+ '</p>' + Zotero.Notes.noteSuffix;
|
||||
note = note.replace(/<p>\s*<\/p>/g, '<p> </p>');
|
||||
let sql = "UPDATE itemNotes SET note=? WHERE itemID=?";
|
||||
yield Zotero.DB.queryAsync(sql, [note, this.id]);
|
||||
}
|
||||
|
||||
// Don't include <div> wrapper when returning value
|
||||
let startLen = note.substr(0, 36).match(/^<div class="zotero-note znv[0-9]+">/)[0].length;
|
||||
let endLen = 6; // "</div>".length
|
||||
note = note.substr(startLen, note.length - startLen - endLen);
|
||||
}
|
||||
|
||||
this._noteText = note ? note : '';
|
||||
}
|
||||
|
||||
this._loaded.note = true;
|
||||
this._clearChanged('note');
|
||||
});
|
||||
|
||||
|
||||
Zotero.Item.prototype.loadDisplayTitle = Zotero.Promise.coroutine(function* (reload) {
|
||||
if (this._displayTitle !== null && !reload) {
|
||||
return;
|
||||
|
@ -4097,7 +4082,6 @@ Zotero.Item.prototype.loadDisplayTitle = Zotero.Promise.coroutine(function* (rel
|
|||
var itemTypeName = Zotero.ItemTypes.getName(itemTypeID);
|
||||
|
||||
if (title === "" && (itemTypeID == 8 || itemTypeID == 10)) { // 'letter' and 'interview' itemTypeIDs
|
||||
yield this.loadCreators();
|
||||
var creatorsData = this.getCreators();
|
||||
var authors = [];
|
||||
var participants = [];
|
||||
|
@ -4175,7 +4159,6 @@ Zotero.Item.prototype.loadDisplayTitle = Zotero.Promise.coroutine(function* (rel
|
|||
strParts.push(part);
|
||||
}
|
||||
|
||||
yield this.loadCreators()
|
||||
var creatorData = this.getCreator(0);
|
||||
if (creatorData && creatorData.creatorTypeID === 1) { // author
|
||||
strParts.push(creatorData.lastName);
|
||||
|
@ -4189,156 +4172,6 @@ Zotero.Item.prototype.loadDisplayTitle = Zotero.Promise.coroutine(function* (rel
|
|||
});
|
||||
|
||||
|
||||
/*
|
||||
* Load in the creators from the database
|
||||
*/
|
||||
Zotero.Item.prototype.loadCreators = Zotero.Promise.coroutine(function* (reload) {
|
||||
if (this._loaded.creators && !reload) {
|
||||
return;
|
||||
}
|
||||
|
||||
Zotero.debug("Loading creators for item " + this.libraryKey);
|
||||
|
||||
if (!this.id) {
|
||||
throw new Error('ItemID not set for item before attempting to load creators');
|
||||
}
|
||||
|
||||
var sql = 'SELECT creatorID, creatorTypeID, orderIndex FROM itemCreators '
|
||||
+ 'WHERE itemID=? ORDER BY orderIndex';
|
||||
var rows = yield Zotero.DB.queryAsync(sql, this.id);
|
||||
|
||||
this._creators = [];
|
||||
this._creatorIDs = [];
|
||||
this._loaded.creators = true;
|
||||
this._clearChanged('creators');
|
||||
|
||||
if (!rows) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var maxOrderIndex = -1;
|
||||
for (var i=0; i<rows.length; i++) {
|
||||
let row = rows[i];
|
||||
if (row.orderIndex > maxOrderIndex) {
|
||||
maxOrderIndex = row.orderIndex;
|
||||
}
|
||||
let creatorData = yield Zotero.Creators.getAsync(row.creatorID);
|
||||
creatorData.creatorTypeID = row.creatorTypeID;
|
||||
this._creators[i] = creatorData;
|
||||
this._creatorIDs[i] = row.creatorID;
|
||||
}
|
||||
if (i <= maxOrderIndex) {
|
||||
Zotero.debug("Fixing incorrect creator indexes for item " + this.libraryKey
|
||||
+ " (" + i + ", " + maxOrderIndex + ")", 2);
|
||||
while (i <= maxOrderIndex) {
|
||||
this._changed.creators[i] = true;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
Zotero.Item.prototype.loadChildItems = Zotero.Promise.coroutine(function* (reload) {
|
||||
if (this._loaded.childItems && !reload) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isNote() || this.isAttachment()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Attachments
|
||||
this._attachments = {
|
||||
rows: null,
|
||||
chronologicalWithTrashed: null,
|
||||
chronologicalWithoutTrashed: null,
|
||||
alphabeticalWithTrashed: null,
|
||||
alphabeticalWithoutTrashed: null
|
||||
};
|
||||
var sql = "SELECT A.itemID, value AS title, CASE WHEN DI.itemID IS NULL THEN 0 ELSE 1 END AS trashed "
|
||||
+ "FROM itemAttachments A "
|
||||
+ "NATURAL JOIN items I "
|
||||
+ "LEFT JOIN itemData ID ON (fieldID=110 AND A.itemID=ID.itemID) "
|
||||
+ "LEFT JOIN itemDataValues IDV USING (valueID) "
|
||||
+ "LEFT JOIN deletedItems DI USING (itemID) "
|
||||
+ "WHERE parentItemID=?";
|
||||
// Since we do the sort here and cache these results, a restart will be required
|
||||
// if this pref (off by default) is turned on, but that's OK
|
||||
if (Zotero.Prefs.get('sortAttachmentsChronologically')) {
|
||||
sql += " ORDER BY dateAdded";
|
||||
}
|
||||
this._attachments.rows = yield Zotero.DB.queryAsync(sql, this.id);
|
||||
|
||||
//
|
||||
// Notes
|
||||
//
|
||||
this._notes = {
|
||||
rows: null,
|
||||
rowsEmbedded: null,
|
||||
chronologicalWithTrashed: null,
|
||||
chronologicalWithoutTrashed: null,
|
||||
alphabeticalWithTrashed: null,
|
||||
alphabeticalWithoutTrashed: null,
|
||||
numWithTrashed: null,
|
||||
numWithoutTrashed: null,
|
||||
numWithTrashedWithEmbedded: null,
|
||||
numWithoutTrashedWithoutEmbedded: null
|
||||
};
|
||||
var sql = "SELECT N.itemID, title, CASE WHEN DI.itemID IS NULL THEN 0 ELSE 1 END AS trashed "
|
||||
+ "FROM itemNotes N "
|
||||
+ "NATURAL JOIN items I "
|
||||
+ "LEFT JOIN deletedItems DI USING (itemID) "
|
||||
+ "WHERE parentItemID=?";
|
||||
if (Zotero.Prefs.get('sortAttachmentsChronologically')) {
|
||||
sql += " ORDER BY dateAdded";
|
||||
}
|
||||
this._notes.rows = yield Zotero.DB.queryAsync(sql, this.id);
|
||||
|
||||
this._loaded.childItems = true;
|
||||
this._clearChanged('childItems');
|
||||
});
|
||||
|
||||
|
||||
Zotero.Item.prototype.loadTags = Zotero.Promise.coroutine(function* (reload) {
|
||||
if (this._loaded.tags && !reload) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._id) {
|
||||
return;
|
||||
}
|
||||
var sql = "SELECT tagID AS id, name AS tag, type FROM itemTags "
|
||||
+ "JOIN tags USING (tagID) WHERE itemID=?";
|
||||
var rows = yield Zotero.DB.queryAsync(sql, this.id);
|
||||
|
||||
this._tags = [];
|
||||
for (let i=0; i<rows.length; i++) {
|
||||
let row = rows[i];
|
||||
this._tags.push(Zotero.Tags.cleanData(row));
|
||||
}
|
||||
|
||||
this._loaded.tags = true;
|
||||
this._clearChanged('tags');
|
||||
});
|
||||
|
||||
|
||||
|
||||
Zotero.Item.prototype.loadCollections = Zotero.Promise.coroutine(function* (reload) {
|
||||
if (this._loaded.collections && !reload) {
|
||||
return;
|
||||
}
|
||||
if (!this._id) {
|
||||
return;
|
||||
}
|
||||
var sql = "SELECT collectionID FROM collectionItems WHERE itemID=?";
|
||||
this._collections = yield Zotero.DB.columnQueryAsync(sql, this.id);
|
||||
this._loaded.collections = true;
|
||||
this._clearChanged('collections');
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Return an item in the specified library equivalent to this item
|
||||
*
|
||||
|
|
|
@ -84,7 +84,10 @@ Zotero.Items = function() {
|
|||
attachmentCharset: "CS.charset AS attachmentCharset",
|
||||
attachmentLinkMode: "IA.linkMode AS attachmentLinkMode",
|
||||
attachmentContentType: "IA.contentType AS attachmentContentType",
|
||||
attachmentPath: "IA.path AS attachmentPath"
|
||||
attachmentPath: "IA.path AS attachmentPath",
|
||||
attachmentSyncState: "IA.syncState AS attachmentSyncState",
|
||||
attachmentSyncedModificationTime: "IA.storageModTime AS attachmentSyncedModificationTime",
|
||||
attachmentSyncedHash: "IA.storageHash AS attachmentSyncedHash"
|
||||
};
|
||||
}
|
||||
}, {lazy: true});
|
||||
|
@ -204,7 +207,7 @@ Zotero.Items = function() {
|
|||
for (let i=0; i<ids.length; i++) {
|
||||
let prefix = i > 0 ? ',\n' : '';
|
||||
let item = yield this.getAsync(ids[i], { noCache: true });
|
||||
var json = yield item.toResponseJSON();
|
||||
var json = item.toResponseJSON();
|
||||
yield prefix + JSON.stringify(json, null, 4);
|
||||
}
|
||||
|
||||
|
@ -212,105 +215,24 @@ Zotero.Items = function() {
|
|||
};
|
||||
|
||||
|
||||
this._cachedFields = {};
|
||||
this.cacheFields = Zotero.Promise.coroutine(function* (libraryID, fields, items) {
|
||||
if (items && items.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var t = new Date;
|
||||
|
||||
fields = fields.concat();
|
||||
|
||||
// Needed for display titles for some item types
|
||||
if (fields.indexOf('title') != -1) {
|
||||
fields.push('reporter', 'court');
|
||||
}
|
||||
|
||||
Zotero.debug("Caching fields [" + fields.join() + "]"
|
||||
+ (items ? " for " + items.length + " items" : '')
|
||||
+ " in library " + libraryID);
|
||||
|
||||
if (items && items.length > 0) {
|
||||
yield this._load(libraryID, items);
|
||||
}
|
||||
else {
|
||||
yield this._load(libraryID);
|
||||
}
|
||||
|
||||
var primaryFields = [];
|
||||
var fieldIDs = [];
|
||||
for each(var field in fields) {
|
||||
// Check if field already cached
|
||||
if (this._cachedFields[libraryID] && this._cachedFields[libraryID].indexOf(field) != -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!this._cachedFields[libraryID]) {
|
||||
this._cachedFields[libraryID] = [];
|
||||
}
|
||||
this._cachedFields[libraryID].push(field);
|
||||
|
||||
if (this.isPrimaryField(field)) {
|
||||
primaryFields.push(field);
|
||||
}
|
||||
else {
|
||||
fieldIDs.push(Zotero.ItemFields.getID(field));
|
||||
if (Zotero.ItemFields.isBaseField(field)) {
|
||||
fieldIDs = fieldIDs.concat(Zotero.ItemFields.getTypeFieldsFromBase(field));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (primaryFields.length) {
|
||||
var sql = "SELECT O.itemID, "
|
||||
+ primaryFields.map((val) => this.getPrimaryDataSQLPart(val)).join(', ')
|
||||
+ this.primaryDataSQLFrom + " AND O.libraryID=?";
|
||||
var params = [libraryID];
|
||||
if (items) {
|
||||
sql += " AND O.itemID IN (" + items.join() + ")";
|
||||
}
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
onRow: function (row) {
|
||||
let obj = {
|
||||
itemID: row.getResultByIndex(0)
|
||||
};
|
||||
for (let i=0; i<primaryFields.length; i++) {
|
||||
obj[primaryFields[i]] = row.getResultByIndex(i);
|
||||
}
|
||||
Zotero.debug(obj.itemID);
|
||||
Zotero.debug(Object.keys(this._objectCache));
|
||||
this._objectCache[obj.itemID].loadFromRow(obj);
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// All fields already cached
|
||||
if (!fieldIDs.length) {
|
||||
Zotero.debug('All fields already cached');
|
||||
return;
|
||||
}
|
||||
|
||||
var sql = "SELECT itemID FROM items WHERE libraryID=?";
|
||||
var params = [libraryID];
|
||||
var allItemIDs = yield Zotero.DB.columnQueryAsync(sql, params);
|
||||
//
|
||||
// Bulk data loading functions
|
||||
//
|
||||
// These are called by Zotero.DataObjects.prototype._loadDataType().
|
||||
//
|
||||
this._loadItemData = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
|
||||
var missingItems = {};
|
||||
var itemFieldsCached = {};
|
||||
|
||||
var sql = "SELECT itemID, fieldID, value FROM items JOIN itemData USING (itemID) "
|
||||
+ "JOIN itemDataValues USING (valueID) WHERE libraryID=?";
|
||||
var params = [libraryID];
|
||||
if (items) {
|
||||
sql += " AND itemID IN (" + items.join() + ")";
|
||||
}
|
||||
sql += " AND fieldID IN (" + fieldIDs.join() + ")";
|
||||
var sql = "SELECT itemID, fieldID, value FROM items "
|
||||
+ "JOIN itemData USING (itemID) "
|
||||
+ "JOIN itemDataValues USING (valueID) WHERE libraryID=? AND itemTypeID!=?" + idSQL;
|
||||
var params = [libraryID, Zotero.ItemTypes.getID('note')];
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
let itemID = row.getResultByIndex(0);
|
||||
let fieldID = row.getResultByIndex(1);
|
||||
|
@ -318,19 +240,17 @@ Zotero.Items = function() {
|
|||
|
||||
//Zotero.debug('Setting field ' + fieldID + ' for item ' + itemID);
|
||||
if (this._objectCache[itemID]) {
|
||||
if (value === null) {
|
||||
value = false;
|
||||
}
|
||||
this._objectCache[itemID].setField(fieldID, value, true);
|
||||
}
|
||||
else {
|
||||
if (!missingItems) {
|
||||
var missingItems = {};
|
||||
}
|
||||
if (!missingItems[itemID]) {
|
||||
missingItems[itemID] = true;
|
||||
Zotero.debug("itemData row references nonexistent item " + itemID);
|
||||
Components.utils.reportError("itemData row references nonexistent item " + itemID);
|
||||
Zotero.logError("itemData row references nonexistent item " + itemID);
|
||||
}
|
||||
}
|
||||
|
||||
if (!itemFieldsCached[itemID]) {
|
||||
itemFieldsCached[itemID] = {};
|
||||
}
|
||||
|
@ -339,68 +259,464 @@ Zotero.Items = function() {
|
|||
}
|
||||
);
|
||||
|
||||
// Set nonexistent fields in the cache list to false (instead of null)
|
||||
for (let i=0; i<allItemIDs.length; i++) {
|
||||
let itemID = allItemIDs[i];
|
||||
for (let j=0; j<fieldIDs.length; j++) {
|
||||
let fieldID = fieldIDs[j];
|
||||
if (Zotero.ItemFields.isValidForType(fieldID, this._objectCache[itemID].itemTypeID)) {
|
||||
if (!itemFieldsCached[itemID] || !itemFieldsCached[itemID][fieldID]) {
|
||||
//Zotero.debug('Setting field ' + fieldID + ' to false for item ' + itemID);
|
||||
this._objectCache[itemID].setField(fieldID, false, true);
|
||||
var sql = "SELECT itemID FROM items WHERE libraryID=?" + idSQL;
|
||||
var params = [libraryID];
|
||||
var allItemIDs = [];
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
let itemID = row.getResultByIndex(0);
|
||||
let item = this._objectCache[itemID];
|
||||
|
||||
// Set nonexistent fields in the cache list to false (instead of null)
|
||||
let fieldIDs = Zotero.ItemFields.getItemTypeFields(item.itemTypeID);
|
||||
for (let j=0; j<fieldIDs.length; j++) {
|
||||
let fieldID = fieldIDs[j];
|
||||
if (!itemFieldsCached[itemID] || !itemFieldsCached[itemID][fieldID]) {
|
||||
//Zotero.debug('Setting field ' + fieldID + ' to false for item ' + itemID);
|
||||
item.setField(fieldID, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allItemIDs.push(itemID);
|
||||
}.bind(this)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
// If 'title' is one of the fields, load in display titles (note titles, letter titles...)
|
||||
if (fields.indexOf('title') != -1) {
|
||||
var titleFieldID = Zotero.ItemFields.getID('title');
|
||||
var titleFieldID = Zotero.ItemFields.getID('title');
|
||||
|
||||
// Note titles
|
||||
var sql = "SELECT itemID, title FROM items JOIN itemNotes USING (itemID) "
|
||||
+ "WHERE libraryID=? AND itemID NOT IN (SELECT itemID FROM itemAttachments)";
|
||||
var params = [libraryID];
|
||||
if (items) {
|
||||
sql += " AND itemID IN (" + items.join() + ")";
|
||||
// Note titles
|
||||
var sql = "SELECT itemID, title FROM items JOIN itemNotes USING (itemID) "
|
||||
+ "WHERE libraryID=? AND itemID NOT IN (SELECT itemID FROM itemAttachments)" + idSQL;
|
||||
var params = [libraryID];
|
||||
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
onRow: function (row) {
|
||||
let itemID = row.getResultByIndex(0);
|
||||
let title = row.getResultByIndex(1);
|
||||
|
||||
//Zotero.debug('Setting title for note ' + row.itemID);
|
||||
if (this._objectCache[itemID]) {
|
||||
this._objectCache[itemID].setField(titleFieldID, title, true);
|
||||
}
|
||||
else {
|
||||
if (!missingItems[itemID]) {
|
||||
missingItems[itemID] = true;
|
||||
Zotero.logError("itemData row references nonexistent item " + itemID);
|
||||
}
|
||||
}
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
onRow: function (row) {
|
||||
let itemID = row.getResultByIndex(0);
|
||||
let title = row.getResultByIndex(1);
|
||||
for (let i=0; i<allItemIDs.length; i++) {
|
||||
let itemID = allItemIDs[i];
|
||||
let item = this._objectCache[itemID];
|
||||
|
||||
//Zotero.debug('Setting title for note ' + row.itemID);
|
||||
if (this._objectCache[itemID]) {
|
||||
this._objectCache[itemID].setField(titleFieldID, title, true);
|
||||
}
|
||||
else {
|
||||
if (!missingItems) {
|
||||
var missingItems = {};
|
||||
}
|
||||
if (!missingItems[itemID]) {
|
||||
missingItems[itemID] = true;
|
||||
Components.utils.reportError(
|
||||
"itemData row references nonexistent item " + itemID
|
||||
);
|
||||
}
|
||||
}
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
// Mark as loaded
|
||||
item._loaded.itemData = true;
|
||||
item._clearChanged('itemData');
|
||||
|
||||
// Display titles
|
||||
for (let i=0; i<allItemIDs.length; i++) {
|
||||
let itemID = allItemIDs[i];
|
||||
let item = this._objectCache[itemID];
|
||||
yield item.loadDisplayTitle()
|
||||
yield item.loadDisplayTitle()
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this._loadCreators = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
|
||||
var sql = 'SELECT itemID, creatorID, creatorTypeID, orderIndex '
|
||||
+ 'FROM items LEFT JOIN itemCreators USING (itemID) '
|
||||
+ 'WHERE libraryID=?' + idSQL + " ORDER BY itemID, orderIndex";
|
||||
var params = [libraryID];
|
||||
var rows = yield Zotero.DB.queryAsync(sql, params);
|
||||
|
||||
// Mark creator indexes above the number of creators as changed,
|
||||
// so that they're cleared if the item is saved
|
||||
var fixIncorrectIndexes = function (item, numCreators, maxOrderIndex) {
|
||||
Zotero.debug("Fixing incorrect creator indexes for item " + item.libraryKey
|
||||
+ " (" + numCreators + ", " + maxOrderIndex + ")", 2);
|
||||
var i = numCreators;
|
||||
while (i <= maxOrderIndex) {
|
||||
item._changed.creators[i] = true;
|
||||
i++;
|
||||
}
|
||||
};
|
||||
|
||||
var lastItemID;
|
||||
var item;
|
||||
var index = 0;
|
||||
var maxOrderIndex = -1;
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
let itemID = row.itemID;
|
||||
|
||||
if (itemID != lastItemID) {
|
||||
if (!this._objectCache[itemID]) {
|
||||
throw new Error("Item " + itemID + " not loaded");
|
||||
}
|
||||
item = this._objectCache[itemID];
|
||||
|
||||
item._creators = [];
|
||||
item._creatorIDs = [];
|
||||
item._loaded.creators = true;
|
||||
item._clearChanged('creators');
|
||||
|
||||
if (!row.creatorID) {
|
||||
lastItemID = row.itemID;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (index <= maxOrderIndex) {
|
||||
fixIncorrectIndexes(item, index, maxOrderIndex);
|
||||
}
|
||||
|
||||
index = 0;
|
||||
maxOrderIndex = -1;
|
||||
}
|
||||
|
||||
lastItemID = row.itemID;
|
||||
|
||||
if (row.orderIndex > maxOrderIndex) {
|
||||
maxOrderIndex = row.orderIndex;
|
||||
}
|
||||
|
||||
let creatorData = Zotero.Creators.get(row.creatorID);
|
||||
creatorData.creatorTypeID = row.creatorTypeID;
|
||||
item._creators[index] = creatorData;
|
||||
item._creatorIDs[index] = row.creatorID;
|
||||
index++;
|
||||
}
|
||||
|
||||
Zotero.debug("Cached fields in " + ((new Date) - t) + "ms");
|
||||
if (index <= maxOrderIndex) {
|
||||
fixIncorrectIndexes(item, index, maxOrderIndex);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this._loadNotes = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
|
||||
var notesToUpdate = [];
|
||||
|
||||
var sql = "SELECT itemID, note FROM items "
|
||||
+ "JOIN itemNotes USING (itemID) "
|
||||
+ "WHERE libraryID=?" + idSQL;
|
||||
var params = [libraryID];
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
let itemID = row.getResultByIndex(0);
|
||||
let item = this._objectCache[itemID];
|
||||
if (!item) {
|
||||
throw new Error("Item " + itemID + " not found");
|
||||
}
|
||||
let note = row.getResultByIndex(1);
|
||||
|
||||
// Convert non-HTML notes on-the-fly
|
||||
if (note !== "") {
|
||||
if (!note.substr(0, 36).match(/^<div class="zotero-note znv[0-9]+">/)) {
|
||||
note = Zotero.Utilities.htmlSpecialChars(note);
|
||||
note = Zotero.Notes.notePrefix + '<p>'
|
||||
+ note.replace(/\n/g, '</p><p>')
|
||||
.replace(/\t/g, ' ')
|
||||
.replace(/ /g, ' ')
|
||||
+ '</p>' + Zotero.Notes.noteSuffix;
|
||||
note = note.replace(/<p>\s*<\/p>/g, '<p> </p>');
|
||||
notesToUpdate.push([item.id, note]);
|
||||
}
|
||||
|
||||
// Don't include <div> wrapper when returning value
|
||||
let startLen = note.substr(0, 36).match(/^<div class="zotero-note znv[0-9]+">/)[0].length;
|
||||
let endLen = 6; // "</div>".length
|
||||
note = note.substr(startLen, note.length - startLen - endLen);
|
||||
}
|
||||
|
||||
item._noteText = note ? note : '';
|
||||
item._loaded.note = true;
|
||||
item._clearChanged('note');
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
|
||||
if (notesToUpdate.length) {
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
for (let i = 0; i < notesToUpdate.length; i++) {
|
||||
let row = notesToUpdate[i];
|
||||
let sql = "UPDATE itemNotes SET note=? WHERE itemID=?";
|
||||
yield Zotero.DB.queryAsync(sql, [row[1], row[0]]);
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
// Mark notes and attachments without notes as loaded
|
||||
sql = "SELECT itemID FROM items WHERE libraryID=?" + idSQL
|
||||
+ " AND itemTypeID IN (?, ?) AND itemID NOT IN (SELECT itemID FROM itemNotes)";
|
||||
params = [libraryID, Zotero.ItemTypes.getID('note'), Zotero.ItemTypes.getID('attachment')];
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
let itemID = row.getResultByIndex(0);
|
||||
let item = this._objectCache[itemID];
|
||||
if (!item) {
|
||||
throw new Error("Item " + itemID + " not loaded");
|
||||
}
|
||||
|
||||
item._noteText = '';
|
||||
item._loaded.note = true;
|
||||
item._clearChanged('note');
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
this._loadChildItems = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
|
||||
var params = [libraryID];
|
||||
var rows = [];
|
||||
var onRow = function (row, setFunc) {
|
||||
var itemID = row.getResultByIndex(0);
|
||||
|
||||
if (lastItemID && itemID !== lastItemID) {
|
||||
setFunc(lastItemID, rows);
|
||||
rows = [];
|
||||
}
|
||||
|
||||
lastItemID = itemID;
|
||||
rows.push({
|
||||
itemID: row.getResultByIndex(1),
|
||||
title: row.getResultByIndex(2),
|
||||
trashed: row.getResultByIndex(3)
|
||||
});
|
||||
};
|
||||
|
||||
var sql = "SELECT parentItemID, A.itemID, value AS title, "
|
||||
+ "CASE WHEN DI.itemID IS NULL THEN 0 ELSE 1 END AS trashed "
|
||||
+ "FROM itemAttachments A "
|
||||
+ "JOIN items I ON (A.parentItemID=I.itemID) "
|
||||
+ "LEFT JOIN itemData ID ON (fieldID=110 AND A.itemID=ID.itemID) "
|
||||
+ "LEFT JOIN itemDataValues IDV USING (valueID) "
|
||||
+ "LEFT JOIN deletedItems DI USING (itemID) "
|
||||
+ "WHERE libraryID=?"
|
||||
+ (ids.length ? " AND parentItemID IN (" + ids.map(id => parseInt(id)).join(", ") + ")" : "");
|
||||
// Since we do the sort here and cache these results, a restart will be required
|
||||
// if this pref (off by default) is turned on, but that's OK
|
||||
if (Zotero.Prefs.get('sortAttachmentsChronologically')) {
|
||||
sql += " ORDER BY parentItemID, dateAdded";
|
||||
}
|
||||
var setAttachmentItem = function (itemID, rows) {
|
||||
var item = this._objectCache[itemID];
|
||||
if (!item) {
|
||||
throw new Error("Item " + itemID + " not loaded");
|
||||
}
|
||||
|
||||
item._attachments = {
|
||||
rows,
|
||||
chronologicalWithTrashed: null,
|
||||
chronologicalWithoutTrashed: null,
|
||||
alphabeticalWithTrashed: null,
|
||||
alphabeticalWithoutTrashed: null
|
||||
};
|
||||
}.bind(this);
|
||||
var lastItemID = null;
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
onRow(row, setAttachmentItem);
|
||||
}
|
||||
}
|
||||
);
|
||||
if (lastItemID) {
|
||||
setAttachmentItem(lastItemID, rows);
|
||||
}
|
||||
|
||||
//
|
||||
// Notes
|
||||
//
|
||||
sql = "SELECT parentItemID, N.itemID, title, "
|
||||
+ "CASE WHEN DI.itemID IS NULL THEN 0 ELSE 1 END AS trashed "
|
||||
+ "FROM itemNotes N "
|
||||
+ "JOIN items I ON (N.parentItemID=I.itemID) "
|
||||
+ "LEFT JOIN deletedItems DI USING (itemID) "
|
||||
+ "WHERE libraryID=?"
|
||||
+ (ids.length ? " AND parentItemID IN (" + ids.map(id => parseInt(id)).join(", ") + ")" : "");
|
||||
if (Zotero.Prefs.get('sortNotesChronologically')) {
|
||||
sql += " ORDER BY parentItemID, dateAdded";
|
||||
}
|
||||
var setNoteItem = function (itemID, rows) {
|
||||
var item = this._objectCache[itemID];
|
||||
if (!item) {
|
||||
throw new Error("Item " + itemID + " not loaded");
|
||||
}
|
||||
|
||||
item._notes = {
|
||||
rows,
|
||||
rowsEmbedded: null,
|
||||
chronologicalWithTrashed: null,
|
||||
chronologicalWithoutTrashed: null,
|
||||
alphabeticalWithTrashed: null,
|
||||
alphabeticalWithoutTrashed: null,
|
||||
numWithTrashed: null,
|
||||
numWithoutTrashed: null,
|
||||
numWithTrashedWithEmbedded: null,
|
||||
numWithoutTrashedWithoutEmbedded: null
|
||||
};
|
||||
}.bind(this);
|
||||
lastItemID = null;
|
||||
rows = [];
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
onRow(row, setNoteItem);
|
||||
}
|
||||
}
|
||||
);
|
||||
if (lastItemID) {
|
||||
setNoteItem(lastItemID, rows);
|
||||
}
|
||||
|
||||
// Mark all top-level items as having child items loaded
|
||||
sql = "SELECT itemID FROM items I WHERE libraryID=?" + idSQL + " AND itemID NOT IN "
|
||||
+ "(SELECT itemID FROM itemAttachments UNION SELECT itemID FROM itemNotes)";
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
var itemID = row.getResultByIndex(0);
|
||||
var item = this._objectCache[itemID];
|
||||
if (!item) {
|
||||
throw new Error("Item " + itemID + " not loaded");
|
||||
}
|
||||
item._loaded.childItems = true;
|
||||
item._clearChanged('childItems');
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
this._loadTags = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
|
||||
var sql = "SELECT itemID, name, type FROM items "
|
||||
+ "LEFT JOIN itemTags USING (itemID) "
|
||||
+ "LEFT JOIN tags USING (tagID) WHERE libraryID=?" + idSQL;
|
||||
var params = [libraryID];
|
||||
|
||||
var lastItemID;
|
||||
var rows = [];
|
||||
var setRows = function (itemID, rows) {
|
||||
var item = this._objectCache[itemID];
|
||||
if (!item) {
|
||||
throw new Error("Item " + itemID + " not found");
|
||||
}
|
||||
|
||||
item._tags = [];
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
item._tags.push(Zotero.Tags.cleanData(row));
|
||||
}
|
||||
|
||||
item._loaded.tags = true;
|
||||
item._clearChanged('tags');
|
||||
}.bind(this);
|
||||
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
let itemID = row.getResultByIndex(0);
|
||||
|
||||
if (lastItemID && itemID !== lastItemID) {
|
||||
setRows(lastItemID, rows);
|
||||
rows = [];
|
||||
}
|
||||
|
||||
lastItemID = itemID;
|
||||
|
||||
// Item has no tags
|
||||
let tag = row.getResultByIndex(1);
|
||||
if (tag === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
rows.push({
|
||||
tag: tag,
|
||||
type: row.getResultByIndex(2)
|
||||
});
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
if (lastItemID) {
|
||||
setRows(lastItemID, rows);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this._loadCollections = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
|
||||
var sql = "SELECT itemID, collectionID FROM items "
|
||||
+ "LEFT JOIN collectionItems USING (itemID) "
|
||||
+ "WHERE libraryID=?" + idSQL;
|
||||
var params = [libraryID];
|
||||
|
||||
var lastItemID;
|
||||
var rows = [];
|
||||
var setRows = function (itemID, rows) {
|
||||
var item = this._objectCache[itemID];
|
||||
if (!item) {
|
||||
throw new Error("Item " + itemID + " not found");
|
||||
}
|
||||
|
||||
item._collections = rows;
|
||||
item._loaded.collections = true;
|
||||
item._clearChanged('collections');
|
||||
}.bind(this);
|
||||
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
let itemID = row.getResultByIndex(0);
|
||||
|
||||
if (lastItemID && itemID !== lastItemID) {
|
||||
setRows(lastItemID, rows);
|
||||
rows = [];
|
||||
}
|
||||
|
||||
lastItemID = itemID;
|
||||
let collectionID = row.getResultByIndex(1);
|
||||
// No collections
|
||||
if (collectionID === null) {
|
||||
return;
|
||||
}
|
||||
rows.push(collectionID);
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
if (lastItemID) {
|
||||
setRows(lastItemID, rows);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
@ -409,17 +725,11 @@ Zotero.Items = function() {
|
|||
var otherItemIDs = [];
|
||||
var itemURI = Zotero.URI.getItemURI(item);
|
||||
|
||||
yield item.loadTags();
|
||||
yield item.loadRelations();
|
||||
var replPred = Zotero.Relations.replacedItemPredicate;
|
||||
var toSave = {};
|
||||
toSave[this.id];
|
||||
|
||||
for each(var otherItem in otherItems) {
|
||||
yield otherItem.loadChildItems();
|
||||
yield otherItem.loadCollections();
|
||||
yield otherItem.loadTags();
|
||||
yield otherItem.loadRelations();
|
||||
let otherItemURI = Zotero.URI.getItemURI(otherItem);
|
||||
|
||||
// Move child items to master
|
||||
|
@ -632,16 +942,6 @@ Zotero.Items = function() {
|
|||
});
|
||||
|
||||
|
||||
this._postLoad = function (libraryID, ids) {
|
||||
if (!ids) {
|
||||
if (!this._cachedFields[libraryID]) {
|
||||
this._cachedFields[libraryID] = [];
|
||||
}
|
||||
this._cachedFields[libraryID] = this.primaryFields.concat();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Generate SQL to retrieve firstCreator field
|
||||
*
|
||||
|
|
|
@ -630,7 +630,13 @@ Zotero.DBConnection.prototype.queryAsync = Zotero.Promise.coroutine(function* (s
|
|||
}
|
||||
}
|
||||
}
|
||||
let rows = yield conn.executeCached(sql, params, onRow);
|
||||
let rows;
|
||||
if (options && options.noCache) {
|
||||
rows = yield conn.execute(sql, params, onRow);
|
||||
}
|
||||
else {
|
||||
rows = yield conn.executeCached(sql, params, onRow);
|
||||
}
|
||||
// Parse out the SQL command being used
|
||||
let op = sql.match(/^[^a-z]*[^ ]+/i);
|
||||
if (op) {
|
||||
|
|
|
@ -82,7 +82,7 @@ Zotero.ItemTreeView.prototype.setTree = Zotero.Promise.coroutine(function* (tree
|
|||
|
||||
if (this._treebox) {
|
||||
if (this._needsSort) {
|
||||
yield this.sort();
|
||||
this.sort();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -133,11 +133,11 @@ Zotero.ItemTreeView.prototype.setTree = Zotero.Promise.coroutine(function* (tree
|
|||
if (self._treebox.view.selection.count > 1) {
|
||||
switch (event.keyCode) {
|
||||
case 39:
|
||||
self.expandSelectedRows().done();
|
||||
self.expandSelectedRows();
|
||||
break;
|
||||
|
||||
case 37:
|
||||
self.collapseSelectedRows().done();
|
||||
self.collapseSelectedRows();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -148,7 +148,7 @@ Zotero.ItemTreeView.prototype.setTree = Zotero.Promise.coroutine(function* (tree
|
|||
|
||||
var key = String.fromCharCode(event.which);
|
||||
if (key == '+' && !(event.ctrlKey || event.altKey || event.metaKey)) {
|
||||
self.expandAllRows().done();
|
||||
self.expandAllRows();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
@ -230,8 +230,8 @@ Zotero.ItemTreeView.prototype.setTree = Zotero.Promise.coroutine(function* (tree
|
|||
// handleKeyPress() in zoteroPane.js.
|
||||
tree._handleEnter = function () {};
|
||||
|
||||
yield this.sort();
|
||||
yield this.expandMatchParents();
|
||||
this.sort();
|
||||
this.expandMatchParents();
|
||||
|
||||
if (this._ownerDocument.defaultView.ZoteroPane_Local) {
|
||||
this._ownerDocument.defaultView.ZoteroPane_Local.clearItemsPaneMessage();
|
||||
|
@ -266,13 +266,10 @@ Zotero.ItemTreeView.prototype.setTree = Zotero.Promise.coroutine(function* (tree
|
|||
*/
|
||||
Zotero.ItemTreeView.prototype.refresh = Zotero.serial(Zotero.Promise.coroutine(function* () {
|
||||
Zotero.debug('Refreshing items list for ' + this.id);
|
||||
//if(!Zotero.ItemTreeView._haveCachedFields) yield Zotero.Promise.resolve();
|
||||
|
||||
var cacheFields = ['title', 'date'];
|
||||
|
||||
// Cache the visible fields so they don't load individually
|
||||
// DEBUG: necessary?
|
||||
try {
|
||||
var visibleFields = this.getVisibleFields();
|
||||
this._treebox.columns.count
|
||||
}
|
||||
// If treebox isn't ready, skip refresh
|
||||
catch (e) {
|
||||
|
@ -286,33 +283,6 @@ Zotero.ItemTreeView.prototype.refresh = Zotero.serial(Zotero.Promise.coroutine(f
|
|||
});
|
||||
|
||||
try {
|
||||
for (let i=0; i<visibleFields.length; i++) {
|
||||
let field = visibleFields[i];
|
||||
switch (field) {
|
||||
case 'hasAttachment':
|
||||
// Needed by item.getBestAttachments(), called by getBestAttachmentStateAsync()
|
||||
field = 'url';
|
||||
break;
|
||||
|
||||
case 'numNotes':
|
||||
continue;
|
||||
|
||||
case 'year':
|
||||
field = 'date';
|
||||
break;
|
||||
|
||||
case 'itemType':
|
||||
field = 'itemTypeID';
|
||||
break;
|
||||
}
|
||||
if (cacheFields.indexOf(field) == -1) {
|
||||
cacheFields = cacheFields.concat(field);
|
||||
}
|
||||
}
|
||||
|
||||
yield Zotero.Items.cacheFields(this.collectionTreeRow.ref.libraryID, cacheFields);
|
||||
Zotero.ItemTreeView._haveCachedFields = true;
|
||||
|
||||
Zotero.CollectionTreeCache.clear();
|
||||
|
||||
if (!this.selection.selectEventsSuppressed) {
|
||||
|
@ -382,9 +352,9 @@ Zotero.ItemTreeView.prototype.refresh = Zotero.serial(Zotero.Promise.coroutine(f
|
|||
this._searchParentIDs = newSearchParentIDs;
|
||||
this._cellTextCache = {};
|
||||
|
||||
yield this.rememberOpenState(savedOpenState);
|
||||
yield this.rememberSelection(savedSelection);
|
||||
yield this.expandMatchParents();
|
||||
this.rememberOpenState(savedOpenState);
|
||||
this.rememberSelection(savedSelection);
|
||||
this.expandMatchParents();
|
||||
if (unsuppress) {
|
||||
// This causes a problem with the row count being wrong between views
|
||||
//this._treebox.endUpdateBatch();
|
||||
|
@ -404,12 +374,6 @@ Zotero.ItemTreeView.prototype.refresh = Zotero.serial(Zotero.Promise.coroutine(f
|
|||
}));
|
||||
|
||||
|
||||
/**
|
||||
* Generator used internally for refresh
|
||||
*/
|
||||
Zotero.ItemTreeView._haveCachedFields = false;
|
||||
|
||||
|
||||
/*
|
||||
* Called by Zotero.Notifier on any changes to items in the data layer
|
||||
*/
|
||||
|
@ -502,7 +466,7 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
|
|||
delete this._cellTextCache[row];
|
||||
|
||||
this.selection.clearSelection();
|
||||
yield this.rememberSelection(savedSelection);
|
||||
this.rememberSelection(savedSelection);
|
||||
}
|
||||
else {
|
||||
this._cellTextCache = {};
|
||||
|
@ -569,7 +533,6 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
|
|||
push = true;
|
||||
}
|
||||
else {
|
||||
yield collectionTreeRow.ref.loadChildItems();
|
||||
push = !collectionTreeRow.ref.hasItem(ids[i]);
|
||||
}
|
||||
// Row might already be gone (e.g. if this is a child and
|
||||
|
@ -679,7 +642,6 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
|
|||
&& collectionTreeRow.ref.libraryID == item.libraryID;
|
||||
// Collection containing item
|
||||
if (!add && collectionTreeRow.isCollection()) {
|
||||
yield item.loadCollections();
|
||||
add = item.inCollection(collectionTreeRow.ref.id);
|
||||
}
|
||||
if (add) {
|
||||
|
@ -719,14 +681,7 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
|
|||
}
|
||||
else if(action == 'add')
|
||||
{
|
||||
// New items need their item data and collections loaded
|
||||
// before they're inserted into the tree
|
||||
let items = yield Zotero.Items.getAsync(ids);
|
||||
for (let i=0; i<items.length; i++) {
|
||||
let item = items[i];
|
||||
yield item.loadItemData();
|
||||
yield item.loadCollections();
|
||||
}
|
||||
|
||||
// In some modes, just re-run search
|
||||
if (collectionTreeRow.isSearch() || collectionTreeRow.isTrash() || collectionTreeRow.isUnfiled()) {
|
||||
|
@ -824,7 +779,7 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
|
|||
}
|
||||
|
||||
if (sort) {
|
||||
yield this.sort(typeof sort == 'number' ? sort : false);
|
||||
this.sort(typeof sort == 'number' ? sort : false);
|
||||
}
|
||||
else {
|
||||
this._refreshItemRowMap();
|
||||
|
@ -853,7 +808,7 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
|
|||
yield this.selectItem(ids[0]);
|
||||
}
|
||||
else {
|
||||
yield this.rememberSelection(savedSelection);
|
||||
this.rememberSelection(savedSelection);
|
||||
}
|
||||
}
|
||||
// On removal of a row, select item at previous position
|
||||
|
@ -891,7 +846,7 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
|
|||
}
|
||||
}
|
||||
else {
|
||||
yield this.rememberSelection(savedSelection);
|
||||
this.rememberSelection(savedSelection);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1159,8 +1114,7 @@ Zotero.ItemTreeView.prototype.hasNextSibling = function(row,afterIndex)
|
|||
}
|
||||
}
|
||||
|
||||
Zotero.ItemTreeView.prototype.toggleOpenState = Zotero.Promise.coroutine(function* (row, skipItemMapRefresh)
|
||||
{
|
||||
Zotero.ItemTreeView.prototype.toggleOpenState = function (row, skipItemMapRefresh) {
|
||||
// Shouldn't happen but does if an item is dragged over a closed
|
||||
// container until it opens and then released, since the container
|
||||
// is no longer in the same place when the spring-load closes
|
||||
|
@ -1179,7 +1133,6 @@ Zotero.ItemTreeView.prototype.toggleOpenState = Zotero.Promise.coroutine(functio
|
|||
// Open
|
||||
//
|
||||
var item = this.getRow(row).ref;
|
||||
yield item.loadChildItems();
|
||||
|
||||
//Get children
|
||||
var includeTrashed = this.collectionTreeRow.isTrash();
|
||||
|
@ -1198,7 +1151,7 @@ Zotero.ItemTreeView.prototype.toggleOpenState = Zotero.Promise.coroutine(functio
|
|||
}
|
||||
|
||||
if (newRows) {
|
||||
newRows = yield Zotero.Items.getAsync(newRows);
|
||||
newRows = Zotero.Items.get(newRows);
|
||||
|
||||
for (let i = 0; i < newRows.length; i++) {
|
||||
count++;
|
||||
|
@ -1221,7 +1174,7 @@ Zotero.ItemTreeView.prototype.toggleOpenState = Zotero.Promise.coroutine(functio
|
|||
Zotero.debug('Refreshing hash map');
|
||||
this._refreshItemRowMap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Zotero.ItemTreeView.prototype._closeContainer = function (row, skipItemMapRefresh) {
|
||||
|
@ -1263,8 +1216,7 @@ Zotero.ItemTreeView.prototype.isSorted = function()
|
|||
return true;
|
||||
}
|
||||
|
||||
Zotero.ItemTreeView.prototype.cycleHeader = Zotero.Promise.coroutine(function* (column)
|
||||
{
|
||||
Zotero.ItemTreeView.prototype.cycleHeader = function (column) {
|
||||
for(var i=0, len=this._treebox.columns.count; i<len; i++)
|
||||
{
|
||||
col = this._treebox.columns.getColumnAt(i);
|
||||
|
@ -1291,8 +1243,8 @@ Zotero.ItemTreeView.prototype.cycleHeader = Zotero.Promise.coroutine(function* (
|
|||
if (savedSelection.length == 1) {
|
||||
var pos = this._rowMap[savedSelection[0]] - this._treebox.getFirstVisibleRow();
|
||||
}
|
||||
yield this.sort();
|
||||
yield this.rememberSelection(savedSelection);
|
||||
this.sort();
|
||||
this.rememberSelection(savedSelection);
|
||||
// If single row was selected, try to keep it in the same place
|
||||
if (savedSelection.length == 1) {
|
||||
var newRow = this._rowMap[savedSelection[0]];
|
||||
|
@ -1304,12 +1256,12 @@ Zotero.ItemTreeView.prototype.cycleHeader = Zotero.Promise.coroutine(function* (
|
|||
}
|
||||
this._treebox.invalidate();
|
||||
this.selection.selectEventsSuppressed = false;
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Sort the items by the currently sorted column.
|
||||
*/
|
||||
Zotero.ItemTreeView.prototype.sort = Zotero.Promise.coroutine(function* (itemID) {
|
||||
Zotero.ItemTreeView.prototype.sort = function (itemID) {
|
||||
var t = new Date;
|
||||
|
||||
// If Zotero pane is hidden, mark tree for sorting later in setTree()
|
||||
|
@ -1324,7 +1276,7 @@ Zotero.ItemTreeView.prototype.sort = Zotero.Promise.coroutine(function* (itemID)
|
|||
this.getRow(this._rowMap[itemID]).ref.parentKey) {
|
||||
let parentIndex = this.getParentIndex(this._rowMap[itemID]);
|
||||
this._closeContainer(parentIndex);
|
||||
yield this.toggleOpenState(parentIndex);
|
||||
this.toggleOpenState(parentIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1566,8 +1518,8 @@ Zotero.ItemTreeView.prototype.sort = Zotero.Promise.coroutine(function* (itemID)
|
|||
|
||||
this._refreshItemRowMap();
|
||||
|
||||
yield this.rememberOpenState(openItemIDs);
|
||||
yield this.rememberSelection(savedSelection);
|
||||
this.rememberOpenState(openItemIDs);
|
||||
this.rememberSelection(savedSelection);
|
||||
|
||||
if (unsuppress) {
|
||||
this.selection.selectEventsSuppressed = false;
|
||||
|
@ -1575,7 +1527,7 @@ Zotero.ItemTreeView.prototype.sort = Zotero.Promise.coroutine(function* (itemID)
|
|||
}
|
||||
|
||||
Zotero.debug("Sorted items list in " + (new Date - t) + " ms");
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1648,7 +1600,7 @@ Zotero.ItemTreeView.prototype.selectItem = Zotero.Promise.coroutine(function* (i
|
|||
this._closeContainer(parentRow);
|
||||
|
||||
// Open the parent
|
||||
yield this.toggleOpenState(parentRow);
|
||||
this.toggleOpenState(parentRow);
|
||||
row = this._rowMap[id];
|
||||
}
|
||||
|
||||
|
@ -1669,7 +1621,7 @@ Zotero.ItemTreeView.prototype.selectItem = Zotero.Promise.coroutine(function* (i
|
|||
|
||||
// If |expand|, open row if container
|
||||
if (expand && this.isContainer(row) && !this.isContainerOpen(row)) {
|
||||
yield this.toggleOpenState(row);
|
||||
this.toggleOpenState(row);
|
||||
}
|
||||
this.selection.select(row);
|
||||
|
||||
|
@ -1836,7 +1788,7 @@ Zotero.ItemTreeView.prototype.setFilter = Zotero.Promise.coroutine(function* (ty
|
|||
var oldCount = this.rowCount;
|
||||
yield this.refresh();
|
||||
|
||||
yield this.sort();
|
||||
this.sort();
|
||||
|
||||
//this._treebox.endUpdateBatch();
|
||||
this.selection.selectEventsSuppressed = false;
|
||||
|
@ -1869,8 +1821,7 @@ Zotero.ItemTreeView.prototype.saveSelection = function () {
|
|||
/*
|
||||
* Sets the selection based on saved selection ids
|
||||
*/
|
||||
Zotero.ItemTreeView.prototype.rememberSelection = Zotero.Promise.coroutine(function* (selection)
|
||||
{
|
||||
Zotero.ItemTreeView.prototype.rememberSelection = function (selection) {
|
||||
if (!selection.length) {
|
||||
return;
|
||||
}
|
||||
|
@ -1888,7 +1839,7 @@ Zotero.ItemTreeView.prototype.rememberSelection = Zotero.Promise.coroutine(funct
|
|||
}
|
||||
// Try the parent
|
||||
else {
|
||||
var item = yield Zotero.Items.getAsync(selection[i]);
|
||||
var item = Zotero.Items.get(selection[i]);
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
@ -1900,7 +1851,7 @@ Zotero.ItemTreeView.prototype.rememberSelection = Zotero.Promise.coroutine(funct
|
|||
|
||||
if (this._rowMap[parent] != null) {
|
||||
this._closeContainer(this._rowMap[parent]);
|
||||
yield this.toggleOpenState(this._rowMap[parent]);
|
||||
this.toggleOpenState(this._rowMap[parent]);
|
||||
this.selection.toggleSelect(this._rowMap[selection[i]]);
|
||||
}
|
||||
}
|
||||
|
@ -1909,21 +1860,21 @@ Zotero.ItemTreeView.prototype.rememberSelection = Zotero.Promise.coroutine(funct
|
|||
//this._treebox.endUpdateBatch();
|
||||
this.selection.selectEventsSuppressed = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Zotero.ItemTreeView.prototype.selectSearchMatches = Zotero.Promise.coroutine(function* () {
|
||||
Zotero.ItemTreeView.prototype.selectSearchMatches = function () {
|
||||
if (this._searchMode) {
|
||||
var ids = [];
|
||||
for (var id in this._searchItemIDs) {
|
||||
ids.push(id);
|
||||
}
|
||||
yield this.rememberSelection(ids);
|
||||
this.rememberSelection(ids);
|
||||
}
|
||||
else {
|
||||
this.selection.clearSelection();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Zotero.ItemTreeView.prototype._saveOpenState = function (close) {
|
||||
|
@ -1953,7 +1904,7 @@ Zotero.ItemTreeView.prototype._saveOpenState = function (close) {
|
|||
}
|
||||
|
||||
|
||||
Zotero.ItemTreeView.prototype.rememberOpenState = Zotero.Promise.coroutine(function* (itemIDs) {
|
||||
Zotero.ItemTreeView.prototype.rememberOpenState = function (itemIDs) {
|
||||
var rowsToOpen = [];
|
||||
for each(var id in itemIDs) {
|
||||
var row = this._rowMap[id];
|
||||
|
@ -1973,17 +1924,17 @@ Zotero.ItemTreeView.prototype.rememberOpenState = Zotero.Promise.coroutine(funct
|
|||
}
|
||||
// Reopen from bottom up
|
||||
for (var i=rowsToOpen.length-1; i>=0; i--) {
|
||||
yield this.toggleOpenState(rowsToOpen[i], true);
|
||||
this.toggleOpenState(rowsToOpen[i], true);
|
||||
}
|
||||
this._refreshItemRowMap();
|
||||
if (unsuppress) {
|
||||
//this._treebox.endUpdateBatch();
|
||||
this.selection.selectEventsSuppressed = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Zotero.ItemTreeView.prototype.expandMatchParents = Zotero.Promise.coroutine(function* () {
|
||||
Zotero.ItemTreeView.prototype.expandMatchParents = function () {
|
||||
// Expand parents of child matches
|
||||
if (!this._searchMode) {
|
||||
return;
|
||||
|
@ -2001,7 +1952,7 @@ Zotero.ItemTreeView.prototype.expandMatchParents = Zotero.Promise.coroutine(func
|
|||
for (var i=0; i<this.rowCount; i++) {
|
||||
var id = this.getRow(i).ref.id;
|
||||
if (hash[id] && this.isContainer(i) && !this.isContainerOpen(i)) {
|
||||
yield this.toggleOpenState(i, true);
|
||||
this.toggleOpenState(i, true);
|
||||
}
|
||||
}
|
||||
this._refreshItemRowMap();
|
||||
|
@ -2009,7 +1960,7 @@ Zotero.ItemTreeView.prototype.expandMatchParents = Zotero.Promise.coroutine(func
|
|||
//this._treebox.endUpdateBatch();
|
||||
this.selection.selectEventsSuppressed = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Zotero.ItemTreeView.prototype.saveFirstRow = function() {
|
||||
|
@ -2028,18 +1979,18 @@ Zotero.ItemTreeView.prototype.rememberFirstRow = function(firstRow) {
|
|||
}
|
||||
|
||||
|
||||
Zotero.ItemTreeView.prototype.expandAllRows = Zotero.Promise.coroutine(function* () {
|
||||
Zotero.ItemTreeView.prototype.expandAllRows = function () {
|
||||
var unsuppress = this.selection.selectEventsSuppressed = true;
|
||||
//this._treebox.beginUpdateBatch();
|
||||
for (var i=0; i<this.rowCount; i++) {
|
||||
if (this.isContainer(i) && !this.isContainerOpen(i)) {
|
||||
yield this.toggleOpenState(i, true);
|
||||
this.toggleOpenState(i, true);
|
||||
}
|
||||
}
|
||||
this._refreshItemRowMap();
|
||||
//this._treebox.endUpdateBatch();
|
||||
this.selection.selectEventsSuppressed = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Zotero.ItemTreeView.prototype.collapseAllRows = Zotero.Promise.coroutine(function* () {
|
||||
|
@ -2056,7 +2007,7 @@ Zotero.ItemTreeView.prototype.collapseAllRows = Zotero.Promise.coroutine(functio
|
|||
});
|
||||
|
||||
|
||||
Zotero.ItemTreeView.prototype.expandSelectedRows = Zotero.Promise.coroutine(function* () {
|
||||
Zotero.ItemTreeView.prototype.expandSelectedRows = function () {
|
||||
var start = {}, end = {};
|
||||
this.selection.selectEventsSuppressed = true;
|
||||
//this._treebox.beginUpdateBatch();
|
||||
|
@ -2064,17 +2015,17 @@ Zotero.ItemTreeView.prototype.expandSelectedRows = Zotero.Promise.coroutine(func
|
|||
this.selection.getRangeAt(i, start, end);
|
||||
for (var j = start.value; j <= end.value; j++) {
|
||||
if (this.isContainer(j) && !this.isContainerOpen(j)) {
|
||||
yield this.toggleOpenState(j, true);
|
||||
this.toggleOpenState(j, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
this._refreshItemRowMap();
|
||||
//this._treebox.endUpdateBatch();
|
||||
this.selection.selectEventsSuppressed = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Zotero.ItemTreeView.prototype.collapseSelectedRows = Zotero.Promise.coroutine(function* () {
|
||||
Zotero.ItemTreeView.prototype.collapseSelectedRows = function () {
|
||||
var start = {}, end = {};
|
||||
this.selection.selectEventsSuppressed = true;
|
||||
//this._treebox.beginUpdateBatch();
|
||||
|
@ -2089,7 +2040,7 @@ Zotero.ItemTreeView.prototype.collapseSelectedRows = Zotero.Promise.coroutine(fu
|
|||
this._refreshItemRowMap();
|
||||
//this._treebox.endUpdateBatch();
|
||||
this.selection.selectEventsSuppressed = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Zotero.ItemTreeView.prototype.getVisibleFields = function() {
|
||||
|
@ -2392,17 +2343,16 @@ Zotero.ItemTreeCommandController.prototype.isCommandEnabled = function(cmd)
|
|||
return (cmd == 'cmd_selectAll');
|
||||
}
|
||||
|
||||
Zotero.ItemTreeCommandController.prototype.doCommand = Zotero.Promise.coroutine(function* (cmd)
|
||||
{
|
||||
Zotero.ItemTreeCommandController.prototype.doCommand = function (cmd) {
|
||||
if (cmd == 'cmd_selectAll') {
|
||||
if (this.tree.view.wrappedJSObject.collectionTreeRow.isSearchMode()) {
|
||||
yield this.tree.view.wrappedJSObject.selectSearchMatches();
|
||||
this.tree.view.wrappedJSObject.selectSearchMatches();
|
||||
}
|
||||
else {
|
||||
this.tree.view.selection.selectAll();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Zotero.ItemTreeCommandController.prototype.onEvent = function(evt)
|
||||
{
|
||||
|
@ -2474,10 +2424,6 @@ Zotero.ItemTreeView.prototype.onDragStart = function (event) {
|
|||
}
|
||||
}
|
||||
|
||||
// TEMP
|
||||
Zotero.debug("TEMP: Skipping Quick Copy");
|
||||
return;
|
||||
|
||||
// Get Quick Copy format for current URL
|
||||
var url = this._ownerDocument.defaultView.content && this._ownerDocument.defaultView.content.location ?
|
||||
this._ownerDocument.defaultView.content.location.href : null;
|
||||
|
@ -2937,7 +2883,6 @@ Zotero.ItemTreeView.prototype.drop = Zotero.Promise.coroutine(function* (row, or
|
|||
for (let i=0; i<items.length; i++) {
|
||||
let item = items[i];
|
||||
var source = item.isRegularItem() ? false : item.parentItemID;
|
||||
yield item.loadCollections();
|
||||
// Top-level item
|
||||
if (source) {
|
||||
item.parentID = false;
|
||||
|
|
|
@ -382,6 +382,7 @@ Zotero.Search.prototype.addCondition = function (condition, operator, value, req
|
|||
this._sqlParams = false;
|
||||
this._markFieldChange('conditions', this._conditions);
|
||||
this._changed.conditions = true;
|
||||
|
||||
return searchConditionID;
|
||||
}
|
||||
|
||||
|
@ -499,14 +500,11 @@ Zotero.Search.prototype.hasPostSearchFilter = function() {
|
|||
Zotero.Search.prototype.search = Zotero.Promise.coroutine(function* (asTempTable) {
|
||||
var tmpTable;
|
||||
|
||||
if (this._identified) {
|
||||
yield this.loadConditions();
|
||||
}
|
||||
// Mark conditions as loaded
|
||||
else {
|
||||
// TODO: Necessary?
|
||||
if (!this._identified) {
|
||||
this._requireData('conditions');
|
||||
}
|
||||
|
||||
try {
|
||||
if (!this._sql){
|
||||
yield this._buildQuery();
|
||||
|
@ -554,11 +552,6 @@ Zotero.Search.prototype.search = Zotero.Promise.coroutine(function* (asTempTable
|
|||
|
||||
// Run a subsearch to define the superset of possible results
|
||||
if (this._scope) {
|
||||
if (this._scope._identified) {
|
||||
yield this._scope.loadPrimaryData();
|
||||
yield this._scope.loadConditions();
|
||||
}
|
||||
|
||||
// If subsearch has post-search filter, run and insert ids into temp table
|
||||
if (this._scope.hasPostSearchFilter()) {
|
||||
var ids = yield this._scope.search();
|
||||
|
@ -840,13 +833,13 @@ Zotero.Search.prototype.fromJSON = function (json) {
|
|||
}
|
||||
}
|
||||
|
||||
Zotero.Collection.prototype.toResponseJSON = Zotero.Promise.coroutine(function* (options = {}) {
|
||||
var json = yield this.constructor._super.prototype.toResponseJSON.apply(this, options);
|
||||
Zotero.Collection.prototype.toResponseJSON = function (options = {}) {
|
||||
var json = this.constructor._super.prototype.toResponseJSON.apply(this, options);
|
||||
return json;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Zotero.Search.prototype.toJSON = Zotero.Promise.coroutine(function* (options = {}) {
|
||||
Zotero.Search.prototype.toJSON = function (options = {}) {
|
||||
var env = this._preToJSON(options);
|
||||
var mode = env.mode;
|
||||
|
||||
|
@ -854,11 +847,10 @@ Zotero.Search.prototype.toJSON = Zotero.Promise.coroutine(function* (options = {
|
|||
obj.key = this.key;
|
||||
obj.version = this.version;
|
||||
obj.name = this.name;
|
||||
yield this.loadConditions();
|
||||
obj.conditions = this.getConditions();
|
||||
|
||||
return this._postToJSON(env);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
@ -866,7 +858,6 @@ Zotero.Search.prototype.toJSON = Zotero.Promise.coroutine(function* (options = {
|
|||
*/
|
||||
Zotero.Search.prototype.getSQL = Zotero.Promise.coroutine(function* () {
|
||||
if (!this._sql) {
|
||||
yield this.loadConditions();
|
||||
yield this._buildQuery();
|
||||
}
|
||||
return this._sql;
|
||||
|
@ -875,68 +866,12 @@ Zotero.Search.prototype.getSQL = Zotero.Promise.coroutine(function* () {
|
|||
|
||||
Zotero.Search.prototype.getSQLParams = Zotero.Promise.coroutine(function* () {
|
||||
if (!this._sql) {
|
||||
yield this.loadConditions();
|
||||
yield this._buildQuery();
|
||||
}
|
||||
return this._sqlParams;
|
||||
});
|
||||
|
||||
|
||||
Zotero.Search.prototype.loadConditions = Zotero.Promise.coroutine(function* (reload) {
|
||||
if (this._loaded.conditions && !reload) return;
|
||||
|
||||
Zotero.debug("Loading conditions for search " + this.libraryKey);
|
||||
|
||||
if (!this.id) {
|
||||
throw new Error('ID not set for object before attempting to load conditions');
|
||||
}
|
||||
|
||||
var sql = "SELECT * FROM savedSearchConditions "
|
||||
+ "WHERE savedSearchID=? ORDER BY searchConditionID";
|
||||
var conditions = yield Zotero.DB.queryAsync(sql, this.id);
|
||||
|
||||
if (conditions.length) {
|
||||
this._maxSearchConditionID = conditions[conditions.length - 1].searchConditionID;
|
||||
}
|
||||
|
||||
this._conditions = {};
|
||||
|
||||
// Reindex conditions, in case they're not contiguous in the DB
|
||||
for (let i=0; i<conditions.length; i++) {
|
||||
let condition = conditions[i];
|
||||
|
||||
// Parse "condition[/mode]"
|
||||
let [conditionName, mode] = Zotero.SearchConditions.parseCondition(condition.condition);
|
||||
|
||||
let cond = Zotero.SearchConditions.get(conditionName);
|
||||
if (!cond || cond.noLoad) {
|
||||
Zotero.debug("Invalid saved search condition '" + conditionName + "' -- skipping", 2);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Convert itemTypeID to itemType
|
||||
//
|
||||
// TEMP: This can be removed at some point
|
||||
if (conditionName == 'itemTypeID') {
|
||||
conditionName = 'itemType';
|
||||
condition.value = Zotero.ItemTypes.getName(condition.value);
|
||||
}
|
||||
|
||||
this._conditions[i] = {
|
||||
id: i,
|
||||
condition: conditionName,
|
||||
mode: mode,
|
||||
operator: condition.operator,
|
||||
value: condition.value,
|
||||
required: !!condition.required
|
||||
};
|
||||
}
|
||||
|
||||
this._loaded.conditions = true;
|
||||
this._clearChanged('conditions');
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* Batch insert
|
||||
*/
|
||||
|
@ -1687,26 +1622,18 @@ Zotero.Searches = function() {
|
|||
* @param {Integer} [libraryID]
|
||||
*/
|
||||
this.getAll = Zotero.Promise.coroutine(function* (libraryID) {
|
||||
var sql = "SELECT savedSearchID AS id, savedSearchName AS name FROM savedSearches ";
|
||||
if (libraryID) {
|
||||
sql += "WHERE libraryID=? ";
|
||||
var params = libraryID;
|
||||
var sql = "SELECT savedSearchID FROM savedSearches WHERE libraryID=?";
|
||||
var ids = yield Zotero.DB.columnQueryAsync(sql, libraryID);
|
||||
if (!ids.length) {
|
||||
return []
|
||||
}
|
||||
var rows = yield Zotero.DB.queryAsync(sql, params);
|
||||
|
||||
var searches = this.get(ids);
|
||||
// Do proper collation sort
|
||||
var collation = Zotero.getLocaleCollation();
|
||||
rows.sort(function (a, b) {
|
||||
searches.sort(function (a, b) {
|
||||
return collation.compareString(1, a.name, b.name);
|
||||
});
|
||||
|
||||
var searches = [];
|
||||
for (var i=0; i<rows.length; i++) {
|
||||
let search = new Zotero.Search;
|
||||
search.id = rows[i].id;
|
||||
yield search.loadPrimaryData();
|
||||
searches.push(search);
|
||||
}
|
||||
return searches;
|
||||
});
|
||||
|
||||
|
@ -1719,6 +1646,95 @@ Zotero.Searches = function() {
|
|||
+ "FROM savedSearches O WHERE 1";
|
||||
}
|
||||
|
||||
|
||||
this._loadConditions = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
|
||||
var sql = "SELECT savedSearchID, searchConditionID, condition, operator, value, required "
|
||||
+ "FROM savedSearches LEFT JOIN savedSearchConditions USING (savedSearchID) "
|
||||
+ "WHERE libraryID=?" + idSQL
|
||||
+ "ORDER BY savedSearchID, searchConditionID";
|
||||
var params = [libraryID];
|
||||
var lastID = null;
|
||||
var rows = [];
|
||||
var setRows = function (searchID, rows) {
|
||||
var search = this._objectCache[searchID];
|
||||
if (!search) {
|
||||
throw new Error("Search " + searchID + " not found");
|
||||
}
|
||||
|
||||
search._conditions = {};
|
||||
|
||||
if (rows.length) {
|
||||
search._maxSearchConditionID = rows[rows.length - 1].searchConditionID;
|
||||
}
|
||||
|
||||
// Reindex conditions, in case they're not contiguous in the DB
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let condition = rows[i];
|
||||
|
||||
// Parse "condition[/mode]"
|
||||
let [conditionName, mode] = Zotero.SearchConditions.parseCondition(condition.condition);
|
||||
|
||||
let cond = Zotero.SearchConditions.get(conditionName);
|
||||
if (!cond || cond.noLoad) {
|
||||
Zotero.debug("Invalid saved search condition '" + conditionName + "' -- skipping", 2);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Convert itemTypeID to itemType
|
||||
//
|
||||
// TEMP: This can be removed at some point
|
||||
if (conditionName == 'itemTypeID') {
|
||||
conditionName = 'itemType';
|
||||
condition.value = Zotero.ItemTypes.getName(condition.value);
|
||||
}
|
||||
|
||||
search._conditions[i] = {
|
||||
id: i,
|
||||
condition: conditionName,
|
||||
mode: mode,
|
||||
operator: condition.operator,
|
||||
value: condition.value,
|
||||
required: !!condition.required
|
||||
};
|
||||
}
|
||||
search._loaded.conditions = true;
|
||||
search._clearChanged('conditions');
|
||||
}.bind(this);
|
||||
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
let searchID = row.getResultByIndex(0);
|
||||
|
||||
if (lastID && searchID != lastID) {
|
||||
setRows(lastID, rows);
|
||||
rows = [];
|
||||
}
|
||||
|
||||
lastID = searchID;
|
||||
let searchConditionID = row.getResultByIndex(1);
|
||||
// No conditions
|
||||
if (searchConditionID === null) {
|
||||
return;
|
||||
}
|
||||
rows.push({
|
||||
searchConditionID,
|
||||
condition: row.getResultByIndex(2),
|
||||
operator: row.getResultByIndex(3),
|
||||
value: row.getResultByIndex(4),
|
||||
required: row.getResultByIndex(5)
|
||||
});
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
if (lastID) {
|
||||
setRows(lastID, rows);
|
||||
}
|
||||
});
|
||||
|
||||
Zotero.DataObjects.call(this);
|
||||
|
||||
return this;
|
||||
|
|
|
@ -65,9 +65,9 @@ Zotero.Sync.Storage = new function () {
|
|||
return false;
|
||||
}
|
||||
|
||||
var syncModTime = Zotero.Sync.Storage.getSyncedModificationTime(itemID);
|
||||
var syncModTime = item.attachmentSyncedModificationTime;
|
||||
if (fileModTime != syncModTime) {
|
||||
var syncHash = Zotero.Sync.Storage.getSyncedHash(itemID);
|
||||
var syncHash = item.attachmentSyncedHash;
|
||||
if (syncHash) {
|
||||
var fileHash = item.attachmentHash;
|
||||
if (fileHash && fileHash == syncHash) {
|
||||
|
|
|
@ -273,7 +273,7 @@ Zotero.Sync.Storage.Engine.prototype.stop = function () {
|
|||
}
|
||||
|
||||
Zotero.Sync.Storage.Engine.prototype.queueItem = Zotero.Promise.coroutine(function* (item) {
|
||||
switch (yield this.local.getSyncState(item.id)) {
|
||||
switch (item.attachmentSyncState) {
|
||||
case Zotero.Sync.Storage.Local.SYNC_STATE_TO_DOWNLOAD:
|
||||
case Zotero.Sync.Storage.Local.SYNC_STATE_FORCE_DOWNLOAD:
|
||||
var type = 'download';
|
||||
|
@ -295,7 +295,7 @@ Zotero.Sync.Storage.Engine.prototype.queueItem = Zotero.Promise.coroutine(functi
|
|||
return;
|
||||
|
||||
default:
|
||||
throw new Error("Invalid sync state " + (yield this.local.getSyncState(item.id)));
|
||||
throw new Error("Invalid sync state " + item.attachmentSyncState);
|
||||
}
|
||||
|
||||
var request = new Zotero.Sync.Storage.Request({
|
||||
|
|
|
@ -239,7 +239,8 @@ Zotero.Sync.Storage.Local = {
|
|||
// TODO: Catch error?
|
||||
let state = yield this._checkForUpdatedFile(item, attachmentData[item.id]);
|
||||
if (state !== false) {
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, state);
|
||||
item.attachmentSyncState = state;
|
||||
yield item.saveTx({ skipAll: true });
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
@ -282,7 +283,7 @@ Zotero.Sync.Storage.Local = {
|
|||
// If file is already marked for upload, skip check. Even if the file was changed
|
||||
// both locally and remotely, conflicts are checked at upload time, so we don't need
|
||||
// to worry about it here.
|
||||
if ((yield this.getSyncState(item.id)) == this.SYNC_STATE_TO_UPLOAD) {
|
||||
if (item.attachmentSyncState == this.SYNC_STATE_TO_UPLOAD) {
|
||||
Zotero.debug("File is already marked for upload");
|
||||
return false;
|
||||
}
|
||||
|
@ -298,7 +299,7 @@ Zotero.Sync.Storage.Local = {
|
|||
Zotero.debug(`Remote mod time for item ${lk} is ${remoteModTime}`);
|
||||
|
||||
// Ignore attachments whose stored mod times haven't changed
|
||||
mtime = mtime !== false ? mtime : (yield this.getSyncedModificationTime(item.id));
|
||||
mtime = mtime !== false ? mtime : item.attachmentSyncedModificationTime;
|
||||
if (mtime == remoteModTime) {
|
||||
Zotero.debug(`Synced mod time (${mtime}) hasn't changed for item ${lk}`);
|
||||
return false;
|
||||
|
@ -474,125 +475,23 @@ Zotero.Sync.Storage.Local = {
|
|||
},
|
||||
|
||||
|
||||
/**
|
||||
* @param {Integer} itemID
|
||||
*/
|
||||
getSyncState: function (itemID) {
|
||||
var sql = "SELECT syncState FROM itemAttachments WHERE itemID=?";
|
||||
return Zotero.DB.valueQueryAsync(sql, itemID);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @param {Integer} itemID
|
||||
* @param {Integer|String} syncState - Zotero.Sync.Storage.Local.SYNC_STATE_* or last part
|
||||
* as string (e.g., "TO_UPLOAD")
|
||||
*/
|
||||
setSyncState: Zotero.Promise.method(function (itemID, syncState) {
|
||||
if (typeof syncState == 'string') {
|
||||
syncState = this["SYNC_STATE_" + syncState.toUpperCase()];
|
||||
resetModeSyncStates: Zotero.Promise.coroutine(function* () {
|
||||
var sql = "SELECT itemID FROM items JOIN itemAttachments USING (itemID) "
|
||||
+ "WHERE libraryID=? AND itemTypeID=? AND linkMode IN (?, ?)";
|
||||
var params = [
|
||||
Zotero.Libraries.userLibraryID,
|
||||
Zotero.ItemTypes.getID('attachment'),
|
||||
Zotero.Attachments.LINK_MODE_IMPORTED_FILE,
|
||||
Zotero.Attachments.LINK_MODE_IMPORTED_URL,
|
||||
];
|
||||
var itemIDs = yield Zotero.DB.columnQueryAsync(sql, params);
|
||||
for (let itemID of items) {
|
||||
let item = Zotero.Items.get(itemID);
|
||||
item._attachmentSyncState = this.SYNC_STATE_TO_UPLOAD;
|
||||
}
|
||||
|
||||
switch (syncState) {
|
||||
case this.SYNC_STATE_TO_UPLOAD:
|
||||
case this.SYNC_STATE_TO_DOWNLOAD:
|
||||
case this.SYNC_STATE_IN_SYNC:
|
||||
case this.SYNC_STATE_FORCE_UPLOAD:
|
||||
case this.SYNC_STATE_FORCE_DOWNLOAD:
|
||||
case this.SYNC_STATE_IN_CONFLICT:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("Invalid sync state " + syncState);
|
||||
}
|
||||
|
||||
var sql = "UPDATE itemAttachments SET syncState=? WHERE itemID=?";
|
||||
return Zotero.DB.valueQueryAsync(sql, [syncState, itemID]);
|
||||
}),
|
||||
|
||||
|
||||
resetModeSyncStates: Zotero.Promise.coroutine(function* (mode) {
|
||||
var sql = "UPDATE itemAttachments SET syncState=? "
|
||||
+ "WHERE itemID IN (SELECT itemID FROM items WHERE libraryID=?)";
|
||||
var params = [this.SYNC_STATE_TO_UPLOAD, Zotero.Libraries.userLibraryID];
|
||||
yield Zotero.DB.queryAsync(sql, params);
|
||||
}),
|
||||
|
||||
|
||||
/**
|
||||
* @param {Integer} itemID
|
||||
* @return {Integer|NULL} Mod time as timestamp in ms,
|
||||
* or NULL if never synced
|
||||
*/
|
||||
getSyncedModificationTime: Zotero.Promise.coroutine(function* (itemID) {
|
||||
var sql = "SELECT storageModTime FROM itemAttachments WHERE itemID=?";
|
||||
var mtime = yield Zotero.DB.valueQueryAsync(sql, itemID);
|
||||
if (mtime === false) {
|
||||
throw new Error("Item " + itemID + " not found")
|
||||
}
|
||||
return mtime;
|
||||
}),
|
||||
|
||||
|
||||
/**
|
||||
* @param {Integer} itemID
|
||||
* @param {Integer} mtime - File modification time as timestamp in ms
|
||||
* @param {Boolean} [updateItem=FALSE] - Mark attachment item as unsynced
|
||||
*/
|
||||
setSyncedModificationTime: Zotero.Promise.coroutine(function* (itemID, mtime, updateItem) {
|
||||
mtime = parseInt(mtime)
|
||||
if (isNaN(mtime) || mtime < 0) {
|
||||
Components.utils.reportError("Invalid file mod time " + mtime
|
||||
+ " in Zotero.Storage.setSyncedModificationTime()");
|
||||
mtime = 0;
|
||||
}
|
||||
|
||||
Zotero.DB.requireTransaction();
|
||||
|
||||
var sql = "UPDATE itemAttachments SET storageModTime=? WHERE itemID=?";
|
||||
yield Zotero.DB.queryAsync(sql, [mtime, itemID]);
|
||||
|
||||
if (updateItem) {
|
||||
let item = yield Zotero.Items.getAsync(itemID);
|
||||
yield item.updateSynced(false);
|
||||
}
|
||||
}),
|
||||
|
||||
|
||||
/**
|
||||
* @param {Integer} itemID
|
||||
* @return {Promise<String|null|false>} - File hash, null if never synced, if false if
|
||||
* file doesn't exist
|
||||
*/
|
||||
getSyncedHash: Zotero.Promise.coroutine(function* (itemID) {
|
||||
var sql = "SELECT storageHash FROM itemAttachments WHERE itemID=?";
|
||||
var hash = yield Zotero.DB.valueQueryAsync(sql, itemID);
|
||||
if (hash === false) {
|
||||
throw new Error("Item " + itemID + " not found");
|
||||
}
|
||||
return hash;
|
||||
}),
|
||||
|
||||
|
||||
/**
|
||||
* @param {Integer} itemID
|
||||
* @param {String} hash File hash
|
||||
* @param {Boolean} [updateItem=FALSE] - Mark attachment item as unsynced
|
||||
*/
|
||||
setSyncedHash: Zotero.Promise.coroutine(function* (itemID, hash, updateItem) {
|
||||
if (hash !== null && hash.length != 32) {
|
||||
throw new Error("Invalid file hash '" + hash + "'");
|
||||
}
|
||||
|
||||
Zotero.DB.requireTransaction();
|
||||
|
||||
var sql = "UPDATE itemAttachments SET storageHash=? WHERE itemID=?";
|
||||
yield Zotero.DB.queryAsync(sql, [hash, itemID]);
|
||||
|
||||
if (updateItem) {
|
||||
let item = yield Zotero.Items.getAsync(itemID);
|
||||
yield item.updateSynced(false);
|
||||
}
|
||||
sql = "UPDATE itemAttachments SET syncState=? WHERE itemID IN (" + sql + ")";
|
||||
yield Zotero.DB.queryAsync(sql, [this.SYNC_STATE_TO_UPLOAD].concat(params));
|
||||
}),
|
||||
|
||||
|
||||
|
@ -678,11 +577,10 @@ Zotero.Sync.Storage.Local = {
|
|||
// Set the file mtime to the time from the server
|
||||
yield OS.File.setDates(path, null, new Date(parseInt(mtime)));
|
||||
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield this.setSyncedHash(item.id, md5);
|
||||
yield this.setSyncState(item.id, this.SYNC_STATE_IN_SYNC);
|
||||
yield this.setSyncedModificationTime(item.id, mtime);
|
||||
}.bind(this));
|
||||
item.attachmentSyncState = this.SYNC_STATE_IN_SYNC;
|
||||
item.attachmentSyncedModificationTime = mtime;
|
||||
item.attachmentSyncedHash = md5;
|
||||
yield item.saveTx();
|
||||
|
||||
return new Zotero.Sync.Storage.Result({
|
||||
localChanges: true
|
||||
|
@ -1040,7 +938,7 @@ Zotero.Sync.Storage.Local = {
|
|||
for (let localItem of localItems) {
|
||||
// Use the mtime for the dateModified field, since that's all that's shown in the
|
||||
// CR window at the moment
|
||||
let localItemJSON = yield localItem.toJSON();
|
||||
let localItemJSON = localItem.toJSON();
|
||||
localItemJSON.dateModified = Zotero.Date.dateToISO(
|
||||
new Date(yield localItem.attachmentModificationTime)
|
||||
);
|
||||
|
@ -1101,8 +999,9 @@ Zotero.Sync.Storage.Local = {
|
|||
else {
|
||||
syncState = this.SYNC_STATE_FORCE_DOWNLOAD;
|
||||
}
|
||||
let itemID = Zotero.Items.getIDFromLibraryAndKey(libraryID, conflict.left.key);
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(itemID, syncState);
|
||||
let item = Zotero.Items.getByLibraryAndKey(libraryID, conflict.left.key);
|
||||
item.attachmentSyncState = syncState;
|
||||
yield item.save({ skipAll: true });
|
||||
}
|
||||
}.bind(this));
|
||||
return true;
|
||||
|
|
|
@ -288,15 +288,14 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
|
|||
Zotero.debug("File mod time matches remote file -- skipping download of "
|
||||
+ item.libraryKey);
|
||||
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
var syncState = Zotero.Sync.Storage.Local.getSyncState(item.id);
|
||||
// DEBUG: Necessary to update item?
|
||||
var updateItem = syncState != 1;
|
||||
yield Zotero.Sync.Storage.Local.setSyncedModificationTime(
|
||||
item.id, metadata.mtime, updateItem
|
||||
);
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "in_sync");
|
||||
});
|
||||
var updateItem = item.attachmentSyncState != 1
|
||||
item.attachmentSyncedModificationTime = metadata.mtime;
|
||||
item.attachmentSyncState = "in_sync";
|
||||
yield item.saveTx({ skipAll: true });
|
||||
// DEBUG: Necessary?
|
||||
if (updateItem) {
|
||||
yield item.updateSynced(false);
|
||||
}
|
||||
|
||||
return new Zotero.Sync.Storage.Result({
|
||||
localChanges: true, // ?
|
||||
|
@ -416,7 +415,7 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
|
|||
}
|
||||
|
||||
// Check if file already exists on WebDAV server
|
||||
if ((yield Zotero.Sync.Storage.Local.getSyncState(item.id))
|
||||
if (item.attachmentSyncState
|
||||
!= Zotero.Sync.Storage.Local.SYNC_STATE_FORCE_UPLOAD) {
|
||||
if (metadata.mtime) {
|
||||
// Local file time
|
||||
|
@ -438,15 +437,14 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
|
|||
|
||||
// If WebDAV server already has file, update synced properties
|
||||
if (!changed) {
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.Sync.Storage.Local.setSyncedModificationTime(
|
||||
item.id, fmtime, true
|
||||
);
|
||||
if (hash) {
|
||||
yield Zotero.Sync.Storage.Local.setSyncedHash(item.id, hash);
|
||||
}
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "in_sync");
|
||||
});
|
||||
item.attachmentSyncedModificationTime = fmtime;
|
||||
if (hash) {
|
||||
item.attachmentSyncedHash = hash;
|
||||
}
|
||||
item.attachmentSyncState = "in_sync";
|
||||
yield item.saveTx({ skipAll: true });
|
||||
// skipAll doesn't mark as unsynced, so do that separately
|
||||
yield item.updateSynced(false);
|
||||
return new Zotero.Sync.Storage.Result;
|
||||
}
|
||||
}
|
||||
|
@ -460,9 +458,9 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
|
|||
// API would ever be updated with the correct values, so we can't just wait for
|
||||
// the API to change.) If a conflict is found, we flag the item as in conflict
|
||||
// and require another file sync, which will trigger conflict resolution.
|
||||
let smtime = yield Zotero.Sync.Storage.Local.getSyncedModificationTime(item.id);
|
||||
let smtime = item.attachmentSyncedModificationTime;
|
||||
if (smtime != mtime) {
|
||||
let shash = yield Zotero.Sync.Storage.Local.getSyncedHash(item.id);
|
||||
let shash = item.attachmentSyncedHash;
|
||||
if (shash && metadata.md5 && shash == metadata.md5) {
|
||||
Zotero.debug("Last synced mod time for item " + item.libraryKey
|
||||
+ " doesn't match time on storage server but hash does -- ignoring");
|
||||
|
@ -472,12 +470,13 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
|
|||
Zotero.logError("Conflict -- last synced file mod time for item "
|
||||
+ item.libraryKey + " does not match time on storage server"
|
||||
+ " (" + smtime + " != " + mtime + ")");
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
// Conflict resolution uses the synced mtime as the remote value, so set
|
||||
// that to the WebDAV value, since that's the one in conflict.
|
||||
yield Zotero.Sync.Storage.Local.setSyncedModificationTime(item.id, mtime);
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "in_conflict");
|
||||
});
|
||||
|
||||
// Conflict resolution uses the synced mtime as the remote value, so set
|
||||
// that to the WebDAV value, since that's the one in conflict.
|
||||
item.attachmentSyncedModificationTime = mtime;
|
||||
item.attachmentSyncState = "in_conflict";
|
||||
yield item.saveTx({ skipAll: true });
|
||||
|
||||
return new Zotero.Sync.Storage.Result({
|
||||
fileSyncRequired: true
|
||||
});
|
||||
|
@ -1191,7 +1190,10 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
|
|||
throw new Error(Zotero.Sync.Storage.Mode.WebDAV.defaultError);
|
||||
}
|
||||
|
||||
return { mtime, md5 };
|
||||
return {
|
||||
mtime: parseInt(mtime),
|
||||
md5
|
||||
};
|
||||
}),
|
||||
|
||||
|
||||
|
@ -1243,11 +1245,12 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
|
|||
// Update .prop file on WebDAV server
|
||||
yield this._setStorageFileMetadata(item);
|
||||
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "in_sync");
|
||||
yield Zotero.Sync.Storage.Local.setSyncedModificationTime(item.id, params.mtime, true);
|
||||
yield Zotero.Sync.Storage.Local.setSyncedHash(item.id, params.md5);
|
||||
});
|
||||
item.attachmentSyncedModificationTime = params.mtime;
|
||||
item.attachmentSyncedHash = params.md5;
|
||||
item.attachmentSyncState = "in_sync";
|
||||
yield item.saveTx({ skipAll: true });
|
||||
// skipAll doesn't mark as unsynced, so do that separately
|
||||
yield item.updateSynced(false);
|
||||
|
||||
try {
|
||||
yield OS.File.remove(
|
||||
|
|
|
@ -131,15 +131,13 @@ Zotero.Sync.Storage.Mode.ZFS.prototype = {
|
|||
}
|
||||
|
||||
// Update local metadata and stop request, skipping file download
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
if (updateHash) {
|
||||
yield Zotero.Sync.Storage.Local.setSyncedHash(item.id, requestData.md5);
|
||||
}
|
||||
yield Zotero.Sync.Storage.Local.setSyncedModificationTime(
|
||||
item.id, requestData.mtime
|
||||
);
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "in_sync");
|
||||
});
|
||||
item.attachmentSyncedModificationTime = requestData.mtime;
|
||||
if (updateHash) {
|
||||
item.attachmentSyncedHash = requestData.md5;
|
||||
}
|
||||
item.attachmentSyncState = "in_sync";
|
||||
yield item.saveTx({ skipAll: true });
|
||||
|
||||
return false;
|
||||
}),
|
||||
onProgress: function (a, b, c) {
|
||||
|
@ -261,7 +259,7 @@ Zotero.Sync.Storage.Mode.ZFS.prototype = {
|
|||
|
||||
var sql = "SELECT value FROM settings WHERE setting=? AND key=?";
|
||||
var values = yield Zotero.DB.columnQueryAsync(sql, ['storage', 'zfsPurge']);
|
||||
if (!values) {
|
||||
if (!values.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -353,7 +351,7 @@ Zotero.Sync.Storage.Mode.ZFS.prototype = {
|
|||
var headers = {
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
};
|
||||
var storedHash = yield Zotero.Sync.Storage.Local.getSyncedHash(item.id);
|
||||
var storedHash = item.attachmentSyncedHash;
|
||||
//var storedModTime = yield Zotero.Sync.Storage.getSyncedModificationTime(item.id);
|
||||
if (storedHash) {
|
||||
headers["If-Match"] = storedHash;
|
||||
|
@ -538,17 +536,17 @@ Zotero.Sync.Storage.Mode.ZFS.prototype = {
|
|||
Zotero.debug(fileHash);
|
||||
|
||||
if (json.data.md5 == fileHash) {
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.Sync.Storage.Local.setSyncedModificationTime(
|
||||
item.id, fileModTime
|
||||
);
|
||||
yield Zotero.Sync.Storage.Local.setSyncedHash(item.id, fileHash);
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "in_sync");
|
||||
});
|
||||
item.attachmentSyncedModificationTime = fileModTime;
|
||||
item.attachmentSyncedHash = fileHash;
|
||||
item.attachmentSyncState = "in_sync";
|
||||
yield item.saveTx({ skipAll: true });
|
||||
|
||||
return new Zotero.Sync.Storage.Result;
|
||||
}
|
||||
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "in_conflict");
|
||||
item.attachmentSyncState = "in_conflict";
|
||||
yield item.saveTx({ skipAll: true });
|
||||
|
||||
return new Zotero.Sync.Storage.Result({
|
||||
fileSyncRequired: true
|
||||
});
|
||||
|
@ -767,11 +765,12 @@ Zotero.Sync.Storage.Mode.ZFS.prototype = {
|
|||
_updateItemFileInfo: Zotero.Promise.coroutine(function* (item, params) {
|
||||
// Mark as in-sync
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "in_sync");
|
||||
// Store file mod time and hash
|
||||
item.attachmentSyncedModificationTime = params.mtime;
|
||||
item.attachmentSyncedHash = params.md5;
|
||||
item.attachmentSyncState = "in_sync";
|
||||
yield item.save({ skipAll: true });
|
||||
|
||||
// Store file mod time and hash
|
||||
yield Zotero.Sync.Storage.Local.setSyncedModificationTime(item.id, params.mtime);
|
||||
yield Zotero.Sync.Storage.Local.setSyncedHash(item.id, params.md5);
|
||||
// Update sync cache with new file metadata and version from server
|
||||
var json = yield Zotero.Sync.Data.Local.getCacheObject(
|
||||
'item', item.libraryID, item.key, item.version
|
||||
|
@ -933,7 +932,7 @@ Zotero.Sync.Storage.Mode.ZFS.prototype = {
|
|||
}
|
||||
|
||||
// Check for conflict
|
||||
if ((yield Zotero.Sync.Storage.Local.getSyncState(item.id))
|
||||
if (item.attachmentSyncState
|
||||
!= Zotero.Sync.Storage.Local.SYNC_STATE_FORCE_UPLOAD) {
|
||||
if (info) {
|
||||
// Local file time
|
||||
|
|
|
@ -316,7 +316,7 @@ Zotero.Sync.Data.Engine.prototype._startDownload = Zotero.Promise.coroutine(func
|
|||
// Conflict resolution
|
||||
else if (objectType == 'item') {
|
||||
conflicts.push({
|
||||
left: yield obj.toJSON(),
|
||||
left: obj.toJSON(),
|
||||
right: {
|
||||
deleted: true
|
||||
}
|
||||
|
|
|
@ -512,7 +512,7 @@ Zotero.Sync.Data.Local = {
|
|||
objectType, obj.libraryID, obj.key, obj.version
|
||||
);
|
||||
|
||||
let jsonDataLocal = yield obj.toJSON();
|
||||
let jsonDataLocal = obj.toJSON();
|
||||
|
||||
// For items, check if mtime or file hash changed in metadata,
|
||||
// which would indicate that a remote storage sync took place and
|
||||
|
@ -780,7 +780,8 @@ Zotero.Sync.Data.Local = {
|
|||
markToDownload = true;
|
||||
}
|
||||
if (markToDownload) {
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "to_download");
|
||||
item.attachmentSyncState = "to_download";
|
||||
yield item.save({ skipAll: true });
|
||||
}
|
||||
}),
|
||||
|
||||
|
@ -870,7 +871,6 @@ Zotero.Sync.Data.Local = {
|
|||
|
||||
_saveObjectFromJSON: Zotero.Promise.coroutine(function* (obj, json, options) {
|
||||
try {
|
||||
yield obj.loadAllData();
|
||||
obj.fromJSON(json);
|
||||
if (!options.saveAsChanged) {
|
||||
obj.version = json.version;
|
||||
|
|
|
@ -31,7 +31,6 @@ Zotero.Timeline = {
|
|||
yield '<data>\n';
|
||||
for (let i=0; i<items.length; i++) {
|
||||
let item = items[i];
|
||||
yield item.loadItemData();
|
||||
var date = item.getField(dateType, true, true);
|
||||
if (date) {
|
||||
let sqlDate = (dateType == 'date') ? Zotero.Date.multipartToSQL(date) : date;
|
||||
|
|
|
@ -659,7 +659,6 @@ Zotero.Translate.ItemGetter.prototype = {
|
|||
|
||||
"setCollection": Zotero.Promise.coroutine(function* (collection, getChildCollections) {
|
||||
// get items in this collection
|
||||
yield collection.loadChildItems();
|
||||
var items = new Set(collection.getChildItems());
|
||||
|
||||
if(getChildCollections) {
|
||||
|
@ -668,7 +667,6 @@ Zotero.Translate.ItemGetter.prototype = {
|
|||
|
||||
// get items in child collections
|
||||
for (let collection of this._collectionsLeft) {
|
||||
yield collection.loadChildItems();
|
||||
var childItems = collection.getChildItems();
|
||||
childItems.forEach(item => items.add(item));
|
||||
}
|
||||
|
@ -720,7 +718,7 @@ Zotero.Translate.ItemGetter.prototype = {
|
|||
* Converts an attachment to array format and copies it to the export folder if desired
|
||||
*/
|
||||
"_attachmentToArray":Zotero.Promise.coroutine(function* (attachment) {
|
||||
var attachmentArray = yield Zotero.Utilities.Internal.itemToExportFormat(attachment, this.legacy);
|
||||
var attachmentArray = Zotero.Utilities.Internal.itemToExportFormat(attachment, this.legacy);
|
||||
var linkMode = attachment.attachmentLinkMode;
|
||||
if(linkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) {
|
||||
var attachFile = attachment.getFile();
|
||||
|
@ -864,13 +862,13 @@ Zotero.Translate.ItemGetter.prototype = {
|
|||
var returnItemArray = yield this._attachmentToArray(returnItem);
|
||||
if(returnItemArray) return returnItemArray;
|
||||
} else {
|
||||
var returnItemArray = yield Zotero.Utilities.Internal.itemToExportFormat(returnItem, this.legacy);
|
||||
var returnItemArray = Zotero.Utilities.Internal.itemToExportFormat(returnItem, this.legacy);
|
||||
|
||||
// get attachments, although only urls will be passed if exportFileData is off
|
||||
returnItemArray.attachments = [];
|
||||
var attachments = returnItem.getAttachments();
|
||||
for each(var attachmentID in attachments) {
|
||||
var attachment = yield Zotero.Items.getAsync(attachmentID);
|
||||
var attachment = Zotero.Items.get(attachmentID);
|
||||
var attachmentInfo = yield this._attachmentToArray(attachment);
|
||||
|
||||
if(attachmentInfo) {
|
||||
|
|
|
@ -1591,8 +1591,9 @@ Zotero.Utilities = {
|
|||
*/
|
||||
"itemToCSLJSON":function(zoteroItem) {
|
||||
if (zoteroItem instanceof Zotero.Item) {
|
||||
return Zotero.Utilities.Internal.itemToExportFormat(zoteroItem).
|
||||
then(Zotero.Utilities.itemToCSLJSON);
|
||||
return this.itemToCSLJSON(
|
||||
Zotero.Utilities.Internal.itemToExportFormat(zoteroItem)
|
||||
);
|
||||
}
|
||||
|
||||
var cslType = CSL_TYPE_MAPPINGS[zoteroItem.itemType];
|
||||
|
|
|
@ -610,44 +610,7 @@ Zotero.Utilities.Internal = {
|
|||
* @param {Boolean} legacy Add mappings for legacy (pre-4.0.27) translators
|
||||
* @return {Promise<Object>}
|
||||
*/
|
||||
"itemToExportFormat": new function() {
|
||||
return Zotero.Promise.coroutine(function* (zoteroItem, legacy) {
|
||||
var item = yield zoteroItem.toJSON();
|
||||
|
||||
item.uri = Zotero.URI.getItemURI(zoteroItem);
|
||||
delete item.key;
|
||||
|
||||
if (!zoteroItem.isAttachment() && !zoteroItem.isNote()) {
|
||||
yield zoteroItem.loadChildItems();
|
||||
|
||||
// Include attachments
|
||||
item.attachments = [];
|
||||
let attachments = zoteroItem.getAttachments();
|
||||
for (let i=0; i<attachments.length; i++) {
|
||||
let zoteroAttachment = yield Zotero.Items.getAsync(attachments[i]),
|
||||
attachment = yield zoteroAttachment.toJSON();
|
||||
if (legacy) addCompatibilityMappings(attachment, zoteroAttachment);
|
||||
|
||||
item.attachments.push(attachment);
|
||||
}
|
||||
|
||||
// Include notes
|
||||
item.notes = [];
|
||||
let notes = zoteroItem.getNotes();
|
||||
for (let i=0; i<notes.length; i++) {
|
||||
let zoteroNote = yield Zotero.Items.getAsync(notes[i]),
|
||||
note = yield zoteroNote.toJSON();
|
||||
if (legacy) addCompatibilityMappings(note, zoteroNote);
|
||||
|
||||
item.notes.push(note);
|
||||
}
|
||||
}
|
||||
|
||||
if (legacy) addCompatibilityMappings(item, zoteroItem);
|
||||
|
||||
return item;
|
||||
});
|
||||
|
||||
itemToExportFormat: function (zoteroItem, legacy) {
|
||||
function addCompatibilityMappings(item, zoteroItem) {
|
||||
item.uniqueFields = {};
|
||||
|
||||
|
@ -735,6 +698,39 @@ Zotero.Utilities.Internal = {
|
|||
|
||||
return item;
|
||||
}
|
||||
|
||||
var item = zoteroItem.toJSON();
|
||||
|
||||
item.uri = Zotero.URI.getItemURI(zoteroItem);
|
||||
delete item.key;
|
||||
|
||||
if (!zoteroItem.isAttachment() && !zoteroItem.isNote()) {
|
||||
// Include attachments
|
||||
item.attachments = [];
|
||||
let attachments = zoteroItem.getAttachments();
|
||||
for (let i=0; i<attachments.length; i++) {
|
||||
let zoteroAttachment = Zotero.Items.get(attachments[i]),
|
||||
attachment = zoteroAttachment.toJSON();
|
||||
if (legacy) addCompatibilityMappings(attachment, zoteroAttachment);
|
||||
|
||||
item.attachments.push(attachment);
|
||||
}
|
||||
|
||||
// Include notes
|
||||
item.notes = [];
|
||||
let notes = zoteroItem.getNotes();
|
||||
for (let i=0; i<notes.length; i++) {
|
||||
let zoteroNote = Zotero.Items.get(notes[i]),
|
||||
note = zoteroNote.toJSON();
|
||||
if (legacy) addCompatibilityMappings(note, zoteroNote);
|
||||
|
||||
item.notes.push(note);
|
||||
}
|
||||
}
|
||||
|
||||
if (legacy) addCompatibilityMappings(item, zoteroItem);
|
||||
|
||||
return item;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -621,11 +621,19 @@ Components.utils.import("resource://gre/modules/osfile.jsm");
|
|||
// Initialize Locate Manager
|
||||
Zotero.LocateManager.init();
|
||||
|
||||
Zotero.Collections.init();
|
||||
Zotero.Items.init();
|
||||
yield Zotero.Collections.init();
|
||||
yield Zotero.Items.init();
|
||||
yield Zotero.Searches.init();
|
||||
yield Zotero.Creators.init();
|
||||
yield Zotero.Groups.init();
|
||||
|
||||
let libraryIDs = Zotero.Libraries.getAll().map(x => x.libraryID);
|
||||
for (let libraryID of libraryIDs) {
|
||||
yield Zotero.Collections.loadAllData(libraryID);
|
||||
yield Zotero.Searches.loadAllData(libraryID);
|
||||
yield Zotero.Items.loadAllData(libraryID);
|
||||
}
|
||||
|
||||
yield Zotero.QuickCopy.init();
|
||||
|
||||
Zotero.Items.startEmptyTrashTimer();
|
||||
|
|
|
@ -1294,7 +1294,6 @@ var ZoteroPane = new function()
|
|||
var clearUndo = noteEditor.item ? noteEditor.item.id != item.id : false;
|
||||
|
||||
noteEditor.parent = null;
|
||||
yield item.loadNote();
|
||||
noteEditor.item = item;
|
||||
|
||||
// If loading new or different note, disable undo while we repopulate the text field
|
||||
|
@ -1325,8 +1324,6 @@ var ZoteroPane = new function()
|
|||
else if (item.isAttachment()) {
|
||||
var attachmentBox = document.getElementById('zotero-attachment-box');
|
||||
attachmentBox.mode = this.collectionsView.editable ? 'edit' : 'view';
|
||||
yield item.loadItemData();
|
||||
yield item.loadNote();
|
||||
attachmentBox.item = item;
|
||||
|
||||
document.getElementById('zotero-item-pane-content').selectedIndex = 3;
|
||||
|
@ -1588,7 +1585,7 @@ var ZoteroPane = new function()
|
|||
var newItem;
|
||||
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
newItem = yield item.clone(null, !Zotero.Prefs.get('groups.copyTags'));
|
||||
newItem = item.clone(null, !Zotero.Prefs.get('groups.copyTags'));
|
||||
yield newItem.save();
|
||||
|
||||
if (self.collectionsView.selectedTreeRow.isCollection() && newItem.isTopLevelItem()) {
|
||||
|
@ -3641,7 +3638,6 @@ var ZoteroPane = new function()
|
|||
|
||||
// Fall back to first attachment link
|
||||
if (!uri) {
|
||||
yield item.loadChildItems();
|
||||
let attachmentID = item.getAttachments()[0];
|
||||
if (attachmentID) {
|
||||
let attachment = yield Zotero.Items.getAsync(attachmentID);
|
||||
|
@ -3851,7 +3847,7 @@ var ZoteroPane = new function()
|
|||
});
|
||||
|
||||
|
||||
this.showPublicationsWizard = Zotero.Promise.coroutine(function* (items) {
|
||||
this.showPublicationsWizard = function (items) {
|
||||
var io = {
|
||||
hasFiles: false,
|
||||
hasNotes: false,
|
||||
|
@ -3863,14 +3859,12 @@ var ZoteroPane = new function()
|
|||
for (let i = 0; i < items.length; i++) {
|
||||
let item = items[i];
|
||||
|
||||
yield item.loadItemData();
|
||||
yield item.loadChildItems();
|
||||
|
||||
// Files
|
||||
if (!io.hasFiles && item.numAttachments()) {
|
||||
let attachments = item.getAttachments();
|
||||
attachments = yield Zotero.Items.getAsync(attachments);
|
||||
io.hasFiles = attachments.some(attachment => attachment.isFileAttachment());
|
||||
let attachmentIDs = item.getAttachments();
|
||||
io.hasFiles = Zotero.Items.get(attachmentIDs).some(
|
||||
attachment => attachment.isFileAttachment()
|
||||
);
|
||||
}
|
||||
// Notes
|
||||
if (!io.hasNotes && item.numNotes()) {
|
||||
|
@ -3887,7 +3881,7 @@ var ZoteroPane = new function()
|
|||
io.hasRights = allItemsHaveRights ? 'all' : (noItemsHaveRights ? 'none' : 'some');
|
||||
window.openDialog('chrome://zotero/content/publicationsDialog.xul','','chrome,modal', io);
|
||||
return io.license ? io : false;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -214,7 +214,7 @@ function ZoteroProtocolHandler() {
|
|||
else if (combineChildItems || !results[i].isRegularItem()
|
||||
|| results[i].numChildren() == 0) {
|
||||
itemsHash[results[i].id] = [items.length];
|
||||
items.push(yield results[i].toJSON({ mode: 'full' }));
|
||||
items.push(results[i].toJSON({ mode: 'full' }));
|
||||
// Flag item as a search match
|
||||
items[items.length - 1].reportSearchMatch = true;
|
||||
}
|
||||
|
@ -241,7 +241,6 @@ function ZoteroProtocolHandler() {
|
|||
}
|
||||
}
|
||||
};
|
||||
yield item.loadChildItems();
|
||||
func(item.getNotes());
|
||||
func(item.getAttachments());
|
||||
}
|
||||
|
@ -252,7 +251,7 @@ function ZoteroProtocolHandler() {
|
|||
else {
|
||||
for (var i in unhandledParents) {
|
||||
itemsHash[results[i].id] = [items.length];
|
||||
items.push(yield results[i].toJSON({ mode: 'full' }));
|
||||
items.push(results[i].toJSON({ mode: 'full' }));
|
||||
// Flag item as a search match
|
||||
items[items.length - 1].reportSearchMatch = true;
|
||||
}
|
||||
|
@ -264,7 +263,7 @@ function ZoteroProtocolHandler() {
|
|||
if (!searchItemIDs[id] && !itemsHash[id]) {
|
||||
var item = yield Zotero.Items.getAsync(id);
|
||||
itemsHash[id] = items.length;
|
||||
items.push(yield item.toJSON({ mode: 'full' }));
|
||||
items.push(item.toJSON({ mode: 'full' }));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -279,10 +278,10 @@ function ZoteroProtocolHandler() {
|
|||
};
|
||||
}
|
||||
if (item.isNote()) {
|
||||
items[itemsHash[parentID]].reportChildren.notes.push(yield item.toJSON({ mode: 'full' }));
|
||||
items[itemsHash[parentID]].reportChildren.notes.push(item.toJSON({ mode: 'full' }));
|
||||
}
|
||||
if (item.isAttachment()) {
|
||||
items[itemsHash[parentID]].reportChildren.attachments.push(yield item.toJSON({ mode: 'full' }));
|
||||
items[itemsHash[parentID]].reportChildren.attachments.push(item.toJSON({ mode: 'full' }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -299,7 +298,7 @@ function ZoteroProtocolHandler() {
|
|||
// add on its own
|
||||
if (searchItemIDs[parentID]) {
|
||||
itemsHash[parentID] = [items.length];
|
||||
items.push(yield parentItem.toJSON({ mode: 'full' }));
|
||||
items.push(parentItem.toJSON({ mode: 'full' }));
|
||||
items[items.length - 1].reportSearchMatch = true;
|
||||
}
|
||||
else {
|
||||
|
@ -312,14 +311,14 @@ function ZoteroProtocolHandler() {
|
|||
items.push(parentItem.toJSON({ mode: 'full' }));
|
||||
if (item.isNote()) {
|
||||
items[items.length - 1].reportChildren = {
|
||||
notes: [yield item.toJSON({ mode: 'full' })],
|
||||
notes: [item.toJSON({ mode: 'full' })],
|
||||
attachments: []
|
||||
};
|
||||
}
|
||||
else if (item.isAttachment()) {
|
||||
items[items.length - 1].reportChildren = {
|
||||
notes: [],
|
||||
attachments: [yield item.toJSON({ mode: 'full' })]
|
||||
attachments: [item.toJSON({ mode: 'full' })]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -609,7 +608,6 @@ function ZoteroProtocolHandler() {
|
|||
if (params.controller == 'data') {
|
||||
switch (params.scopeObject) {
|
||||
case 'collections':
|
||||
yield collection.loadChildItems();
|
||||
var results = collection.getChildItems();
|
||||
break;
|
||||
|
||||
|
|
|
@ -352,10 +352,9 @@ function getNameProperty(objectType) {
|
|||
return objectType == 'item' ? 'title' : 'name';
|
||||
}
|
||||
|
||||
var modifyDataObject = Zotero.Promise.coroutine(function* (obj, params = {}, saveOptions) {
|
||||
var modifyDataObject = function (obj, params = {}, saveOptions) {
|
||||
switch (obj.objectType) {
|
||||
case 'item':
|
||||
yield obj.loadItemData();
|
||||
obj.setField(
|
||||
'title',
|
||||
params.title !== undefined ? params.title : Zotero.Utilities.randomString()
|
||||
|
@ -366,7 +365,7 @@ var modifyDataObject = Zotero.Promise.coroutine(function* (obj, params = {}, sav
|
|||
obj.name = params.name !== undefined ? params.name : Zotero.Utilities.randomString();
|
||||
}
|
||||
return obj.saveTx(saveOptions);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a promise for the error thrown by a promise, or false if none
|
||||
|
@ -584,7 +583,7 @@ var generateItemJSONData = Zotero.Promise.coroutine(function* generateItemJSONDa
|
|||
|
||||
for (let itemName in items) {
|
||||
let zItem = yield Zotero.Items.getAsync(items[itemName].id);
|
||||
jsonData[itemName] = yield zItem.toJSON(options);
|
||||
jsonData[itemName] = zItem.toJSON(options);
|
||||
|
||||
// Don't replace some fields that _always_ change (e.g. item keys)
|
||||
// as long as it follows expected format
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit b369f252432c3486a66a0e93f441e4abb133d229
|
||||
Subproject commit 775281e138df26101fba1e554c516f47438851b5
|
|
@ -1 +1 @@
|
|||
Subproject commit 2a8594424c73ffeca41ef1668446372160528b4a
|
||||
Subproject commit 44b0045463907b1d7963a2e9560c24d9552aac5d
|
|
@ -152,7 +152,6 @@ describe("Zotero.Collection", function() {
|
|||
var collection2 = yield createDataObject('collection', { parentID: collection1.id });
|
||||
yield collection1.saveTx();
|
||||
|
||||
yield collection1.loadChildCollections();
|
||||
var childCollections = collection1.getChildCollections();
|
||||
assert.lengthOf(childCollections, 1);
|
||||
assert.equal(childCollections[0].id, collection2.id);
|
||||
|
@ -163,8 +162,6 @@ describe("Zotero.Collection", function() {
|
|||
var collection2 = yield createDataObject('collection', { parentID: collection1.id });
|
||||
yield collection1.saveTx();
|
||||
|
||||
yield collection1.loadChildCollections();
|
||||
|
||||
collection2.parentID = false;
|
||||
yield collection2.save()
|
||||
|
||||
|
@ -180,7 +177,6 @@ describe("Zotero.Collection", function() {
|
|||
item.addToCollection(collection.key);
|
||||
yield item.saveTx();
|
||||
|
||||
yield collection.loadChildItems();
|
||||
assert.lengthOf(collection.getChildItems(), 1);
|
||||
})
|
||||
|
||||
|
@ -191,7 +187,6 @@ describe("Zotero.Collection", function() {
|
|||
item.addToCollection(collection.key);
|
||||
yield item.saveTx();
|
||||
|
||||
yield collection.loadChildItems();
|
||||
assert.lengthOf(collection.getChildItems(), 0);
|
||||
})
|
||||
|
||||
|
@ -202,7 +197,6 @@ describe("Zotero.Collection", function() {
|
|||
item.addToCollection(collection.key);
|
||||
yield item.saveTx();
|
||||
|
||||
yield collection.loadChildItems();
|
||||
assert.lengthOf(collection.getChildItems(false, true), 1);
|
||||
})
|
||||
})
|
||||
|
|
|
@ -390,12 +390,6 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
parentItemID: item.id
|
||||
});
|
||||
|
||||
// Hack to unload relations to test proper loading
|
||||
//
|
||||
// Probably need a better method for this
|
||||
item._loaded.relations = false;
|
||||
attachment._loaded.relations = false;
|
||||
|
||||
var ids = (yield drop('item', 'L' + group.libraryID, [item.id])).ids;
|
||||
|
||||
yield cv.selectLibrary(group.libraryID);
|
||||
|
@ -413,7 +407,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
|
||||
// Check attachment
|
||||
assert.isTrue(itemsView.isContainer(0));
|
||||
yield itemsView.toggleOpenState(0);
|
||||
itemsView.toggleOpenState(0);
|
||||
assert.equal(itemsView.rowCount, 2);
|
||||
treeRow = itemsView.getRow(1);
|
||||
assert.equal(treeRow.ref.id, ids[1]);
|
||||
|
|
21
test/tests/creatorsTest.js
Normal file
21
test/tests/creatorsTest.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
"use strict";
|
||||
|
||||
describe("Zotero.Creators", function() {
|
||||
describe("#getIDFromData()", function () {
|
||||
it("should create creator and cache data", function* () {
|
||||
var data1 = {
|
||||
firstName: "First",
|
||||
lastName: "Last"
|
||||
};
|
||||
var creatorID;
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
creatorID = yield Zotero.Creators.getIDFromData(data1, true);
|
||||
});
|
||||
assert.typeOf(creatorID, 'number');
|
||||
var data2 = Zotero.Creators.get(creatorID);
|
||||
assert.isObject(data2);
|
||||
assert.propertyVal(data2, "firstName", data1.firstName);
|
||||
assert.propertyVal(data2, "lastName", data1.lastName);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -56,7 +56,6 @@ describe("Zotero.DataObject", function() {
|
|||
yield obj.saveTx();
|
||||
|
||||
if (type == 'item') {
|
||||
yield obj.loadItemData();
|
||||
obj.setField('title', Zotero.Utilities.randomString());
|
||||
}
|
||||
else {
|
||||
|
@ -131,7 +130,6 @@ describe("Zotero.DataObject", function() {
|
|||
yield obj.saveTx();
|
||||
|
||||
if (type == 'item') {
|
||||
yield obj.loadItemData();
|
||||
obj.setField('title', Zotero.Utilities.randomString());
|
||||
}
|
||||
else {
|
||||
|
@ -294,7 +292,7 @@ describe("Zotero.DataObject", function() {
|
|||
let obj = yield createDataObject(type);
|
||||
let libraryID = obj.libraryID;
|
||||
let key = obj.key;
|
||||
let json = yield obj.toJSON();
|
||||
let json = obj.toJSON();
|
||||
yield Zotero.Sync.Data.Local.saveCacheObjects(type, libraryID, [json]);
|
||||
yield obj.eraseTx();
|
||||
let versions = yield Zotero.Sync.Data.Local.getCacheObjectVersions(
|
||||
|
|
|
@ -25,11 +25,11 @@ describe("Zotero.DataObjectUtilities", function() {
|
|||
yield Zotero.DB.executeTransaction(function* () {
|
||||
var item = new Zotero.Item('book');
|
||||
id1 = yield item.save();
|
||||
json1 = yield item.toJSON();
|
||||
json1 = item.toJSON();
|
||||
|
||||
var item = new Zotero.Item('book');
|
||||
id2 = yield item.save();
|
||||
json2 = yield item.toJSON();
|
||||
json2 = item.toJSON();
|
||||
});
|
||||
|
||||
var changes = Zotero.DataObjectUtilities.diff(json1, json2);
|
||||
|
|
|
@ -21,7 +21,7 @@ describe("Zotero_File_Interface", function() {
|
|||
let childItems = importedCollection[0].getChildItems();
|
||||
let savedItems = {};
|
||||
for (let i=0; i<childItems.length; i++) {
|
||||
let savedItem = yield childItems[i].toJSON();
|
||||
let savedItem = childItems[i].toJSON();
|
||||
delete savedItem.dateAdded;
|
||||
delete savedItem.dateModified;
|
||||
delete savedItem.key;
|
||||
|
|
|
@ -141,7 +141,7 @@ describe("Zotero.Item", function () {
|
|||
it("should use current time if value was not given for a new item", function* () {
|
||||
var item = new Zotero.Item('book');
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
item = Zotero.Items.get(id);
|
||||
|
||||
assert.closeTo(Zotero.Date.sqlToDate(item.dateAdded, true).getTime(), Date.now(), 2000);
|
||||
})
|
||||
|
@ -184,10 +184,9 @@ describe("Zotero.Item", function () {
|
|||
var item = new Zotero.Item('book');
|
||||
item.dateModified = dateModified;
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
item = Zotero.Items.get(id);
|
||||
|
||||
// Save again without changing Date Modified
|
||||
yield item.loadItemData();
|
||||
item.setField('title', 'Test');
|
||||
yield item.saveTx()
|
||||
|
||||
|
@ -199,10 +198,9 @@ describe("Zotero.Item", function () {
|
|||
var item = new Zotero.Item('book');
|
||||
item.dateModified = dateModified;
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
item = Zotero.Items.get(id);
|
||||
|
||||
// Set Date Modified to existing value
|
||||
yield item.loadItemData();
|
||||
item.setField('title', 'Test');
|
||||
item.dateModified = dateModified;
|
||||
yield item.saveTx()
|
||||
|
@ -223,10 +221,9 @@ describe("Zotero.Item", function () {
|
|||
var item = new Zotero.Item('book');
|
||||
item.dateModified = dateModified;
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
item = Zotero.Items.get(id);
|
||||
|
||||
// Resave with skipDateModifiedUpdate
|
||||
yield item.loadItemData();
|
||||
item.setField('title', 'Test');
|
||||
yield item.saveTx({
|
||||
skipDateModifiedUpdate: true
|
||||
|
@ -353,8 +350,7 @@ describe("Zotero.Item", function () {
|
|||
var item = new Zotero.Item("journalArticle");
|
||||
item.setCreators(creators);
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
yield item.loadCreators();
|
||||
item = Zotero.Items.get(id);
|
||||
assert.sameDeepMembers(item.getCreatorsJSON(), creators);
|
||||
})
|
||||
|
||||
|
@ -377,8 +373,7 @@ describe("Zotero.Item", function () {
|
|||
var item = new Zotero.Item("journalArticle");
|
||||
item.setCreators(creators);
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
yield item.loadCreators();
|
||||
item = Zotero.Items.get(id);
|
||||
assert.sameDeepMembers(item.getCreators(), creators);
|
||||
})
|
||||
})
|
||||
|
@ -686,8 +681,7 @@ describe("Zotero.Item", function () {
|
|||
var item = new Zotero.Item('journalArticle');
|
||||
item.setTags(tags);
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
yield item.loadTags();
|
||||
item = Zotero.Items.get(id);
|
||||
assert.sameDeepMembers(item.getTags(tags), tags);
|
||||
})
|
||||
|
||||
|
@ -703,8 +697,7 @@ describe("Zotero.Item", function () {
|
|||
var item = new Zotero.Item('journalArticle');
|
||||
item.setTags(tags);
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
yield item.loadTags();
|
||||
item = Zotero.Items.get(id);
|
||||
item.setTags(tags);
|
||||
assert.isFalse(item.hasChanged());
|
||||
})
|
||||
|
@ -721,8 +714,7 @@ describe("Zotero.Item", function () {
|
|||
var item = new Zotero.Item('journalArticle');
|
||||
item.setTags(tags);
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
yield item.loadTags();
|
||||
item = Zotero.Items.get(id);
|
||||
item.setTags(tags.slice(0));
|
||||
yield item.saveTx();
|
||||
assert.sameDeepMembers(item.getTags(tags), tags.slice(0));
|
||||
|
@ -837,7 +829,7 @@ describe("Zotero.Item", function () {
|
|||
}
|
||||
]);
|
||||
yield item.saveTx();
|
||||
var newItem = yield item.clone();
|
||||
var newItem = item.clone();
|
||||
assert.sameDeepMembers(item.getCreators(), newItem.getCreators());
|
||||
})
|
||||
})
|
||||
|
@ -851,8 +843,8 @@ describe("Zotero.Item", function () {
|
|||
var item = new Zotero.Item(itemType);
|
||||
item.setField("title", title);
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
var json = yield item.toJSON();
|
||||
item = Zotero.Items.get(id);
|
||||
var json = item.toJSON();
|
||||
|
||||
assert.equal(json.itemType, itemType);
|
||||
assert.equal(json.title, title);
|
||||
|
@ -868,13 +860,13 @@ describe("Zotero.Item", function () {
|
|||
item.setField("title", title);
|
||||
item.deleted = true;
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
var json = yield item.toJSON();
|
||||
item = Zotero.Items.get(id);
|
||||
var json = item.toJSON();
|
||||
|
||||
assert.strictEqual(json.deleted, 1);
|
||||
})
|
||||
|
||||
it("should output attachment fields from file", function* () {
|
||||
it.skip("should output attachment fields from file", function* () {
|
||||
var file = getTestDataDirectory();
|
||||
file.append('test.png');
|
||||
var item = yield Zotero.Attachments.importFromFile({ file });
|
||||
|
@ -888,7 +880,7 @@ describe("Zotero.Item", function () {
|
|||
);
|
||||
});
|
||||
|
||||
var json = yield item.toJSON();
|
||||
var json = item.toJSON();
|
||||
assert.equal(json.linkMode, 'imported_file');
|
||||
assert.equal(json.filename, 'test.png');
|
||||
assert.isUndefined(json.path);
|
||||
|
@ -910,19 +902,19 @@ describe("Zotero.Item", function () {
|
|||
yield Zotero.Sync.Storage.Local.setSyncedHash(item.id, md5);
|
||||
});
|
||||
|
||||
var json = yield item.toJSON({
|
||||
var json = item.toJSON({
|
||||
syncedStorageProperties: true
|
||||
});
|
||||
assert.equal(json.mtime, mtime);
|
||||
assert.equal(json.md5, md5);
|
||||
})
|
||||
|
||||
it("should output unset storage properties as null", function* () {
|
||||
it.skip("should output unset storage properties as null", function* () {
|
||||
var item = new Zotero.Item('attachment');
|
||||
item.attachmentLinkMode = 'imported_file';
|
||||
item.fileName = 'test.txt';
|
||||
var id = yield item.saveTx();
|
||||
var json = yield item.toJSON();
|
||||
var json = item.toJSON();
|
||||
|
||||
assert.isNull(json.mtime);
|
||||
assert.isNull(json.md5);
|
||||
|
@ -938,7 +930,7 @@ describe("Zotero.Item", function () {
|
|||
item.setField("title", title);
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
var json = yield item.toJSON({ mode: 'full' });
|
||||
var json = item.toJSON({ mode: 'full' });
|
||||
assert.equal(json.title, title);
|
||||
assert.equal(json.date, "");
|
||||
assert.equal(json.numPages, "");
|
||||
|
@ -955,11 +947,11 @@ describe("Zotero.Item", function () {
|
|||
item.setField("title", title);
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
var patchBase = yield item.toJSON();
|
||||
var patchBase = item.toJSON();
|
||||
|
||||
item.setField("date", date);
|
||||
yield item.saveTx();
|
||||
var json = yield item.toJSON({
|
||||
var json = item.toJSON({
|
||||
patchBase: patchBase
|
||||
})
|
||||
assert.isUndefined(json.itemType);
|
||||
|
@ -978,10 +970,10 @@ describe("Zotero.Item", function () {
|
|||
item.deleted = true;
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
var patchBase = yield item.toJSON();
|
||||
var patchBase = item.toJSON();
|
||||
|
||||
item.deleted = false;
|
||||
var json = yield item.toJSON({
|
||||
var json = item.toJSON({
|
||||
patchBase: patchBase
|
||||
})
|
||||
assert.isUndefined(json.title);
|
||||
|
@ -992,10 +984,10 @@ describe("Zotero.Item", function () {
|
|||
item.deleted = false;
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
var patchBase = yield item.toJSON();
|
||||
var patchBase = item.toJSON();
|
||||
|
||||
item.deleted = true;
|
||||
var json = yield item.toJSON({
|
||||
var json = item.toJSON({
|
||||
patchBase: patchBase
|
||||
})
|
||||
assert.isUndefined(json.title);
|
||||
|
|
|
@ -148,7 +148,7 @@ describe("Sync Preferences", function () {
|
|||
var cont = yield win.Zotero_Preferences.Sync.checkUser(1, "A");
|
||||
assert.isTrue(cont);
|
||||
|
||||
var json = yield item1.toJSON();
|
||||
var json = item1.toJSON();
|
||||
var uri = json.relations[Zotero.Relations.linkedObjectPredicate][0];
|
||||
assert.notInclude(uri, 'users/local');
|
||||
assert.include(uri, 'users/1/publications');
|
||||
|
|
|
@ -71,12 +71,10 @@ describe("Related Box", function () {
|
|||
var item1 = yield createDataObject('item');
|
||||
var item2 = yield createDataObject('item');
|
||||
|
||||
yield item1.loadRelations();
|
||||
item1.addRelatedItem(item2);
|
||||
yield item1.save();
|
||||
yield item2.loadRelations();
|
||||
yield item1.saveTx();
|
||||
item2.addRelatedItem(item1);
|
||||
yield item2.save();
|
||||
yield item2.saveTx();
|
||||
|
||||
// Select the Related pane
|
||||
var tabbox = doc.getElementById('zotero-view-tabbox');
|
||||
|
|
|
@ -14,16 +14,20 @@ describe("Zotero.Search", function() {
|
|||
var s = new Zotero.Search;
|
||||
s.name = "Test";
|
||||
s.addCondition('title', 'is', 'test');
|
||||
Zotero.debug("BEFORE SAVING");
|
||||
Zotero.debug(s._conditions);
|
||||
var id = yield s.saveTx();
|
||||
Zotero.debug("DONE SAVING");
|
||||
Zotero.debug(s._conditions);
|
||||
assert.typeOf(id, 'number');
|
||||
|
||||
// Check saved search
|
||||
s = yield Zotero.Searches.getAsync(id);
|
||||
s = Zotero.Searches.get(id);
|
||||
assert.ok(s);
|
||||
assert.instanceOf(s, Zotero.Search);
|
||||
assert.equal(s.libraryID, Zotero.Libraries.userLibraryID);
|
||||
assert.equal(s.name, "Test");
|
||||
yield s.loadConditions();
|
||||
Zotero.debug("GETTING CONDITIONS");
|
||||
var conditions = s.getConditions();
|
||||
assert.lengthOf(Object.keys(conditions), 1);
|
||||
assert.property(conditions, "0");
|
||||
|
@ -45,14 +49,12 @@ describe("Zotero.Search", function() {
|
|||
|
||||
// Add condition
|
||||
s = yield Zotero.Searches.getAsync(id);
|
||||
yield s.loadConditions();
|
||||
s.addCondition('title', 'contains', 'foo');
|
||||
var saved = yield s.saveTx();
|
||||
assert.isTrue(saved);
|
||||
|
||||
// Check saved search
|
||||
s = yield Zotero.Searches.getAsync(id);
|
||||
yield s.loadConditions();
|
||||
var conditions = s.getConditions();
|
||||
assert.lengthOf(Object.keys(conditions), 2);
|
||||
});
|
||||
|
@ -69,14 +71,12 @@ describe("Zotero.Search", function() {
|
|||
|
||||
// Remove condition
|
||||
s = yield Zotero.Searches.getAsync(id);
|
||||
yield s.loadConditions();
|
||||
s.removeCondition(0);
|
||||
var saved = yield s.saveTx();
|
||||
assert.isTrue(saved);
|
||||
|
||||
// Check saved search
|
||||
s = yield Zotero.Searches.getAsync(id);
|
||||
yield s.loadConditions();
|
||||
var conditions = s.getConditions();
|
||||
assert.lengthOf(Object.keys(conditions), 1);
|
||||
assert.property(conditions, "0");
|
||||
|
|
|
@ -28,11 +28,10 @@ describe("Zotero.Sync.Storage.Local", function () {
|
|||
yield OS.File.setDates((yield item.getFilePathAsync()), null, mtime);
|
||||
|
||||
// Mark as synced, so it will be checked
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.Sync.Storage.Local.setSyncedHash(item.id, hash);
|
||||
yield Zotero.Sync.Storage.Local.setSyncedModificationTime(item.id, mtime);
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "in_sync");
|
||||
});
|
||||
item.attachmentSyncedModificationTime = mtime;
|
||||
item.attachmentSyncedHash = hash;
|
||||
item.attachmentSyncState = "in_sync";
|
||||
yield item.saveTx({ skipAll: true });
|
||||
|
||||
// Update mtime and contents
|
||||
var path = yield item.getFilePathAsync();
|
||||
|
@ -46,10 +45,7 @@ describe("Zotero.Sync.Storage.Local", function () {
|
|||
yield item.eraseTx();
|
||||
|
||||
assert.equal(changed, true);
|
||||
assert.equal(
|
||||
(yield Zotero.Sync.Storage.Local.getSyncState(item.id)),
|
||||
Zotero.Sync.Storage.Local.SYNC_STATE_TO_UPLOAD
|
||||
);
|
||||
assert.equal(item.attachmentSyncState, Zotero.Sync.Storage.Local.SYNC_STATE_TO_UPLOAD);
|
||||
})
|
||||
|
||||
it("should skip a file if mod time hasn't changed", function* () {
|
||||
|
@ -59,15 +55,14 @@ describe("Zotero.Sync.Storage.Local", function () {
|
|||
var mtime = yield item.attachmentModificationTime;
|
||||
|
||||
// Mark as synced, so it will be checked
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.Sync.Storage.Local.setSyncedHash(item.id, hash);
|
||||
yield Zotero.Sync.Storage.Local.setSyncedModificationTime(item.id, mtime);
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "in_sync");
|
||||
});
|
||||
item.attachmentSyncedModificationTime = mtime;
|
||||
item.attachmentSyncedHash = hash;
|
||||
item.attachmentSyncState = "in_sync";
|
||||
yield item.saveTx({ skipAll: true });
|
||||
|
||||
var libraryID = Zotero.Libraries.userLibraryID;
|
||||
var changed = yield Zotero.Sync.Storage.Local.checkForUpdatedFiles(libraryID);
|
||||
var syncState = yield Zotero.Sync.Storage.Local.getSyncState(item.id);
|
||||
var syncState = item.attachmentSyncState;
|
||||
|
||||
yield item.eraseTx();
|
||||
|
||||
|
@ -84,11 +79,10 @@ describe("Zotero.Sync.Storage.Local", function () {
|
|||
yield OS.File.setDates((yield item.getFilePathAsync()), null, mtime);
|
||||
|
||||
// Mark as synced, so it will be checked
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.Sync.Storage.Local.setSyncedHash(item.id, hash);
|
||||
yield Zotero.Sync.Storage.Local.setSyncedModificationTime(item.id, mtime);
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "in_sync");
|
||||
});
|
||||
item.attachmentSyncedModificationTime = mtime;
|
||||
item.attachmentSyncedHash = hash;
|
||||
item.attachmentSyncState = "in_sync";
|
||||
yield item.saveTx({ skipAll: true });
|
||||
|
||||
// Update mtime, but not contents
|
||||
var path = yield item.getFilePathAsync();
|
||||
|
@ -96,8 +90,8 @@ describe("Zotero.Sync.Storage.Local", function () {
|
|||
|
||||
var libraryID = Zotero.Libraries.userLibraryID;
|
||||
var changed = yield Zotero.Sync.Storage.Local.checkForUpdatedFiles(libraryID);
|
||||
var syncState = yield Zotero.Sync.Storage.Local.getSyncState(item.id);
|
||||
var syncedModTime = yield Zotero.Sync.Storage.Local.getSyncedModificationTime(item.id);
|
||||
var syncState = item.attachmentSyncState;
|
||||
var syncedModTime = item.attachmentSyncedModificationTime;
|
||||
var newModTime = yield item.attachmentModificationTime;
|
||||
|
||||
yield item.eraseTx();
|
||||
|
@ -202,8 +196,8 @@ describe("Zotero.Sync.Storage.Local", function () {
|
|||
item3.version = 11;
|
||||
yield item3.saveTx();
|
||||
|
||||
var json1 = yield item1.toJSON();
|
||||
var json3 = yield item3.toJSON();
|
||||
var json1 = item1.toJSON();
|
||||
var json3 = item3.toJSON();
|
||||
// Change remote mtimes
|
||||
// Round to nearest second because OS X doesn't support ms resolution
|
||||
var now = Math.round(new Date().getTime() / 1000) * 1000;
|
||||
|
@ -211,8 +205,10 @@ describe("Zotero.Sync.Storage.Local", function () {
|
|||
json3.mtime = now - 20000;
|
||||
yield Zotero.Sync.Data.Local.saveCacheObjects('item', libraryID, [json1, json3]);
|
||||
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item1.id, "in_conflict");
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item3.id, "in_conflict");
|
||||
item1.attachmentSyncState = "in_conflict";
|
||||
yield item1.saveTx({ skipAll: true });
|
||||
item3.attachmentSyncState = "in_conflict";
|
||||
yield item3.saveTx({ skipAll: true });
|
||||
|
||||
var conflicts = yield Zotero.Sync.Storage.Local.getConflicts(libraryID);
|
||||
assert.lengthOf(conflicts, 2);
|
||||
|
@ -251,19 +247,17 @@ describe("Zotero.Sync.Storage.Local", function () {
|
|||
item3.version = 11;
|
||||
yield item3.saveTx();
|
||||
|
||||
var json1 = yield item1.toJSON();
|
||||
var json3 = yield item3.toJSON();
|
||||
var json1 = item1.toJSON();
|
||||
var json3 = item3.toJSON();
|
||||
// Change remote mtimes
|
||||
json1.mtime = new Date().getTime() + 10000;
|
||||
json3.mtime = new Date().getTime() - 10000;
|
||||
yield Zotero.Sync.Data.Local.saveCacheObjects('item', libraryID, [json1, json3]);
|
||||
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(
|
||||
item1.id, "in_conflict"
|
||||
);
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(
|
||||
item3.id, "in_conflict"
|
||||
);
|
||||
item1.attachmentSyncState = "in_conflict";
|
||||
yield item1.saveTx({ skipAll: true });
|
||||
item3.attachmentSyncState = "in_conflict";
|
||||
yield item3.saveTx({ skipAll: true });
|
||||
|
||||
var promise = waitForWindow('chrome://zotero/content/merge.xul', function (dialog) {
|
||||
var doc = dialog.document;
|
||||
|
@ -305,14 +299,8 @@ describe("Zotero.Sync.Storage.Local", function () {
|
|||
yield Zotero.Sync.Storage.Local.resolveConflicts(libraryID);
|
||||
yield promise;
|
||||
|
||||
yield assert.eventually.equal(
|
||||
Zotero.Sync.Storage.Local.getSyncState(item1.id),
|
||||
Zotero.Sync.Storage.Local.SYNC_STATE_FORCE_UPLOAD
|
||||
);
|
||||
yield assert.eventually.equal(
|
||||
Zotero.Sync.Storage.Local.getSyncState(item3.id),
|
||||
Zotero.Sync.Storage.Local.SYNC_STATE_FORCE_DOWNLOAD
|
||||
);
|
||||
assert.equal(item1.attachmentSyncState, Zotero.Sync.Storage.Local.SYNC_STATE_FORCE_UPLOAD);
|
||||
assert.equal(item3.attachmentSyncState, Zotero.Sync.Storage.Local.SYNC_STATE_FORCE_DOWNLOAD);
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -283,7 +283,7 @@ describe("Zotero.Sync.Data.Engine", function () {
|
|||
for (let type of types) {
|
||||
objects[type] = [yield createDataObject(type, { setTitle: true })];
|
||||
objectVersions[type] = {};
|
||||
objectResponseJSON[type] = yield Zotero.Promise.all(objects[type].map(o => o.toResponseJSON()));
|
||||
objectResponseJSON[type] = objects[type].map(o => o.toResponseJSON());
|
||||
}
|
||||
|
||||
server.respond(function (req) {
|
||||
|
@ -457,12 +457,11 @@ describe("Zotero.Sync.Data.Engine", function () {
|
|||
var mtime = new Date().getTime();
|
||||
var md5 = '57f8a4fda823187b91e1191487b87fe6';
|
||||
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.Sync.Storage.Local.setSyncedModificationTime(item.id, mtime);
|
||||
yield Zotero.Sync.Storage.Local.setSyncedHash(item.id, md5);
|
||||
});
|
||||
item.attachmentSyncedModificationTime = mtime;
|
||||
item.attachmentSyncedHash = md5;
|
||||
yield item.saveTx({ skipAll: true });
|
||||
|
||||
var itemResponseJSON = yield item.toResponseJSON();
|
||||
var itemResponseJSON = item.toResponseJSON();
|
||||
itemResponseJSON.version = itemResponseJSON.data.version = lastLibraryVersion;
|
||||
itemResponseJSON.data.mtime = mtime;
|
||||
itemResponseJSON.data.md5 = md5;
|
||||
|
@ -520,7 +519,7 @@ describe("Zotero.Sync.Data.Engine", function () {
|
|||
for (let type of types) {
|
||||
objects[type] = [yield createDataObject(type, { setTitle: true })];
|
||||
objectNames[type] = {};
|
||||
objectResponseJSON[type] = yield Zotero.Promise.all(objects[type].map(o => o.toResponseJSON()));
|
||||
objectResponseJSON[type] = objects[type].map(o => o.toResponseJSON());
|
||||
}
|
||||
|
||||
server.respond(function (req) {
|
||||
|
@ -569,7 +568,6 @@ describe("Zotero.Sync.Data.Engine", function () {
|
|||
let version = o.version;
|
||||
let name = objectNames[type][key];
|
||||
if (type == 'item') {
|
||||
yield o.loadItemData();
|
||||
assert.equal(name, o.getField('title'));
|
||||
}
|
||||
else {
|
||||
|
@ -675,7 +673,7 @@ describe("Zotero.Sync.Data.Engine", function () {
|
|||
{
|
||||
key: obj.key,
|
||||
version: obj.version,
|
||||
data: (yield obj.toJSON())
|
||||
data: obj.toJSON()
|
||||
}
|
||||
]
|
||||
);
|
||||
|
|
|
@ -105,7 +105,7 @@ describe("Zotero.Sync.Data.Local", function() {
|
|||
for (let type of types) {
|
||||
let objectsClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType(type);
|
||||
let obj = yield createDataObject(type);
|
||||
let data = yield obj.toJSON();
|
||||
let data = obj.toJSON();
|
||||
data.key = obj.key;
|
||||
data.version = 10;
|
||||
let json = {
|
||||
|
@ -130,7 +130,7 @@ describe("Zotero.Sync.Data.Local", function() {
|
|||
|
||||
var type = 'item';
|
||||
let obj = yield createDataObject(type, { version: 5 });
|
||||
let data = yield obj.toJSON();
|
||||
let data = obj.toJSON();
|
||||
yield Zotero.Sync.Data.Local.saveCacheObjects(
|
||||
type, libraryID, [data]
|
||||
);
|
||||
|
@ -165,7 +165,7 @@ describe("Zotero.Sync.Data.Local", function() {
|
|||
|
||||
for (let type of types) {
|
||||
let obj = yield createDataObject(type, { version: 5 });
|
||||
let data = yield obj.toJSON();
|
||||
let data = obj.toJSON();
|
||||
yield Zotero.Sync.Data.Local.saveCacheObjects(
|
||||
type, libraryID, [data]
|
||||
);
|
||||
|
@ -175,7 +175,7 @@ describe("Zotero.Sync.Data.Local", function() {
|
|||
let objectsClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType(type);
|
||||
|
||||
let obj = yield createDataObject(type, { version: 10 });
|
||||
let data = yield obj.toJSON();
|
||||
let data = obj.toJSON();
|
||||
yield Zotero.Sync.Data.Local.saveCacheObjects(
|
||||
type, libraryID, [data]
|
||||
);
|
||||
|
@ -222,11 +222,8 @@ describe("Zotero.Sync.Data.Local", function() {
|
|||
yield Zotero.Sync.Data.Local.processSyncCacheForObjectType(
|
||||
libraryID, 'item', { stopOnError: true }
|
||||
);
|
||||
var id = Zotero.Items.getIDFromLibraryAndKey(libraryID, key);
|
||||
assert.equal(
|
||||
(yield Zotero.Sync.Storage.Local.getSyncState(id)),
|
||||
Zotero.Sync.Storage.Local.SYNC_STATE_TO_DOWNLOAD
|
||||
);
|
||||
var item = Zotero.Items.getByLibraryAndKey(libraryID, key);
|
||||
assert.equal(item.attachmentSyncState, Zotero.Sync.Storage.Local.SYNC_STATE_TO_DOWNLOAD);
|
||||
})
|
||||
|
||||
it("should mark updated attachment items for download", function* () {
|
||||
|
@ -239,18 +236,13 @@ describe("Zotero.Sync.Data.Local", function() {
|
|||
yield item.saveTx();
|
||||
|
||||
// Set file as synced
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.Sync.Storage.Local.setSyncedModificationTime(
|
||||
item.id, (yield item.attachmentModificationTime)
|
||||
);
|
||||
yield Zotero.Sync.Storage.Local.setSyncedHash(
|
||||
item.id, (yield item.attachmentHash)
|
||||
);
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "in_sync");
|
||||
});
|
||||
item.attachmentSyncedModificationTime = yield item.attachmentModificationTime;
|
||||
item.attachmentSyncedHash = yield item.attachmentHash;
|
||||
item.attachmentSyncState = "in_sync";
|
||||
yield item.saveTx({ skipAll: true });
|
||||
|
||||
// Simulate download of version with updated attachment
|
||||
var json = yield item.toResponseJSON();
|
||||
var json = item.toResponseJSON();
|
||||
json.version = 10;
|
||||
json.data.version = 10;
|
||||
json.data.md5 = '57f8a4fda823187b91e1191487b87fe6';
|
||||
|
@ -263,10 +255,7 @@ describe("Zotero.Sync.Data.Local", function() {
|
|||
libraryID, 'item', { stopOnError: true }
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
(yield Zotero.Sync.Storage.Local.getSyncState(item.id)),
|
||||
Zotero.Sync.Storage.Local.SYNC_STATE_TO_DOWNLOAD
|
||||
);
|
||||
assert.equal(item.attachmentSyncState, Zotero.Sync.Storage.Local.SYNC_STATE_TO_DOWNLOAD);
|
||||
})
|
||||
|
||||
it("should ignore attachment metadata when resolving metadata conflict", function* () {
|
||||
|
@ -276,19 +265,14 @@ describe("Zotero.Sync.Data.Local", function() {
|
|||
var item = yield importFileAttachment('test.png');
|
||||
item.version = 5;
|
||||
yield item.saveTx();
|
||||
var json = yield item.toResponseJSON();
|
||||
var json = item.toResponseJSON();
|
||||
yield Zotero.Sync.Data.Local.saveCacheObjects('item', libraryID, [json]);
|
||||
|
||||
// Set file as synced
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.Sync.Storage.Local.setSyncedModificationTime(
|
||||
item.id, (yield item.attachmentModificationTime)
|
||||
);
|
||||
yield Zotero.Sync.Storage.Local.setSyncedHash(
|
||||
item.id, (yield item.attachmentHash)
|
||||
);
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "in_sync");
|
||||
});
|
||||
item.attachmentSyncedModificationTime = yield item.attachmentModificationTime;
|
||||
item.attachmentSyncedHash = yield item.attachmentHash;
|
||||
item.attachmentSyncState = "in_sync";
|
||||
yield item.saveTx({ skipAll: true });
|
||||
|
||||
// Modify title locally, leaving item unsynced
|
||||
var newTitle = Zotero.Utilities.randomString();
|
||||
|
@ -307,10 +291,7 @@ describe("Zotero.Sync.Data.Local", function() {
|
|||
);
|
||||
|
||||
assert.equal(item.getField('title'), newTitle);
|
||||
assert.equal(
|
||||
(yield Zotero.Sync.Storage.Local.getSyncState(item.id)),
|
||||
Zotero.Sync.Storage.Local.SYNC_STATE_TO_DOWNLOAD
|
||||
);
|
||||
assert.equal(item.attachmentSyncState, Zotero.Sync.Storage.Local.SYNC_STATE_TO_DOWNLOAD);
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -348,7 +329,7 @@ describe("Zotero.Sync.Data.Local", function() {
|
|||
)
|
||||
}
|
||||
);
|
||||
let jsonData = yield obj.toJSON();
|
||||
let jsonData = obj.toJSON();
|
||||
jsonData.key = obj.key;
|
||||
jsonData.version = 10;
|
||||
let json = {
|
||||
|
@ -426,7 +407,7 @@ describe("Zotero.Sync.Data.Local", function() {
|
|||
)
|
||||
}
|
||||
);
|
||||
let jsonData = yield obj.toJSON();
|
||||
let jsonData = obj.toJSON();
|
||||
jsonData.key = obj.key;
|
||||
jsonData.version = 10;
|
||||
let json = {
|
||||
|
@ -496,7 +477,7 @@ describe("Zotero.Sync.Data.Local", function() {
|
|||
|
||||
// Create object, generate JSON, and delete
|
||||
var obj = yield createDataObject(type, { version: 10 });
|
||||
var jsonData = yield obj.toJSON();
|
||||
var jsonData = obj.toJSON();
|
||||
var key = jsonData.key = obj.key;
|
||||
jsonData.version = 10;
|
||||
let json = {
|
||||
|
@ -544,7 +525,7 @@ describe("Zotero.Sync.Data.Local", function() {
|
|||
|
||||
// Create object, generate JSON, and delete
|
||||
var obj = yield createDataObject(type, { version: 10 });
|
||||
var jsonData = yield obj.toJSON();
|
||||
var jsonData = obj.toJSON();
|
||||
var key = jsonData.key = obj.key;
|
||||
jsonData.version = 10;
|
||||
let json = {
|
||||
|
@ -576,7 +557,6 @@ describe("Zotero.Sync.Data.Local", function() {
|
|||
|
||||
obj = objectsClass.getByLibraryAndKey(libraryID, key);
|
||||
assert.ok(obj);
|
||||
yield obj.loadItemData();
|
||||
assert.equal(obj.getField('title'), jsonData.title);
|
||||
})
|
||||
|
||||
|
@ -594,7 +574,7 @@ describe("Zotero.Sync.Data.Local", function() {
|
|||
obj.setNote("");
|
||||
obj.version = 10;
|
||||
yield obj.saveTx();
|
||||
var jsonData = yield obj.toJSON();
|
||||
var jsonData = obj.toJSON();
|
||||
var key = jsonData.key = obj.key;
|
||||
let json = {
|
||||
key: obj.key,
|
||||
|
@ -626,7 +606,6 @@ describe("Zotero.Sync.Data.Local", function() {
|
|||
|
||||
obj = objectsClass.getByLibraryAndKey(libraryID, key);
|
||||
assert.ok(obj);
|
||||
yield obj.loadNote();
|
||||
assert.equal(obj.getNote(), noteText2);
|
||||
})
|
||||
})
|
||||
|
|
|
@ -175,7 +175,7 @@ describe("Zotero.Translate", function() {
|
|||
let newItems = yield saveItemsThroughTranslator("import", saveItems);
|
||||
let savedItems = {};
|
||||
for (let i=0; i<newItems.length; i++) {
|
||||
let savedItem = yield newItems[i].toJSON();
|
||||
let savedItem = newItems[i].toJSON();
|
||||
savedItems[Zotero.ItemTypes.getName(newItems[i].itemTypeID)] = savedItem;
|
||||
delete savedItem.dateAdded;
|
||||
delete savedItem.dateModified;
|
||||
|
|
|
@ -179,7 +179,7 @@ describe("Zotero.Utilities", function() {
|
|||
|
||||
let fromZoteroItem;
|
||||
try {
|
||||
fromZoteroItem = yield Zotero.Utilities.itemToCSLJSON(item);
|
||||
fromZoteroItem = Zotero.Utilities.itemToCSLJSON(item);
|
||||
} catch(e) {
|
||||
assert.fail(e, null, 'accepts Zotero Item');
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ describe("Zotero.Utilities", function() {
|
|||
let fromExportItem;
|
||||
try {
|
||||
fromExportItem = Zotero.Utilities.itemToCSLJSON(
|
||||
yield Zotero.Utilities.Internal.itemToExportFormat(item)
|
||||
Zotero.Utilities.Internal.itemToExportFormat(item)
|
||||
);
|
||||
} catch(e) {
|
||||
assert.fail(e, null, 'accepts Zotero export item');
|
||||
|
@ -205,7 +205,7 @@ describe("Zotero.Utilities", function() {
|
|||
note.setNote('Some note longer than 50 characters, which will become the title.');
|
||||
yield note.saveTx();
|
||||
|
||||
let cslJSONNote = yield Zotero.Utilities.itemToCSLJSON(note);
|
||||
let cslJSONNote = Zotero.Utilities.itemToCSLJSON(note);
|
||||
assert.equal(cslJSONNote.type, 'article', 'note is exported as "article"');
|
||||
assert.equal(cslJSONNote.title, note.getNoteTitle(), 'note title is set to Zotero pseudo-title');
|
||||
}));
|
||||
|
@ -221,7 +221,7 @@ describe("Zotero.Utilities", function() {
|
|||
|
||||
yield attachment.saveTx();
|
||||
|
||||
let cslJSONAttachment = yield Zotero.Utilities.itemToCSLJSON(attachment);
|
||||
let cslJSONAttachment = Zotero.Utilities.itemToCSLJSON(attachment);
|
||||
assert.equal(cslJSONAttachment.type, 'article', 'attachment is exported as "article"');
|
||||
assert.equal(cslJSONAttachment.title, 'Empty', 'attachment title is correct');
|
||||
assert.deepEqual(cslJSONAttachment.accessed, {"date-parts":[["2001",2,3]]}, 'attachment access date is mapped correctly');
|
||||
|
@ -240,27 +240,27 @@ describe("Zotero.Utilities", function() {
|
|||
item.setField('extra', 'PMID: 12345\nPMCID:123456');
|
||||
yield item.saveTx();
|
||||
|
||||
let cslJSON = yield Zotero.Utilities.itemToCSLJSON(item);
|
||||
let cslJSON = Zotero.Utilities.itemToCSLJSON(item);
|
||||
|
||||
assert.equal(cslJSON.PMID, '12345', 'PMID from Extra is mapped to PMID');
|
||||
assert.equal(cslJSON.PMCID, '123456', 'PMCID from Extra is mapped to PMCID');
|
||||
|
||||
item.setField('extra', 'PMID: 12345');
|
||||
yield item.saveTx();
|
||||
cslJSON = yield Zotero.Utilities.itemToCSLJSON(item);
|
||||
cslJSON = Zotero.Utilities.itemToCSLJSON(item);
|
||||
|
||||
assert.equal(cslJSON.PMID, '12345', 'single-line entry is extracted correctly');
|
||||
|
||||
item.setField('extra', 'some junk: note\nPMID: 12345\nstuff in-between\nPMCID: 123456\nlast bit of junk!');
|
||||
yield item.saveTx();
|
||||
cslJSON = yield Zotero.Utilities.itemToCSLJSON(item);
|
||||
cslJSON = Zotero.Utilities.itemToCSLJSON(item);
|
||||
|
||||
assert.equal(cslJSON.PMID, '12345', 'PMID from mixed Extra field is mapped to PMID');
|
||||
assert.equal(cslJSON.PMCID, '123456', 'PMCID from mixed Extra field is mapped to PMCID');
|
||||
|
||||
item.setField('extra', 'a\n PMID: 12345\nfoo PMCID: 123456');
|
||||
yield item.saveTx();
|
||||
cslJSON = yield Zotero.Utilities.itemToCSLJSON(item);
|
||||
cslJSON = Zotero.Utilities.itemToCSLJSON(item);
|
||||
|
||||
assert.isUndefined(cslJSON.PMCID, 'field label must not be preceded by other text');
|
||||
assert.isUndefined(cslJSON.PMID, 'field label must not be preceded by a space');
|
||||
|
@ -268,7 +268,7 @@ describe("Zotero.Utilities", function() {
|
|||
|
||||
item.setField('extra', 'something\npmid: 12345\n');
|
||||
yield item.saveTx();
|
||||
cslJSON = yield Zotero.Utilities.itemToCSLJSON(item);
|
||||
cslJSON = Zotero.Utilities.itemToCSLJSON(item);
|
||||
|
||||
assert.isUndefined(cslJSON.PMID, 'field labels are case-sensitive');
|
||||
}));
|
||||
|
@ -347,7 +347,7 @@ describe("Zotero.Utilities", function() {
|
|||
});
|
||||
|
||||
let item = Zotero.Items.get(data.item.id);
|
||||
let cslCreators = (yield Zotero.Utilities.itemToCSLJSON(item)).author;
|
||||
let cslCreators = Zotero.Utilities.itemToCSLJSON(item).author;
|
||||
|
||||
assert.deepEqual(cslCreators[0], creators[0].expect, 'simple name is not parsed');
|
||||
assert.deepEqual(cslCreators[1], creators[1].expect, 'name with dropping and non-dropping particles is parsed');
|
||||
|
@ -368,7 +368,7 @@ describe("Zotero.Utilities", function() {
|
|||
Zotero.Utilities.itemFromCSLJSON(item, json);
|
||||
yield item.saveTx();
|
||||
|
||||
let newJSON = yield Zotero.Utilities.itemToCSLJSON(item);
|
||||
let newJSON = Zotero.Utilities.itemToCSLJSON(item);
|
||||
|
||||
delete newJSON.id;
|
||||
delete json.id;
|
||||
|
@ -382,7 +382,7 @@ describe("Zotero.Utilities", function() {
|
|||
note.setNote('Some note longer than 50 characters, which will become the title.');
|
||||
yield note.saveTx();
|
||||
|
||||
let jsonNote = yield Zotero.Utilities.itemToCSLJSON(note);
|
||||
let jsonNote = Zotero.Utilities.itemToCSLJSON(note);
|
||||
|
||||
let item = new Zotero.Item();
|
||||
Zotero.Utilities.itemFromCSLJSON(item, jsonNote);
|
||||
|
@ -397,7 +397,7 @@ describe("Zotero.Utilities", function() {
|
|||
attachment.setNote('Note');
|
||||
yield attachment.saveTx();
|
||||
|
||||
let jsonAttachment = yield Zotero.Utilities.itemToCSLJSON(attachment);
|
||||
let jsonAttachment = Zotero.Utilities.itemToCSLJSON(attachment);
|
||||
|
||||
let item = new Zotero.Item();
|
||||
Zotero.Utilities.itemFromCSLJSON(item, jsonAttachment);
|
||||
|
|
|
@ -186,8 +186,8 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
|||
var item = new Zotero.Item("attachment");
|
||||
item.attachmentLinkMode = 'imported_file';
|
||||
item.attachmentPath = 'storage:test.txt';
|
||||
item.attachmentSyncState = "to_download";
|
||||
yield item.saveTx();
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "to_download");
|
||||
|
||||
setResponse({
|
||||
method: "GET",
|
||||
|
@ -217,8 +217,8 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
|||
var item = new Zotero.Item("attachment");
|
||||
item.attachmentLinkMode = 'imported_file';
|
||||
item.attachmentPath = 'storage:test.txt';
|
||||
item.attachmentSyncState = "to_download";
|
||||
yield item.saveTx();
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "to_download");
|
||||
|
||||
setResponse({
|
||||
method: "GET",
|
||||
|
@ -251,8 +251,8 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
|||
var item = new Zotero.Item("attachment");
|
||||
item.attachmentLinkMode = 'imported_file';
|
||||
item.attachmentPath = 'storage:test.txt';
|
||||
item.attachmentSyncState = "to_download";
|
||||
yield item.saveTx();
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "to_download");
|
||||
|
||||
setResponse({
|
||||
method: "GET",
|
||||
|
@ -298,8 +298,8 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
|||
item.attachmentPath = 'storage:' + fileName;
|
||||
// TODO: Test binary data
|
||||
var text = Zotero.Utilities.randomString();
|
||||
item.attachmentSyncState = "to_download";
|
||||
yield item.saveTx();
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "to_download");
|
||||
|
||||
// Create ZIP file containing above text file
|
||||
var tmpPath = Zotero.getTempDirectory().path;
|
||||
|
@ -447,8 +447,8 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
|||
assert.isTrue(result.syncRequired);
|
||||
|
||||
// Check local objects
|
||||
assert.equal((yield Zotero.Sync.Storage.Local.getSyncedModificationTime(item.id)), mtime);
|
||||
assert.equal((yield Zotero.Sync.Storage.Local.getSyncedHash(item.id)), hash);
|
||||
assert.equal(item.attachmentSyncedModificationTime, mtime);
|
||||
assert.equal(item.attachmentSyncedHash, hash);
|
||||
assert.isFalse(item.synced);
|
||||
})
|
||||
|
||||
|
@ -464,12 +464,9 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
|||
var syncedModTime = Date.now() - 10000;
|
||||
var syncedHash = "3a2f092dd62178eb8bbfda42e07e64da";
|
||||
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
// Set an mtime in the past
|
||||
yield Zotero.Sync.Storage.Local.setSyncedModificationTime(item.id, syncedModTime);
|
||||
// And a different hash
|
||||
yield Zotero.Sync.Storage.Local.setSyncedHash(item.id, syncedHash);
|
||||
});
|
||||
item.attachmentSyncedModificationTime = syncedModTime;
|
||||
item.attachmentSyncedHash = syncedHash;
|
||||
yield item.saveTx({ skipAll: true });
|
||||
|
||||
var mtime = yield item.attachmentModificationTime;
|
||||
var hash = yield item.attachmentHash;
|
||||
|
@ -507,8 +504,8 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
|||
assert.isFalse(result.fileSyncRequired);
|
||||
|
||||
// Check local objects
|
||||
assert.equal((yield Zotero.Sync.Storage.Local.getSyncedModificationTime(item.id)), mtime);
|
||||
assert.equal((yield Zotero.Sync.Storage.Local.getSyncedHash(item.id)), hash);
|
||||
assert.equal(item.attachmentSyncedModificationTime, mtime);
|
||||
assert.equal(item.attachmentSyncedHash, hash);
|
||||
assert.isFalse(item.synced);
|
||||
})
|
||||
|
||||
|
@ -547,8 +544,8 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
|||
assert.isFalse(result.syncRequired);
|
||||
|
||||
// Check local object
|
||||
assert.equal((yield Zotero.Sync.Storage.Local.getSyncedModificationTime(item.id)), mtime);
|
||||
assert.equal((yield Zotero.Sync.Storage.Local.getSyncedHash(item.id)), hash);
|
||||
assert.equal(item.attachmentSyncedModificationTime, mtime);
|
||||
assert.equal(item.attachmentSyncedHash, hash);
|
||||
assert.isFalse(item.synced);
|
||||
})
|
||||
|
||||
|
@ -593,15 +590,10 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
|||
// Check local object
|
||||
//
|
||||
// Item should be marked as in conflict
|
||||
assert.equal(
|
||||
(yield Zotero.Sync.Storage.Local.getSyncState(item.id)),
|
||||
Zotero.Sync.Storage.Local.SYNC_STATE_IN_CONFLICT
|
||||
);
|
||||
assert.equal(item.attachmentSyncState, Zotero.Sync.Storage.Local.SYNC_STATE_IN_CONFLICT);
|
||||
// Synced mod time should have been changed, because that's what's shown in the
|
||||
// conflict dialog
|
||||
assert.equal(
|
||||
(yield Zotero.Sync.Storage.Local.getSyncedModificationTime(item.id)), newModTime
|
||||
);
|
||||
assert.equal(item.attachmentSyncedModificationTime, newModTime);
|
||||
assert.isTrue(item.synced);
|
||||
})
|
||||
})
|
||||
|
|
|
@ -143,8 +143,8 @@ describe("Zotero.Sync.Storage.Mode.ZFS", function () {
|
|||
var item = new Zotero.Item("attachment");
|
||||
item.attachmentLinkMode = 'imported_file';
|
||||
item.attachmentPath = 'storage:test.txt';
|
||||
item.attachmentSyncState = "to_download";
|
||||
yield item.saveTx();
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "to_download");
|
||||
|
||||
this.httpd.registerPathHandler(
|
||||
`/users/1/items/${item.key}/file`,
|
||||
|
@ -175,8 +175,8 @@ describe("Zotero.Sync.Storage.Mode.ZFS", function () {
|
|||
var item = new Zotero.Item("attachment");
|
||||
item.attachmentLinkMode = 'imported_file';
|
||||
item.attachmentPath = 'storage:test.txt';
|
||||
item.attachmentSyncState = "to_download";
|
||||
yield item.saveTx();
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "to_download");
|
||||
|
||||
this.httpd.registerPathHandler(
|
||||
`/users/1/items/${item.key}/file`,
|
||||
|
@ -208,8 +208,8 @@ describe("Zotero.Sync.Storage.Mode.ZFS", function () {
|
|||
item.attachmentPath = 'storage:test.txt';
|
||||
// TODO: Test binary data
|
||||
var text = Zotero.Utilities.randomString();
|
||||
item.attachmentSyncState = "to_download";
|
||||
yield item.saveTx();
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "to_download");
|
||||
|
||||
var mtime = "1441252524905";
|
||||
var md5 = Zotero.Utilities.Internal.md5(text)
|
||||
|
@ -553,11 +553,11 @@ describe("Zotero.Sync.Storage.Mode.ZFS", function () {
|
|||
assert.isFalse(result.syncRequired);
|
||||
|
||||
// Check local objects
|
||||
assert.equal((yield Zotero.Sync.Storage.Local.getSyncedModificationTime(item1.id)), mtime1);
|
||||
assert.equal((yield Zotero.Sync.Storage.Local.getSyncedHash(item1.id)), hash1);
|
||||
assert.equal(item1.attachmentSyncedModificationTime, mtime1);
|
||||
assert.equal(item1.attachmentSyncedHash, hash1);
|
||||
assert.equal(item1.version, 10);
|
||||
assert.equal((yield Zotero.Sync.Storage.Local.getSyncedModificationTime(item2.id)), mtime2);
|
||||
assert.equal((yield Zotero.Sync.Storage.Local.getSyncedHash(item2.id)), hash2);
|
||||
assert.equal(item2.attachmentSyncedModificationTime, mtime2);
|
||||
assert.equal(item2.attachmentSyncedHash, hash2);
|
||||
assert.equal(item2.version, 15);
|
||||
})
|
||||
|
||||
|
@ -569,7 +569,7 @@ describe("Zotero.Sync.Storage.Mode.ZFS", function () {
|
|||
var item = yield Zotero.Attachments.importFromFile({ file: file });
|
||||
item.version = 5;
|
||||
yield item.saveTx();
|
||||
var json = yield item.toJSON();
|
||||
var json = item.toJSON();
|
||||
yield Zotero.Sync.Data.Local.saveCacheObject('item', item.libraryID, json);
|
||||
|
||||
var mtime = yield item.attachmentModificationTime;
|
||||
|
@ -615,8 +615,8 @@ describe("Zotero.Sync.Storage.Mode.ZFS", function () {
|
|||
assert.isFalse(result.syncRequired);
|
||||
|
||||
// Check local objects
|
||||
assert.equal((yield Zotero.Sync.Storage.Local.getSyncedModificationTime(item.id)), mtime);
|
||||
assert.equal((yield Zotero.Sync.Storage.Local.getSyncedHash(item.id)), hash);
|
||||
assert.equal(item.attachmentSyncedModificationTime, mtime);
|
||||
assert.equal(item.attachmentSyncedHash, hash);
|
||||
assert.equal(item.version, newVersion);
|
||||
})
|
||||
})
|
||||
|
@ -635,7 +635,7 @@ describe("Zotero.Sync.Storage.Mode.ZFS", function () {
|
|||
item.synced = true;
|
||||
yield item.saveTx();
|
||||
|
||||
var itemJSON = yield item.toResponseJSON();
|
||||
var itemJSON = item.toResponseJSON();
|
||||
itemJSON.data.mtime = yield item.attachmentModificationTime;
|
||||
itemJSON.data.md5 = yield item.attachmentHash;
|
||||
|
||||
|
@ -645,9 +645,8 @@ describe("Zotero.Sync.Storage.Mode.ZFS", function () {
|
|||
// storage directory was transferred, the mtime doesn't match, but the file was
|
||||
// never downloaded), but there's no difference in behavior
|
||||
var dbHash = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.Sync.Storage.Local.setSyncedHash(item.id, dbHash)
|
||||
});
|
||||
item.attachmentSyncedHash = dbHash;
|
||||
yield item.saveTx({ skipAll: true });
|
||||
|
||||
server.respond(function (req) {
|
||||
if (req.method == "POST"
|
||||
|
@ -674,10 +673,7 @@ describe("Zotero.Sync.Storage.Mode.ZFS", function () {
|
|||
var result = yield zfs._processUploadFile({
|
||||
name: item.libraryKey
|
||||
});
|
||||
yield assert.eventually.equal(
|
||||
Zotero.Sync.Storage.Local.getSyncedHash(item.id),
|
||||
(yield item.attachmentHash)
|
||||
);
|
||||
assert.equal(item.attachmentSyncedHash, (yield item.attachmentHash));
|
||||
assert.isFalse(result.localChanges);
|
||||
assert.isFalse(result.remoteChanges);
|
||||
assert.isFalse(result.syncRequired);
|
||||
|
@ -697,7 +693,7 @@ describe("Zotero.Sync.Storage.Mode.ZFS", function () {
|
|||
yield item.saveTx();
|
||||
|
||||
var fileHash = yield item.attachmentHash;
|
||||
var itemJSON = yield item.toResponseJSON();
|
||||
var itemJSON = item.toResponseJSON();
|
||||
itemJSON.data.md5 = 'aaaaaaaaaaaaaaaaaaaaaaaa'
|
||||
|
||||
server.respond(function (req) {
|
||||
|
@ -725,11 +721,8 @@ describe("Zotero.Sync.Storage.Mode.ZFS", function () {
|
|||
var result = yield zfs._processUploadFile({
|
||||
name: item.libraryKey
|
||||
});
|
||||
yield assert.eventually.isNull(Zotero.Sync.Storage.Local.getSyncedHash(item.id));
|
||||
yield assert.eventually.equal(
|
||||
Zotero.Sync.Storage.Local.getSyncState(item.id),
|
||||
Zotero.Sync.Storage.Local.SYNC_STATE_IN_CONFLICT
|
||||
);
|
||||
assert.isNull(item.attachmentSyncedHash);
|
||||
assert.equal(item.attachmentSyncState, Zotero.Sync.Storage.Local.SYNC_STATE_IN_CONFLICT);
|
||||
assert.isFalse(result.localChanges);
|
||||
assert.isFalse(result.remoteChanges);
|
||||
assert.isFalse(result.syncRequired);
|
||||
|
|
|
@ -157,8 +157,8 @@ describe("ZoteroPane", function() {
|
|||
item.attachmentPath = 'storage:test.txt';
|
||||
// TODO: Test binary data
|
||||
var text = Zotero.Utilities.randomString();
|
||||
item.attachmentSyncState = "to_download";
|
||||
yield item.saveTx();
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "to_download");
|
||||
|
||||
var mtime = "1441252524000";
|
||||
var md5 = Zotero.Utilities.Internal.md5(text)
|
||||
|
|
Loading…
Reference in a new issue