commit a949d6bf8df4c99fa313db57bdd8cb51649e6292
parent de897d287832fbd85fe0f54e13125c69cd77e86c
Author: Dan Stillman <dstillman@zotero.org>
Date: Wed, 16 Mar 2016 02:02:41 -0400
Merge branch 'deasyncification'
Diffstat:
75 files changed, 2608 insertions(+), 2112 deletions(-)
diff --git a/chrome/content/zotero/bindings/itembox.xml b/chrome/content/zotero/bindings/itembox.xml
@@ -1569,7 +1569,7 @@
this._lastTabIndex = parseInt(textbox.getAttribute('ztabindex')) - 1;
this._tabDirection = 1;
- var creator = yield Zotero.Creators.getAsync(creatorID);
+ var creator = Zotero.Creators.get(creatorID);
var otherField = creatorField == 'lastName' ? 'firstName' : 'lastName';
diff --git a/chrome/content/zotero/bindings/noteeditor.xml b/chrome/content/zotero/bindings/noteeditor.xml
@@ -417,48 +417,41 @@
</method>
<method name="updateTagsSummary">
<body><![CDATA[
- Zotero.spawn(function* () {
- var v = yield this.id('tags').summary;
-
- 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);
+ var v = this.id('tags').summary;
+
+ 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;
]]></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;
-
- 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)
+ var v = this.id('related').summary;
+
+ 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;
]]></body>
</method>
<method name="parentClick">
diff --git a/chrome/content/zotero/bindings/relatedbox.xml b/chrome/content/zotero/bindings/relatedbox.xml
@@ -74,25 +74,20 @@
<property name="summary">
<getter>
<![CDATA[
- return Zotero.spawn(function* () {
- 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);
+ var r = "";
+
+ 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;
-
- 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;
-
- 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_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);
+ <body><![CDATA[
+ var addButton = this.id('addButton');
+ addButton.hidden = !this.editable;
+
+ var rows = this.id('relatedRows');
+ while(rows.hasChildNodes())
+ rows.removeChild(rows.firstChild);
+
+ 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_LINKED_FILE:
+ type += '-link';
+ break;
+
+ case Zotero.Attachments.LINK_MODE_IMPORTED_FILE:
+ type += '-file';
+ break;
}
- 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
diff --git a/chrome/content/zotero/bindings/tagsbox.xml b/chrome/content/zotero/bindings/tagsbox.xml
@@ -91,24 +91,20 @@
<property name="summary">
<getter><![CDATA[
- return Zotero.spawn(function* () {
- 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);
+ var r = "";
+
+ 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>
@@ -141,7 +137,8 @@
return Zotero.spawn(function* () {
if (type == 'setting') {
if (ids.some(function (val) val.split("/")[1] == 'tagColors') && this.item) {
- return this.reload();
+ this.reload();
+ return;
}
}
else if (type == 'item-tag') {
@@ -198,7 +195,8 @@
}
else if (type == 'tag') {
if (event == 'modify') {
- return this.reload();
+ this.reload();
+ return;
}
}
}.bind(this));
@@ -208,41 +206,32 @@
<method name="reload">
<body><![CDATA[
- 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;
-
- this.id('addButton').hidden = !this.editable;
-
- this._tagColors = yield Zotero.Tags.getColors(this.item.libraryID)
- .tap(() => Zotero.Promise.check(this.mode));
-
- var rows = this.id('tagRows');
- while(rows.hasChildNodes()) {
- rows.removeChild(rows.firstChild);
- }
- var tags = this.item.getTags();
-
- // Sort tags alphabetically
- var collation = Zotero.getLocaleCollation();
- tags.sort(function (a, b) collation.compareString(1, a.tag, b.tag));
-
- for (let i=0; i<tags.length; i++) {
- this.addDynamicRow(tags[i], i+1);
- }
- this.updateCount(tags.length);
-
- this._reloading = false;
- this._focusField();
-
- var event = new Event('refresh');
- this.dispatchEvent(event);
- }, this);
+ Zotero.debug('Reloading tags box');
+
+ // Cancel field focusing while we're updating
+ this._reloading = true;
+
+ this.id('addButton').hidden = !this.editable;
+
+ this._tagColors = Zotero.Tags.getColors(this.item.libraryID);
+
+ var rows = this.id('tagRows');
+ while(rows.hasChildNodes()) {
+ rows.removeChild(rows.firstChild);
+ }
+ var tags = this.item.getTags();
+
+ // Sort tags alphabetically
+ var collation = Zotero.getLocaleCollation();
+ tags.sort(function (a, b) collation.compareString(1, a.tag, b.tag));
+
+ for (let i=0; i<tags.length; i++) {
+ this.addDynamicRow(tags[i], i+1);
+ }
+ this.updateCount(tags.length);
+
+ this._reloading = false;
+ this._focusField();
]]></body>
</method>
@@ -718,7 +707,7 @@
this._lastTabIndex = this.item.getTags().length;
}
- yield this.reload();
+ this.reload();
}
// Single tag at end
else {
diff --git a/chrome/content/zotero/bindings/tagselector.xml b/chrome/content/zotero/bindings/tagselector.xml
@@ -236,8 +236,7 @@
var emptyRegular = true;
var tagsBox = this.id('tags-box');
- var tagColors = yield Zotero.Tags.getColors(this.libraryID)
- .tap(() => Zotero.Promise.check(this.mode));
+ var tagColors = Zotero.Tags.getColors(this.libraryID);
// If new data, rebuild boxes
if (fetch || this._dirty) {
@@ -245,6 +244,13 @@
.tap(() => Zotero.Promise.check(this.mode));
tagsBox.textContent = "";
+ // Add colored tags that aren't already real tags
+ let regularTags = new Set(this._tags.map(tag => tag.tag));
+ let coloredTags = new Set(tagColors.keys());
+ [for (x of coloredTags) if (!regularTags.has(x)) x].forEach(x =>
+ this._tags.push(Zotero.Tags.cleanData({ tag: x }))
+ );
+
// Sort by name
let collation = Zotero.getLocaleCollation();
this._tags.sort(function (a, b) {
@@ -375,45 +381,43 @@
<method name="insertSorted">
<parameter name="tagObjs"/>
<body><![CDATA[
- return Zotero.spawn(function* () {
- var tagColors = yield Zotero.Tags.getColors(this._libraryID);
-
- var collation = Zotero.getLocaleCollation();
- tagObjs.sort(function (a, b) {
- return collation.compareString(1, a.tag, b.tag);
- });
-
- // Create tag elements in sorted order
- var tagsBox = this.id('tags-box');
- var tagElems = tagsBox.childNodes;
- var j = 0;
- loop:
- for (let i = 0; i < tagObjs.length; i++) {
- let tagObj = tagObjs[i];
- while (j < tagElems.length) {
- let elem = tagElems[j];
- let comp = collation.compareString(
- 1, tagObj.tag, elem.textContent
- );
- // If tag already exists, update type if new one is lower
- if (comp == 0) {
- let tagType = elem.getAttribute('tagType');
- if (parseInt(tagObj.type) < parseInt(tagType)) {
- elem.setAttribute('tagType', tagObj.type);
- }
- continue loop;
- }
- if (comp < 0) {
- break;
+ var tagColors = Zotero.Tags.getColors(this._libraryID);
+
+ var collation = Zotero.getLocaleCollation();
+ tagObjs.sort(function (a, b) {
+ return collation.compareString(1, a.tag, b.tag);
+ });
+
+ // Create tag elements in sorted order
+ var tagsBox = this.id('tags-box');
+ var tagElems = tagsBox.childNodes;
+ var j = 0;
+ loop:
+ for (let i = 0; i < tagObjs.length; i++) {
+ let tagObj = tagObjs[i];
+ while (j < tagElems.length) {
+ let elem = tagElems[j];
+ let comp = collation.compareString(
+ 1, tagObj.tag, elem.textContent
+ );
+ // If tag already exists, update type if new one is lower
+ if (comp == 0) {
+ let tagType = elem.getAttribute('tagType');
+ if (parseInt(tagObj.type) < parseInt(tagType)) {
+ elem.setAttribute('tagType', tagObj.type);
}
- j++;
+ continue loop;
}
- this._insertClickableTag(tagsBox, tagObj, tagElems[j]);
- this._updateClickableTag(
- tagElems[j], tagElems[j].textContent, tagColors
- );
+ if (comp < 0) {
+ break;
+ }
+ j++;
}
- }, this);
+ this._insertClickableTag(tagsBox, tagObj, tagElems[j]);
+ this._updateClickableTag(
+ tagElems[j], tagElems[j].textContent, tagColors
+ );
+ }
]]></body>
</method>
@@ -512,7 +516,7 @@
}.bind(this));
if (tagObjs.length) {
- yield this.insertSorted(tagObjs);
+ this.insertSorted(tagObjs);
}
}
// Don't add anything for item or collection-item; just update scope
@@ -671,7 +675,7 @@
// Colored tags don't need to exist, so in that case
// just rename the color setting
else {
- let color = yield Zotero.Tags.getColor(this.libraryID, oldName);
+ let color = Zotero.Tags.getColor(this.libraryID, oldName);
if (!color) {
throw new Error("Can't rename missing tag");
}
@@ -715,18 +719,6 @@
]]></body>
</method>
- <method name="getColor">
- <parameter name="tagIDs"/>
- <body><![CDATA[
- return Zotero.spawn(function* () {
- tagIDs = tagIDs.split('-');
- var name = yield Zotero.Tags.getName(tagIDs[0]);
- var colorData = yield Zotero.Tags.getColor(this.libraryID, name);
- return colorData ? colorData.color : '#000000';
- }.bind(this));
- ]]></body>
- </method>
-
<method name="_insertClickableTag">
<parameter name="tagsBox"/>
@@ -877,7 +869,7 @@
name: name
};
- var tagColors = yield Zotero.Tags.getColors(this.libraryID);
+ var tagColors = Zotero.Tags.getColors(this.libraryID);
if (tagColors.size >= Zotero.Tags.MAX_COLORED_TAGS && !tagColors.has(io.name)) {
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
diff --git a/chrome/content/zotero/duplicatesMerge.js b/chrome/content/zotero/duplicatesMerge.js
@@ -23,10 +23,13 @@
***** END LICENSE BLOCK *****
*/
+"use strict";
+
var Zotero_Duplicates_Pane = new function () {
- _items = [];
- _otherItems = [];
- _ignoreFields = ['dateAdded', 'dateModified', 'accessDate'];
+ var _masterItem;
+ var _items = [];
+ var _otherItems = [];
+ var _ignoreFields = ['dateAdded', 'dateModified', 'accessDate'];
this.setItems = function (items, displayNumItemsOnTypeError) {
var itemTypeID, oldestItem, otherItems = [];
@@ -77,15 +80,13 @@ var Zotero_Duplicates_Pane = new function () {
// Update the UI
//
- var diff = oldestItem.multiDiff(otherItems, _ignoreFields);
-
var button = document.getElementById('zotero-duplicates-merge-button');
var versionSelect = document.getElementById('zotero-duplicates-merge-version-select');
var itembox = document.getElementById('zotero-duplicates-merge-item-box');
var fieldSelect = document.getElementById('zotero-duplicates-merge-field-select');
- versionSelect.hidden = !diff;
- if (diff) {
+ var alternatives = oldestItem.multiDiff(otherItems, _ignoreFields);
+ if (alternatives) {
// Populate menulist with Date Added values from all items
var dateList = document.getElementById('zotero-duplicates-merge-original-date');
@@ -111,8 +112,8 @@ var Zotero_Duplicates_Pane = new function () {
}
button.label = Zotero.getString('pane.item.duplicates.mergeItems', (otherItems.length + 1));
- itembox.hiddenFields = diff ? [] : ['dateAdded', 'dateModified'];
- fieldSelect.hidden = !diff;
+ versionSelect.hidden = fieldSelect.hidden = !alternatives;
+ itembox.hiddenFields = alternatives ? [] : ['dateAdded', 'dateModified'];
this.setMaster(0);
@@ -130,27 +131,25 @@ var Zotero_Duplicates_Pane = new function () {
// Add master item's values to the beginning of each set of
// 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);
- if (diff) {
- let itemValues = yield item.toJSON();
- for (let i in diff) {
- diff[i].unshift(itemValues[i] !== undefined ? itemValues[i] : '');
- }
- itembox.fieldAlternatives = diff;
+ var alternatives = item.multiDiff(_otherItems, _ignoreFields);
+ if (alternatives) {
+ let itemValues = item.toJSON();
+ for (let i in alternatives) {
+ alternatives[i].unshift(itemValues[i] !== undefined ? itemValues[i] : '');
}
-
- var newItem = yield item.copy();
- yield newItem.loadItemData();
- yield newItem.loadCreators();
- itembox.item = newItem;
- });
+ itembox.fieldAlternatives = alternatives;
+ }
+
+ _masterItem = item;
+ itembox.item = item.clone();
}
- this.merge = function () {
+ this.merge = Zotero.Promise.coroutine(function* () {
var itembox = document.getElementById('zotero-duplicates-merge-item-box');
Zotero.CollectionTreeCache.clear();
- Zotero.Items.merge(itembox.item, _otherItems);
- }
+ // Update master item with any field alternatives from the item box
+ _masterItem.fromJSON(itembox.item.toJSON());
+ Zotero.Items.merge(_masterItem, _otherItems);
+ });
}
diff --git a/chrome/content/zotero/fileInterface.js b/chrome/content/zotero/fileInterface.js
@@ -126,6 +126,7 @@ var Zotero_File_Interface = new function() {
this.exportItems = exportItems;
this.bibliographyFromCollection = bibliographyFromCollection;
this.bibliographyFromItems = bibliographyFromItems;
+ this.copyItemsToClipboard = copyItemsToClipboard;
this.copyCitationToClipboard = copyCitationToClipboard;
/**
@@ -407,7 +408,7 @@ var Zotero_File_Interface = new function() {
*
* Does not check that items are actual references (and not notes or attachments)
*/
- this.copyItemsToClipboard = Zotero.Promise.coroutine(function* (items, style, locale, asHTML, asCitations) {
+ function copyItemsToClipboard(items, style, locale, asHTML, asCitations) {
// copy to clipboard
var transferable = Components.classes["@mozilla.org/widget/transferable;1"].
createInstance(Components.interfaces.nsITransferable);
@@ -417,7 +418,7 @@ var Zotero_File_Interface = new function() {
var cslEngine = style.getCiteProc(locale);
// add HTML
- var bibliography = yield Zotero.Cite.makeFormattedBibliographyOrCitationList(cslEngine, items, "html", asCitations);
+ var bibliography = Zotero.Cite.makeFormattedBibliographyOrCitationList(cslEngine, items, "html", asCitations);
var str = Components.classes["@mozilla.org/supports-string;1"].
createInstance(Components.interfaces.nsISupportsString);
str.data = bibliography;
@@ -427,7 +428,7 @@ var Zotero_File_Interface = new function() {
// add text (or HTML source)
if(!asHTML) {
cslEngine = style.getCiteProc(locale);
- var bibliography = yield Zotero.Cite.makeFormattedBibliographyOrCitationList(cslEngine, items, "text", asCitations);
+ var bibliography = Zotero.Cite.makeFormattedBibliographyOrCitationList(cslEngine, items, "text", asCitations);
}
var str = Components.classes["@mozilla.org/supports-string;1"].
createInstance(Components.interfaces.nsISupportsString);
@@ -436,7 +437,7 @@ var Zotero_File_Interface = new function() {
transferable.setTransferData("text/unicode", str, bibliography.length*2);
clipboardService.setData(transferable, null, Components.interfaces.nsIClipboard.kGlobalClipboard);
- });
+ }
/*
@@ -483,7 +484,7 @@ var Zotero_File_Interface = new function() {
/*
* Shows bibliography options and creates a bibliography
*/
- let _doBibliographyOptions = Zotero.Promise.coroutine(function* (name, items) {
+ function _doBibliographyOptions(name, items) {
// make sure at least one item is not a standalone note or attachment
var haveRegularItem = false;
for each(var item in items) {
@@ -515,12 +516,12 @@ var Zotero_File_Interface = new function() {
// generate bibliography
try {
if(io.method == 'copy-to-clipboard') {
- yield Zotero_File_Interface.copyItemsToClipboard(items, io.style, locale, false, io.mode === "citations");
+ Zotero_File_Interface.copyItemsToClipboard(items, io.style, locale, false, io.mode === "citations");
}
else {
var style = Zotero.Styles.get(io.style);
var cslEngine = style.getCiteProc(locale);
- var bibliography = yield Zotero.Cite.makeFormattedBibliographyOrCitationList(cslEngine,
+ var bibliography = Zotero.Cite.makeFormattedBibliographyOrCitationList(cslEngine,
items, format, io.mode === "citations");
}
} catch(e) {
@@ -598,7 +599,7 @@ var Zotero_File_Interface = new function() {
fStream.close();
}
}
- });
+ }
function _saveBibliography(name, format) {
diff --git a/chrome/content/zotero/itemPane.js b/chrome/content/zotero/itemPane.js
@@ -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;
});
diff --git a/chrome/content/zotero/locateMenu.js b/chrome/content/zotero/locateMenu.js
@@ -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
diff --git a/chrome/content/zotero/recognizePDF.js b/chrome/content/zotero/recognizePDF.js
@@ -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]);
diff --git a/chrome/content/zotero/xpcom/api.js b/chrome/content/zotero/xpcom/api.js
@@ -56,7 +56,6 @@ Zotero.API = {
if (!col) {
throw new Error('Invalid collection ID or key');
}
- yield col.loadChildItems();
results = col.getChildItems();
break;
diff --git a/chrome/content/zotero/xpcom/attachments.js b/chrome/content/zotero/xpcom/attachments.js
@@ -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;
diff --git a/chrome/content/zotero/xpcom/cite.js b/chrome/content/zotero/xpcom/cite.js
@@ -71,9 +71,9 @@ Zotero.Cite = {
* @param {String} format The format of the output (html, text, or rtf)
* @return {String} Bibliography or item list in specified format
*/
- "makeFormattedBibliographyOrCitationList":Zotero.Promise.coroutine(function* (cslEngine, items, format, asCitationList) {
+ "makeFormattedBibliographyOrCitationList":function(cslEngine, items, format, asCitationList) {
cslEngine.setOutputFormat(format);
- yield cslEngine.updateItems(items.map(item => item.id));
+ cslEngine.updateItems(items.map(item => item.id));
if(!asCitationList) {
var bibliography = Zotero.Cite.makeFormattedBibliography(cslEngine, format);
@@ -84,7 +84,7 @@ Zotero.Cite = {
var citations=[];
for (var i=0, ilen=items.length; i<ilen; i++) {
var item = items[i];
- var outList = yield cslEngine.appendCitationCluster({"citationItems":[{"id":item.id}], "properties":{}}, true);
+ var outList = cslEngine.appendCitationCluster({"citationItems":[{"id":item.id}], "properties":{}}, true);
for (var j=0, jlen=outList.length; j<jlen; j++) {
var citationPos = outList[j][0];
citations[citationPos] = outList[j][1];
@@ -124,7 +124,7 @@ Zotero.Cite = {
return "<\\rtf \n"+citations.join("\\\n")+"\n}";
}
}
- }),
+ },
/**
* Makes a formatted bibliography
@@ -492,54 +492,21 @@ Zotero.Cite.System = function(automaticJournalAbbreviations) {
if(automaticJournalAbbreviations) {
this.getAbbreviation = Zotero.Cite.getAbbreviation;
}
- this.items = {};
}
Zotero.Cite.System.prototype = {
/**
- * Asynchronously fetch item and convert to CSL JSON
- */
- "addItem":Zotero.Promise.coroutine(function* (zoteroItem) {
- if (typeof(zoteroItem) != "object") {
- if (this.items.hasOwnProperty(zoteroItem)) return;
- zoteroItem = yield Zotero.Items.getAsync(zoteroItem);
- }
- if (this.items.hasOwnProperty(zoteroItem.id)) return;
- let item = yield Zotero.Utilities.itemToCSLJSON(zoteroItem);
- item.id = zoteroItem.id;
-
- if (!Zotero.Prefs.get("export.citePaperJournalArticleURL")) {
- var itemType = Zotero.ItemTypes.getName(zoteroItem.itemTypeID);
- // don't return URL or accessed information for journal articles if a
- // pages field exists
- if (["journalArticle", "newspaperArticle", "magazineArticle"].indexOf(itemType) !== -1
- && item.pages
- ) {
- delete item.URL;
- delete item.accessed;
- }
- }
- this.items[item.id] = item;
- }),
-
- /**
- * Asynchronously fetch items and convert them to CSL JSON
- */
- "addItems":Zotero.Promise.coroutine(function* (items) {
- for (let item of items) {
- yield this.addItem(item);
- }
- }),
-
- /**
* citeproc-js system function for getting items
* See http://gsl-nagoya-u.net/http/pub/citeproc-doc.html#retrieveitem
* @param {String|Integer} Item ID, or string item for embedded citations
* @return {Object} citeproc-js item
*/
"retrieveItem":function retrieveItem(item) {
- let slashIndex;
- if(typeof item === "string" && (slashIndex = item.indexOf("/")) !== -1) {
+ var zoteroItem, slashIndex;
+ if(typeof item === "object" && item !== null && item instanceof Zotero.Item) {
+ //if(this._cache[item.id]) return this._cache[item.id];
+ zoteroItem = item;
+ } else if(typeof item === "string" && (slashIndex = item.indexOf("/")) !== -1) {
// is an embedded item
var sessionID = item.substr(0, slashIndex);
var session = Zotero.Integration.sessions[sessionID]
@@ -550,8 +517,36 @@ Zotero.Cite.System.prototype = {
return embeddedCitation;
}
}
+ } else {
+ // is an item ID
+ //if(this._cache[item]) return this._cache[item];
+ try {
+ zoteroItem = Zotero.Items.get(item);
+ } catch(e) {}
}
- return this.items[item];
+
+ if(!zoteroItem) {
+ throw "Zotero.Cite.System.retrieveItem called on non-item "+item;
+ }
+
+ var cslItem = Zotero.Utilities.itemToCSLJSON(zoteroItem);
+
+ // TEMP: citeproc-js currently expects the id property to be the item DB id
+ cslItem.id = zoteroItem.id;
+
+ if (!Zotero.Prefs.get("export.citePaperJournalArticleURL")) {
+ var itemType = Zotero.ItemTypes.getName(zoteroItem.itemTypeID);
+ // don't return URL or accessed information for journal articles if a
+ // pages field exists
+ if (["journalArticle", "newspaperArticle", "magazineArticle"].indexOf(itemType) !== -1
+ && zoteroItem.getField("pages")
+ ) {
+ delete cslItem.URL;
+ delete cslItem.accessed;
+ }
+ }
+
+ return cslItem;
},
/**
@@ -579,20 +574,3 @@ Zotero.Cite.System.prototype = {
return str.value;
}
};
-
-Zotero.Cite.AsyncCiteProc = function() {
- Zotero.CiteProc.CSL.Engine.apply(this, arguments);
-}
-Zotero.Cite.AsyncCiteProc.prototype = Object.create(Zotero.CiteProc.CSL.Engine.prototype);
-Zotero.Cite.AsyncCiteProc.prototype.updateItems = Zotero.Promise.coroutine(function*(items) {
- yield this.sys.addItems(items);
- Zotero.CiteProc.CSL.Engine.prototype.updateItems.call(this, items);
-});
-Zotero.Cite.AsyncCiteProc.prototype.appendCitationCluster = Zotero.Promise.coroutine(function*(citation, isRegistered) {
- if (!isRegistered) {
- for (let citationItem of citation.citationItems) {
- yield this.sys.addItem(citationItem.id);
- }
- }
- return Zotero.CiteProc.CSL.Engine.prototype.appendCitationCluster.call(this, citation, isRegistered);
-});
diff --git a/chrome/content/zotero/xpcom/collectionTreeRow.js b/chrome/content/zotero/xpcom/collectionTreeRow.js
@@ -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');
}
diff --git a/chrome/content/zotero/xpcom/collectionTreeView.js b/chrome/content/zotero/xpcom/collectionTreeView.js
@@ -67,6 +67,9 @@ Zotero.CollectionTreeView.prototype.type = 'collection';
Object.defineProperty(Zotero.CollectionTreeView.prototype, "selectedTreeRow", {
get: function () {
+ if (!this.selection || !this.selection.count) {
+ return false;
+ }
return this.getRow(this.selection.currentIndex);
}
});
@@ -156,25 +159,28 @@ Zotero.CollectionTreeView.prototype.refresh = Zotero.Promise.coroutine(function*
this._containerState = {};
}
- if (this.hideSources.indexOf('duplicates') == -1) {
- try {
- this._duplicateLibraries = Zotero.Prefs.get('duplicateLibraries').split(',').map(function (val) parseInt(val));
+ var userLibraryID = Zotero.Libraries.userLibraryID;
+
+ var readPref = function (pref) {
+ let ids = Zotero.Prefs.get(pref);
+ if (ids === "") {
+ this["_" + pref] = [];
}
- catch (e) {
- // Add to personal library by default
- Zotero.Prefs.set('duplicateLibraries', '0');
- this._duplicateLibraries = [0];
+ else {
+ if (ids === undefined || typeof ids != 'string') {
+ ids = "" + userLibraryID;
+ Zotero.Prefs.set(pref, "" + userLibraryID);
+ }
+ this["_" + pref] = ids.split(',')
+ // Convert old id and convert to int
+ .map(id => id === "0" ? userLibraryID : parseInt(id));
}
- }
+ }.bind(this);
- try {
- this._unfiledLibraries = Zotero.Prefs.get('unfiledLibraries').split(',').map(function (val) parseInt(val));
- }
- catch (e) {
- // Add to personal library by default
- Zotero.Prefs.set('unfiledLibraries', '0');
- this._unfiledLibraries = [0];
+ if (this.hideSources.indexOf('duplicates') == -1) {
+ readPref('duplicateLibraries');
}
+ readPref('unfiledLibraries');
var oldCount = this.rowCount || 0;
var newRows = [];
@@ -526,7 +532,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 +551,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
@@ -900,8 +905,7 @@ Zotero.CollectionTreeView.prototype.selectByID = Zotero.Promise.coroutine(functi
switch (type) {
case 'L':
- var found = yield this.selectLibrary(id);
- break;
+ return yield this.selectLibrary(id);
case 'C':
var found = yield this.expandToCollection(id);
@@ -913,14 +917,13 @@ Zotero.CollectionTreeView.prototype.selectByID = Zotero.Promise.coroutine(functi
break;
case 'T':
- var found = yield this.selectTrash(id);
- break;
+ return yield this.selectTrash(id);
}
- if (!found) {
+ var row = this._rowMap[type + id];
+ if (!row) {
return false;
}
- var row = this._rowMap[type + id];
this._treebox.ensureRowIsVisible(row);
yield this.selectWait(row);
@@ -940,7 +943,7 @@ Zotero.CollectionTreeView.prototype.selectLibrary = Zotero.Promise.coroutine(fun
}
// Check if library is already selected
- if (this.selection.currentIndex != -1) {
+ if (this.selection && this.selection.count && this.selection.currentIndex != -1) {
var treeRow = this.getRow(this.selection.currentIndex);
if (treeRow.isLibrary(true) && treeRow.ref.libraryID == libraryID) {
this._treebox.ensureRowIsVisible(this.selection.currentIndex);
@@ -972,7 +975,7 @@ Zotero.CollectionTreeView.prototype.selectSearch = function (id) {
Zotero.CollectionTreeView.prototype.selectTrash = Zotero.Promise.coroutine(function* (libraryID) {
// Check if trash is already selected
- if (this.selection.currentIndex != -1) {
+ if (this.selection && this.selection.count && this.selection.currentIndex != -1) {
let itemGroup = this.getRow(this.selection.currentIndex);
if (itemGroup.isTrash() && itemGroup.ref.libraryID == libraryID) {
this._treebox.ensureRowIsVisible(this.selection.currentIndex);
@@ -1196,6 +1199,8 @@ Zotero.CollectionTreeView.prototype._expandRow = Zotero.Promise.coroutine(functi
* Returns libraryID or FALSE if not a library
*/
Zotero.CollectionTreeView.prototype.getSelectedLibraryID = function() {
+ if (!this.selection || !this.selection.count || this.selection.currentIndex == -1) return false;
+
var treeRow = this.getRow(this.selection.currentIndex);
return treeRow && treeRow.ref && treeRow.ref.libraryID !== undefined
&& treeRow.ref.libraryID;
@@ -1504,10 +1509,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 +1628,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 +1693,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 +1717,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 +1732,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 +1862,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;
}
diff --git a/chrome/content/zotero/xpcom/data/collection.js b/chrome/content/zotero/xpcom/data/collection.js
@@ -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);
}
}
diff --git a/chrome/content/zotero/xpcom/data/collections.js b/chrome/content/zotero/xpcom/data/collections.js
@@ -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);
diff --git a/chrome/content/zotero/xpcom/data/creators.js b/chrome/content/zotero/xpcom/data/creators.js
@@ -30,29 +30,35 @@ Zotero.Creators = new function() {
var _cache = {};
+ this.init = Zotero.Promise.coroutine(function* () {
+ 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;
});
diff --git a/chrome/content/zotero/xpcom/data/dataObject.js b/chrome/content/zotero/xpcom/data/dataObject.js
@@ -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,23 +1162,19 @@ 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) {
- if (!this._id) {
- throw new Error(`${this._ObjectType} must be saved before running toJSON()`);
- }
-
var env = { options };
env.mode = options.mode || 'new';
if (env.mode == 'patch') {
@@ -1272,32 +1216,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;
-}
diff --git a/chrome/content/zotero/xpcom/data/dataObjects.js b/chrome/content/zotero/xpcom/data/dataObjects.js
@@ -337,6 +337,254 @@ 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.loadAll = Zotero.Promise.coroutine(function* (libraryID, ids) {
+ var t = new Date();
+ var libraryName = Zotero.Libraries.get(libraryID).name;
+
+ Zotero.debug("Loading "
+ + (ids ? ids.length : "all") + " "
+ + (ids && ids.length == 1 ? this._ZDO_object : 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
*
* @param {Array|Number} ids - An id or array of ids
@@ -557,25 +805,23 @@ 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 = {};
-
+
// If library isn't an integer (presumably false or null), skip it
if (parseInt(libraryID) != libraryID) {
libraryID = false;
}
-
+
if (libraryID === false && !ids) {
throw new Error("Either libraryID or ids must be provided");
}
-
+
if (libraryID !== false && this._loadedLibraries[libraryID]) {
return loaded;
}
-
+
var sql = this.primaryDataSQL;
var params = [];
if (libraryID !== false) {
@@ -585,7 +831,7 @@ Zotero.DataObjects.prototype._load = Zotero.Promise.coroutine(function* (library
if (ids) {
sql += ' AND O.' + this._ZDO_id + ' IN (' + ids.join(',') + ')';
}
-
+
var t = new Date();
yield Zotero.DB.queryAsync(
sql,
@@ -599,7 +845,7 @@ Zotero.DataObjects.prototype._load = Zotero.Promise.coroutine(function* (library
rowObj[columns[i]] = row.getResultByIndex(i);
}
var obj;
-
+
// Existing object -- reload in place
if (this._objectCache[id]) {
this._objectCache[id].loadFromRow(rowObj, true);
@@ -618,10 +864,10 @@ Zotero.DataObjects.prototype._load = Zotero.Promise.coroutine(function* (library
}
);
Zotero.debug("Loaded " + this._ZDO_objects + " in " + ((new Date) - t) + "ms");
-
+
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];
@@ -632,15 +878,17 @@ Zotero.DataObjects.prototype._load = Zotero.Promise.coroutine(function* (library
this.unload(obj.id);
}
}
-
+
if (this._postLoad) {
this._postLoad(libraryID, ids);
}
}
-
+
return loaded;
});
+
+
Zotero.DataObjects.prototype._getObjectForRow = function(row) {
return new Zotero[this._ZDO_Object];
};
diff --git a/chrome/content/zotero/xpcom/data/item.js b/chrome/content/zotero/xpcom/data/item.js
@@ -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;
@@ -658,10 +663,16 @@ Zotero.Item.prototype.setField = function(field, value, loadIn) {
switch (field) {
case 'itemTypeID':
- case 'dateAdded':
break;
+ case 'dateAdded':
case 'dateModified':
+ // Accept ISO dates
+ if (Zotero.Date.isISODate(value)) {
+ let d = Zotero.Date.isoToDate(value);
+ value = Zotero.Date.dateToSQL(d, true);
+ }
+
// Make sure it's valid
let date = Zotero.Date.sqlToDate(value, true);
if (!date) throw new Error("Invalid SQL date: " + value);
@@ -785,11 +796,18 @@ Zotero.Item.prototype.setField = function(field, value, loadIn) {
}
// Validate access date
else if (fieldID == Zotero.ItemFields.getID('accessDate')) {
- if (value && (!Zotero.Date.isSQLDate(value) &&
- !Zotero.Date.isSQLDateTime(value) &&
- value != 'CURRENT_TIMESTAMP')) {
- Zotero.debug("Discarding invalid accessDate '" + value + "' in Item.setField()");
- return false;
+ if (value && value != 'CURRENT_TIMESTAMP') {
+ // Accept ISO dates
+ if (Zotero.Date.isISODate(value)) {
+ let d = Zotero.Date.isoToDate(value);
+ value = Zotero.Date.dateToSQL(d, true);
+ }
+
+ if (!Zotero.Date.isSQLDate(value) && !Zotero.Date.isSQLDateTime(value)) {
+ Zotero.logError(`Discarding invalid ${field} '${value}' for `
+ + `item ${this.libraryKey} in setField()`);
+ return false;
+ }
}
}
@@ -831,6 +849,105 @@ Zotero.Item.prototype.getDisplayTitle = function (includeAuthorAndDate) {
}
+/**
+ * Update the generated display title from the loaded data
+ */
+Zotero.Item.prototype.updateDisplayTitle = function () {
+ var title = this.getField('title', false, true);
+ var itemTypeID = this.itemTypeID;
+ var itemTypeName = Zotero.ItemTypes.getName(itemTypeID);
+
+ if (title === "" && (itemTypeID == 8 || itemTypeID == 10)) { // 'letter' and 'interview' itemTypeIDs
+ var creatorsData = this.getCreators();
+ var authors = [];
+ var participants = [];
+ for (let i=0; i<creatorsData.length; i++) {
+ let creatorData = creatorsData[i];
+ let creatorTypeID = creatorsData[i].creatorTypeID;
+ if ((itemTypeID == 8 && creatorTypeID == 16) || // 'letter'
+ (itemTypeID == 10 && creatorTypeID == 7)) { // 'interview'
+ participants.push(creatorData);
+ }
+ else if ((itemTypeID == 8 && creatorTypeID == 1) || // 'letter'/'author'
+ (itemTypeID == 10 && creatorTypeID == 6)) { // 'interview'/'interviewee'
+ authors.push(creatorData);
+ }
+ }
+
+ var strParts = [];
+ if (participants.length > 0) {
+ let names = [];
+ let max = Math.min(4, participants.length);
+ for (let i=0; i<max; i++) {
+ names.push(
+ participants[i].name !== undefined
+ ? participants[i].name
+ : participants[i].lastName
+ );
+ }
+ switch (names.length) {
+ case 1:
+ var str = 'oneParticipant';
+ break;
+
+ case 2:
+ var str = 'twoParticipants';
+ break;
+
+ case 3:
+ var str = 'threeParticipants';
+ break;
+
+ default:
+ var str = 'manyParticipants';
+ }
+ strParts.push(Zotero.getString('pane.items.' + itemTypeName + '.' + str, names));
+ }
+ else {
+ strParts.push(Zotero.ItemTypes.getLocalizedString(itemTypeID));
+ }
+
+ title = '[' + strParts.join('; ') + ']';
+ }
+ else if (itemTypeID == 17) { // 'case' itemTypeID
+ if (title) { // common law cases always have case names
+ var reporter = this.getField('reporter');
+ if (reporter) {
+ title = title + ' (' + reporter + ')';
+ } else {
+ var court = this.getField('court');
+ if (court) {
+ title = title + ' (' + court + ')';
+ }
+ }
+ }
+ else { // civil law cases have only shortTitle as case name
+ var strParts = [];
+ var caseinfo = "";
+
+ var part = this.getField('court');
+ if (part) {
+ strParts.push(part);
+ }
+
+ part = Zotero.Date.multipartToSQL(this.getField('date', true, true));
+ if (part) {
+ strParts.push(part);
+ }
+
+ var creatorData = this.getCreator(0);
+ if (creatorData && creatorData.creatorTypeID === 1) { // author
+ strParts.push(creatorData.lastName);
+ }
+
+ title = '[' + strParts.join(', ') + ']';
+ }
+ }
+
+ this._displayTitle = title;
+};
+
+
/*
* Returns the number of creators for this item
*/
@@ -1318,9 +1435,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 +1567,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 +1591,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);
@@ -1812,7 +1935,9 @@ Zotero.Item.prototype.setNote = function(text) {
this._hasNote = text !== '';
this._noteText = text;
this._noteTitle = Zotero.Notes.noteToTitle(text);
- this._displayTitle = this._noteTitle;
+ if (this.isNote()) {
+ this._displayTitle = this._noteTitle;
+ }
this._markFieldChange('note', oldText);
this._changed.note = true;
@@ -2296,10 +2421,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 +2804,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 +2817,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 +2830,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 +2846,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 +2989,7 @@ Zotero.defineProperty(Zotero.Item.prototype, 'attachmentHash', {
});
+
/**
* Return plain text of attachment content
*
@@ -3337,13 +3543,12 @@ 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;
}
- var tagColors = yield Zotero.Tags.getColors(this.libraryID);
+ var tagColors = Zotero.Tags.getColors(this.libraryID);
var colorData = [];
for (let i=0; i<tags.length; i++) {
let tag = tags[i];
@@ -3512,34 +3717,30 @@ 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;
for (let i = 0; i < otherItems.length; i++) {
- let otherItem = otherItems[i];
- let diff = [];
- let otherData = yield otherItem.toJSON();
- let numDiffs = this.ObjectsClass.diff(thisData, otherData, diff);
-
- if (numDiffs) {
- for (let field in diff[1]) {
- if (ignoreFields && ignoreFields.indexOf(field) != -1) {
- continue;
- }
-
- var value = diff[1][field];
-
- if (!alternatives[field]) {
- hasDiffs = true;
- alternatives[field] = [value];
- }
- else if (alternatives[field].indexOf(value) == -1) {
- hasDiffs = true;
- alternatives[field].push(value);
- }
+ let otherData = otherItems[i].toJSON();
+ let changeset = Zotero.DataObjectUtilities.diff(thisData, otherData, ignoreFields);
+
+ for (let i = 0; i < changeset.length; i++) {
+ let change = changeset[i];
+
+ if (change.op == 'delete') {
+ continue;
+ }
+
+ if (!alternatives[change.field]) {
+ hasDiffs = true;
+ alternatives[change.field] = [change.value];
+ }
+ else if (alternatives[change.field].indexOf(change.value) == -1) {
+ hasDiffs = true;
+ alternatives[change.field].push(change.value);
}
}
}
@@ -3549,7 +3750,7 @@ Zotero.Item.prototype.multiDiff = Zotero.Promise.coroutine(function* (otherItems
}
return alternatives;
-});
+};
/**
@@ -3561,15 +3762,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 +3778,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 +3786,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,29 +3810,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;
-});
-
-
-/**
- * @return {Promise<Zotero.Item>} - A copy of the item with primary data loaded
- */
-Zotero.Item.prototype.copy = Zotero.Promise.coroutine(function* () {
- var newItem = new Zotero.Item;
- newItem.id = this.id;
- yield newItem.loadPrimaryData();
- return newItem;
-});;
+}
Zotero.Item.prototype._eraseData = Zotero.Promise.coroutine(function* (env) {
@@ -3721,8 +3904,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 +4048,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 +4058,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 +4067,6 @@ Zotero.Item.prototype.toJSON = Zotero.Promise.coroutine(function* (options = {})
// Creators
if (this.isRegularItem()) {
- yield this.loadCreators()
obj.creators = this.getCreatorsJSON();
}
else {
@@ -3912,18 +4091,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 +4111,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 +4118,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
@@ -3956,16 +4132,21 @@ Zotero.Item.prototype.toJSON = Zotero.Promise.coroutine(function* (options = {})
obj.deleted = deleted ? 1 : 0;
}
- obj.dateAdded = Zotero.Date.sqlToISO8601(this.dateAdded);
- obj.dateModified = Zotero.Date.sqlToISO8601(this.dateModified);
if (obj.accessDate) obj.accessDate = Zotero.Date.sqlToISO8601(obj.accessDate);
+ if (this.dateAdded) {
+ obj.dateAdded = Zotero.Date.sqlToISO8601(this.dateAdded);
+ }
+ if (this.dateModified) {
+ obj.dateModified = Zotero.Date.sqlToISO8601(this.dateModified);
+ }
+
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 +4164,7 @@ Zotero.Item.prototype.toResponseJSON = Zotero.Promise.coroutine(function* (optio
json.meta.numChildren = this.numChildren();
}
return json;
-})
+};
//////////////////////////////////////////////////////////////////////////////
@@ -3992,352 +4173,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;
- }
-
- var title = this.getField('title', false, true);
- var itemTypeID = this.itemTypeID;
- 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 = [];
- for (let i=0; i<creatorsData.length; i++) {
- let creatorData = creatorsData[i];
- let creatorTypeID = creatorsData[i].creatorTypeID;
- if ((itemTypeID == 8 && creatorTypeID == 16) || // 'letter'
- (itemTypeID == 10 && creatorTypeID == 7)) { // 'interview'
- participants.push(creatorData);
- }
- else if ((itemTypeID == 8 && creatorTypeID == 1) || // 'letter'/'author'
- (itemTypeID == 10 && creatorTypeID == 6)) { // 'interview'/'interviewee'
- authors.push(creatorData);
- }
- }
-
- var strParts = [];
- if (participants.length > 0) {
- let names = [];
- let max = Math.min(4, participants.length);
- for (let i=0; i<max; i++) {
- names.push(
- participants[i].name !== undefined
- ? participants[i].name
- : participants[i].lastName
- );
- }
- switch (names.length) {
- case 1:
- var str = 'oneParticipant';
- break;
-
- case 2:
- var str = 'twoParticipants';
- break;
-
- case 3:
- var str = 'threeParticipants';
- break;
-
- default:
- var str = 'manyParticipants';
- }
- strParts.push(Zotero.getString('pane.items.' + itemTypeName + '.' + str, names));
- }
- else {
- strParts.push(Zotero.ItemTypes.getLocalizedString(itemTypeID));
- }
-
- title = '[' + strParts.join('; ') + ']';
- }
- else if (itemTypeID == 17) { // 'case' itemTypeID
- if (title) { // common law cases always have case names
- var reporter = this.getField('reporter');
- if (reporter) {
- title = title + ' (' + reporter + ')';
- } else {
- var court = this.getField('court');
- if (court) {
- title = title + ' (' + court + ')';
- }
- }
- }
- else { // civil law cases have only shortTitle as case name
- var strParts = [];
- var caseinfo = "";
-
- var part = this.getField('court');
- if (part) {
- strParts.push(part);
- }
-
- part = Zotero.Date.multipartToSQL(this.getField('date', true, true));
- if (part) {
- strParts.push(part);
- }
-
- yield this.loadCreators()
- var creatorData = this.getCreator(0);
- if (creatorData && creatorData.creatorTypeID === 1) { // author
- strParts.push(creatorData.lastName);
- }
-
- title = '[' + strParts.join(', ') + ']';
- }
- }
-
- return this._displayTitle = title;
-});
-
-
-/*
- * 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
diff --git a/chrome/content/zotero/xpcom/data/items.js b/chrome/content/zotero/xpcom/data/items.js
@@ -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,195 +215,507 @@ Zotero.Items = function() {
};
- this._cachedFields = {};
- this.cacheFields = Zotero.Promise.coroutine(function* (libraryID, fields, items) {
- if (items && items.length == 0) {
- return;
- }
+ //
+ // 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 t = new Date;
+ 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);
+ let value = row.getResultByIndex(2);
+
+ //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[itemID]) {
+ missingItems[itemID] = true;
+ Zotero.logError("itemData row references nonexistent item " + itemID);
+ }
+ }
+ if (!itemFieldsCached[itemID]) {
+ itemFieldsCached[itemID] = {};
+ }
+ itemFieldsCached[itemID][fieldID] = true;
+ }.bind(this)
+ }
+ );
- fields = fields.concat();
+ 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)
+ }
+ );
- // 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);
+ var titleFieldID = Zotero.ItemFields.getID('title');
- if (items && items.length > 0) {
- yield this._load(libraryID, items);
- }
- else {
- yield this._load(libraryID);
- }
+ // 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];
- 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;
+ 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)
}
+ );
+
+ for (let i=0; i<allItemIDs.length; i++) {
+ let itemID = allItemIDs[i];
+ let item = this._objectCache[itemID];
- if (!this._cachedFields[libraryID]) {
- this._cachedFields[libraryID] = [];
- }
- this._cachedFields[libraryID].push(field);
+ // Mark as loaded
+ item._loaded.itemData = true;
+ item._clearChanged('itemData');
- 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));
- }
- }
+ // Display titles
+ item.updateDisplayTitle()
}
+ });
+
+
+ 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);
- 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() + ")";
+ // 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++;
}
- 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)
+ };
+
+ 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++;
}
- // All fields already cached
- if (!fieldIDs.length) {
- Zotero.debug('All fields already cached');
- return;
+ if (index <= maxOrderIndex) {
+ fixIncorrectIndexes(item, index, maxOrderIndex);
}
+ });
+
+
+ this._loadNotes = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
+ var notesToUpdate = [];
- var sql = "SELECT itemID FROM items WHERE libraryID=?";
- var params = [libraryID];
- var allItemIDs = yield Zotero.DB.columnQueryAsync(sql, params);
- var itemFieldsCached = {};
-
- var sql = "SELECT itemID, fieldID, value FROM items JOIN itemData USING (itemID) "
- + "JOIN itemDataValues USING (valueID) WHERE libraryID=?";
+ var sql = "SELECT itemID, note FROM items "
+ + "JOIN itemNotes USING (itemID) "
+ + "WHERE libraryID=?" + idSQL;
var params = [libraryID];
- if (items) {
- sql += " AND itemID IN (" + items.join() + ")";
- }
- sql += " AND fieldID IN (" + fieldIDs.join() + ")";
yield Zotero.DB.queryAsync(
sql,
params,
{
+ noCache: ids.length != 1,
onRow: function (row) {
let itemID = row.getResultByIndex(0);
- let fieldID = row.getResultByIndex(1);
- let value = row.getResultByIndex(2);
-
- //Zotero.debug('Setting field ' + fieldID + ' for item ' + itemID);
- if (this._objectCache[itemID]) {
- this._objectCache[itemID].setField(fieldID, value, true);
+ let item = this._objectCache[itemID];
+ if (!item) {
+ throw new Error("Item " + itemID + " not found");
}
- 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);
+ 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);
}
- if (!itemFieldsCached[itemID]) {
- itemFieldsCached[itemID] = {};
- }
- itemFieldsCached[itemID][fieldID] = true;
+ item._noteText = note ? note : '';
+ item._loaded.note = true;
+ item._clearChanged('note');
}.bind(this)
}
);
- // 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);
+ 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);
}
- // 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');
-
- // 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() + ")";
+ //
+ // 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");
}
- 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) {
- var missingItems = {};
- }
- if (!missingItems[itemID]) {
- missingItems[itemID] = true;
- Components.utils.reportError(
- "itemData row references nonexistent item " + itemID
- );
- }
- }
- }.bind(this)
+ 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");
+ }
- // Display titles
- for (let i=0; i<allItemIDs.length; i++) {
- let itemID = allItemIDs[i];
- let item = this._objectCache[itemID];
- yield item.loadDisplayTitle()
+ 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];
- Zotero.debug("Cached fields in " + ((new Date) - t) + "ms");
+ 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 +724,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 +941,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
*
diff --git a/chrome/content/zotero/xpcom/data/library.js b/chrome/content/zotero/xpcom/data/library.js
@@ -315,6 +315,16 @@ Zotero.Library.prototype._reloadFromDB = Zotero.Promise.coroutine(function* () {
this._loadDataFromRow(row);
});
+/**
+ * Load object data in this library
+ */
+Zotero.Library.prototype.loadAllDataTypes = Zotero.Promise.coroutine(function* () {
+ yield Zotero.SyncedSettings.loadAll(this.libraryID);
+ yield Zotero.Collections.loadAll(this.libraryID);
+ yield Zotero.Searches.loadAll(this.libraryID);
+ yield Zotero.Items.loadAll(this.libraryID);
+});
+
Zotero.Library.prototype.isChildObjectAllowed = function(type) {
return this._childObjectTypes.indexOf(type) != -1;
};
@@ -461,6 +471,8 @@ Zotero.Library.prototype._finalizeSave = Zotero.Promise.coroutine(function* (env
yield this._reloadFromDB();
Zotero.Libraries.register(this);
+
+ yield this.loadAllDataTypes();
}
});
diff --git a/chrome/content/zotero/xpcom/data/tags.js b/chrome/content/zotero/xpcom/data/tags.js
@@ -77,7 +77,7 @@ Zotero.Tags = new function() {
/**
- * Get all tags indexed by tagID
+ * Get all tags in library
*
* @param {Number} libraryID
* @param {Array} [types] Tag types to fetch
@@ -181,7 +181,7 @@ Zotero.Tags = new function() {
// We need to know if the old tag has a color assigned so that
// we can assign it to the new name
- var oldColorData = yield this.getColor(libraryID, oldName);
+ var oldColorData = this.getColor(libraryID, oldName);
yield Zotero.DB.executeTransaction(function* () {
var oldItemIDs = yield this.getTagItems(libraryID, oldTagID);
@@ -393,13 +393,13 @@ Zotero.Tags = new function() {
*
* @param {Integer} libraryID
* @param {String} name Tag name
- * @return {Promise} A Q promise for the tag color as a hex string (e.g., '#990000')
+ * @return {Object|false} An object containing 'color' as a hex string (e.g., '#990000') and
+ * 'position', or false if no colored tag with that name
*/
this.getColor = function (libraryID, name) {
- return this.getColors(libraryID)
- .then(function () {
- return _libraryColorsByName[libraryID].get(name) || false;
- });
+ // Cache colors
+ this.getColors(libraryID);
+ return _libraryColorsByName[libraryID].get(name) || false;
}
@@ -408,14 +408,12 @@ Zotero.Tags = new function() {
*
* @param {Integer} libraryID
* @param {Integer} position The position of the tag, starting at 0
- * @return {Promise} A promise for an object containing 'name' and 'color'
+ * @return {Object|false} An object containing 'name' and 'color', or false if no color at
+ * the given position
*/
this.getColorByPosition = function (libraryID, position) {
- return this.getColors(libraryID)
- .then(function () {
- return _libraryColors[libraryID][position]
- ? _libraryColors[libraryID][position] : false;
- });
+ this.getColors(libraryID);
+ return _libraryColors[libraryID][position] ? _libraryColors[libraryID][position] : false;
}
@@ -423,21 +421,20 @@ Zotero.Tags = new function() {
* Get colored tags within a given library
*
* @param {Integer} libraryID
- * @return {Promise<Map>} - A promise for a Map with tag names as keys and
- * objects containing 'color' and 'position' as values
+ * @return {Map} - A Map with tag names as keys and objects containing 'color' and 'position'
+ * as values
*/
- this.getColors = Zotero.Promise.coroutine(function* (libraryID) {
- if (_libraryColorsByName[libraryID]) {
- return _libraryColorsByName[libraryID];
+ this.getColors = function (libraryID) {
+ if (!libraryID) {
+ throw new Error("libraryID not provided");
}
- var tagColors = yield Zotero.SyncedSettings.get(libraryID, 'tagColors');
-
- // If the colors became available from another run
if (_libraryColorsByName[libraryID]) {
return _libraryColorsByName[libraryID];
}
+ var tagColors = Zotero.SyncedSettings.get(libraryID, 'tagColors');
+
tagColors = tagColors || [];
_libraryColors[libraryID] = tagColors;
@@ -452,7 +449,7 @@ Zotero.Tags = new function() {
}
return _libraryColorsByName[libraryID];
- });
+ };
/**
@@ -465,7 +462,7 @@ Zotero.Tags = new function() {
throw new Error("libraryID must be an integer");
}
- yield this.getColors(libraryID);
+ this.getColors(libraryID);
var tagColors = _libraryColors[libraryID];
@@ -541,7 +538,7 @@ Zotero.Tags = new function() {
delete _libraryColorsByName[libraryID];
// Get the tag colors for each library in which they were modified
- let tagColors = yield Zotero.SyncedSettings.get(libraryID, 'tagColors');
+ let tagColors = Zotero.SyncedSettings.get(libraryID, 'tagColors');
if (!tagColors) {
tagColors = [];
}
diff --git a/chrome/content/zotero/xpcom/date.js b/chrome/content/zotero/xpcom/date.js
@@ -87,6 +87,10 @@ Zotero.Date = new function(){
**/
function sqlToDate(sqldate, isUTC){
try {
+ if (!this.isSQLDate(sqldate) && !this.isSQLDateTime(sqldate)) {
+ throw new Error("Invalid date");
+ }
+
var datetime = sqldate.split(' ');
var dateparts = datetime[0].split('-');
if (datetime[1]){
@@ -98,7 +102,7 @@ Zotero.Date = new function(){
// Invalid date part
if (dateparts.length==1){
- return false;
+ throw new Error("Invalid date part");
}
if (isUTC){
@@ -699,7 +703,7 @@ Zotero.Date = new function(){
function toUnixTimestamp(date) {
if (date === null || typeof date != 'object' ||
date.constructor.name != 'Date') {
- throw ('Not a valid date in Zotero.Date.toUnixTimestamp()');
+ throw new Error(`'${date}' is not a valid date`);
}
return Math.round(date.getTime() / 1000);
}
diff --git a/chrome/content/zotero/xpcom/db.js b/chrome/content/zotero/xpcom/db.js
@@ -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) {
diff --git a/chrome/content/zotero/xpcom/duplicates.js b/chrome/content/zotero/xpcom/duplicates.js
@@ -303,13 +303,13 @@ Zotero.Duplicates.prototype._findDuplicates = Zotero.Promise.coroutine(function*
itemCreators = [];
}
}
- else {
- itemCreators.push({
- lastName: normalizeString(row.lastName),
- firstInitial: row.fieldMode == 0 ? normalizeString(row.firstName).charAt(0) : false
- });
- }
+
lastItemID = row.itemID;
+
+ itemCreators.push({
+ lastName: normalizeString(row.lastName),
+ firstInitial: row.fieldMode == 0 ? normalizeString(row.firstName).charAt(0) : false
+ });
}
// Add final item creators
if (itemCreators.length) {
diff --git a/chrome/content/zotero/xpcom/itemTreeView.js b/chrome/content/zotero/xpcom/itemTreeView.js
@@ -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;
}
@@ -170,12 +170,11 @@ Zotero.ItemTreeView.prototype.setTree = Zotero.Promise.coroutine(function* (tree
if (coloredTagsRE.test(key)) {
let libraryID = self.collectionTreeRow.ref.libraryID;
let position = parseInt(key) - 1;
- let colorData = yield Zotero.Tags.getColorByPosition(libraryID, position);
+ let colorData = Zotero.Tags.getColorByPosition(libraryID, position);
// If a color isn't assigned to this number or any
// other numbers, allow key navigation
if (!colorData) {
- let colors = yield Zotero.Tags.getColors(libraryID);
- return !colors.size;
+ return !Zotero.Tags.getColors(libraryID).size;
}
var items = self.getSelectedItems();
@@ -230,8 +229,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 +265,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 +282,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 +351,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 +373,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 +465,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 +532,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
@@ -627,7 +589,7 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
// If no quicksearch, process modifications manually
else if (!quicksearch || quicksearch.value == '')
{
- var items = yield Zotero.Items.getAsync(ids);
+ var items = Zotero.Items.get(ids);
for (let i = 0; i < items.length; i++) {
let item = items[i];
@@ -646,9 +608,16 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
let parentItemID = this.getRow(row).ref.parentItemID;
let parentIndex = this.getParentIndex(row);
- // Top-level items just have to be resorted
+ // Top-level item
if (this.isContainer(row)) {
- sort = id;
+ // If Unfiled Items and itm was added to a collection, remove from view
+ if (collectionTreeRow.isUnfiled() && item.getCollections().length) {
+ this._removeRow(row);
+ }
+ // Otherwise just resort
+ else {
+ sort = id;
+ }
}
// If item moved from top-level to under another item, remove the old row.
else if (parentIndex == -1 && parentItemID) {
@@ -679,7 +648,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 +687,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 +785,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 +814,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 +852,7 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
}
}
else {
- yield this.rememberSelection(savedSelection);
+ this.rememberSelection(savedSelection);
}
}
@@ -1159,8 +1120,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 +1139,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 +1157,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 +1180,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 +1222,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 +1249,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 +1262,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 +1282,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 +1524,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 +1533,7 @@ Zotero.ItemTreeView.prototype.sort = Zotero.Promise.coroutine(function* (itemID)
}
Zotero.debug("Sorted items list in " + (new Date - t) + " ms");
-});
+};
////////////////////////////////////////////////////////////////////////////////
@@ -1633,7 +1591,7 @@ Zotero.ItemTreeView.prototype.selectItem = Zotero.Promise.coroutine(function* (i
// Clear the quicksearch and tag selection and try again (once)
if (!noRecurse && this._ownerDocument.defaultView.ZoteroPane_Local) {
let cleared1 = yield this._ownerDocument.defaultView.ZoteroPane_Local.clearQuicksearch();
- let cleared2 = yield this._ownerDocument.defaultView.ZoteroPane_Local.clearTagSelection();
+ let cleared2 = this._ownerDocument.defaultView.ZoteroPane_Local.clearTagSelection();
if (cleared1 || cleared2) {
return this.selectItem(id, expand, true);
}
@@ -1648,7 +1606,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 +1627,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 +1794,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 +1827,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 +1845,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 +1857,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 +1866,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 +1910,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 +1930,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 +1958,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 +1966,7 @@ Zotero.ItemTreeView.prototype.expandMatchParents = Zotero.Promise.coroutine(func
//this._treebox.endUpdateBatch();
this.selection.selectEventsSuppressed = false;
}
-});
+}
Zotero.ItemTreeView.prototype.saveFirstRow = function() {
@@ -2028,18 +1985,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 +2013,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 +2021,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 +2046,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 +2349,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 +2430,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 +2889,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;
diff --git a/chrome/content/zotero/xpcom/libraryTreeView.js b/chrome/content/zotero/xpcom/libraryTreeView.js
@@ -79,7 +79,7 @@ Zotero.LibraryTreeView.prototype = {
* Return the index of the row with a given ID (e.g., "C123" for collection 123)
*
* @param {String} - Row id
- * @return {Integer}
+ * @return {Integer|false}
*/
getRowIndexByID: function (id) {
var type = "";
@@ -87,7 +87,7 @@ Zotero.LibraryTreeView.prototype = {
var type = id[0];
id = ('' + id).substr(1);
}
- return this._rowMap[type + id];
+ return this._rowMap[type + id] !== undefined ? this._rowMap[type + id] : false;
},
diff --git a/chrome/content/zotero/xpcom/schema.js b/chrome/content/zotero/xpcom/schema.js
@@ -542,7 +542,7 @@ Zotero.Schema = new function(){
var Mode = Zotero.Utilities.capitalize(mode);
var repotime = yield Zotero.File.getContentsFromURLAsync("resource://zotero/schema/repotime.txt");
- var date = Zotero.Date.sqlToDate(repotime, true);
+ var date = Zotero.Date.sqlToDate(repotime.trim(), true);
repotime = Zotero.Date.toUnixTimestamp(date);
var fileNameRE = new RegExp("^[^\.].+\\" + fileExt + "$");
diff --git a/chrome/content/zotero/xpcom/search.js b/chrome/content/zotero/xpcom/search.js
@@ -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();
@@ -822,7 +815,7 @@ Zotero.Search.prototype.search = Zotero.Promise.coroutine(function* (asTempTable
/**
* 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.
+ * If this object is identified (has an id or library/key), loadAll() must have been called.
*/
Zotero.Search.prototype.fromJSON = function (json) {
if (!json.name) {
@@ -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;
diff --git a/chrome/content/zotero/xpcom/storage.js b/chrome/content/zotero/xpcom/storage.js
@@ -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) {
diff --git a/chrome/content/zotero/xpcom/storage/storageEngine.js b/chrome/content/zotero/xpcom/storage/storageEngine.js
@@ -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({
diff --git a/chrome/content/zotero/xpcom/storage/storageLocal.js b/chrome/content/zotero/xpcom/storage/storageLocal.js
@@ -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()];
- }
-
- 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 + "'");
+ 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;
}
- 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.attachmentSyncedModificationTime = mtime;
+ item.attachmentSyncedHash = md5;
+ item.attachmentSyncState = "in_sync";
+ yield item.saveTx({ skipAll: true });
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;
diff --git a/chrome/content/zotero/xpcom/storage/streamListener.js b/chrome/content/zotero/xpcom/storage/streamListener.js
@@ -156,6 +156,8 @@ Zotero.Sync.Storage.StreamListener.prototype = {
if (!result) {
oldChannel.cancel(Components.results.NS_BINDING_ABORTED);
newChannel.cancel(Components.results.NS_BINDING_ABORTED);
+ Zotero.debug("Cancelling redirect");
+ // TODO: Prevent onStateChange error
return false;
}
}
diff --git a/chrome/content/zotero/xpcom/storage/webdav.js b/chrome/content/zotero/xpcom/storage/webdav.js
@@ -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
});
@@ -1073,7 +1072,7 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
response, ".//D:getlastmodified", { D: 'DAV:' }
);
lastModified = Zotero.Date.strToISO(lastModified);
- lastModified = Zotero.Date.sqlToDate(lastModified);
+ lastModified = Zotero.Date.sqlToDate(lastModified, true);
// Delete files older than a day before last sync time
var days = (lastSyncDate - lastModified) / 1000 / 60 / 60 / 24;
@@ -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(
diff --git a/chrome/content/zotero/xpcom/storage/zfs.js b/chrome/content/zotero/xpcom/storage/zfs.js
@@ -99,7 +99,7 @@ Zotero.Sync.Storage.Mode.ZFS.prototype = {
var header;
try {
header = "Zotero-File-Modification-Time";
- requestData.mtime = oldChannel.getResponseHeader(header);
+ requestData.mtime = parseInt(oldChannel.getResponseHeader(header));
header = "Zotero-File-MD5";
requestData.md5 = oldChannel.getResponseHeader(header);
header = "Zotero-File-Compressed";
@@ -131,15 +131,18 @@ 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");
- });
+ yield OS.File.setDates(path, null, new Date(requestData.mtime));
+ item.attachmentSyncedModificationTime = requestData.mtime;
+ if (updateHash) {
+ item.attachmentSyncedHash = requestData.md5;
+ }
+ item.attachmentSyncState = "in_sync";
+ yield item.saveTx({ skipAll: true });
+
+ deferred.resolve(new Zotero.Sync.Storage.Result({
+ localChanges: true
+ }));
+
return false;
}),
onProgress: function (a, b, c) {
@@ -261,7 +264,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 +356,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 +541,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 +770,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 +937,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
diff --git a/chrome/content/zotero/xpcom/style.js b/chrome/content/zotero/xpcom/style.js
@@ -437,7 +437,7 @@ Zotero.Styles = new function() {
yield Zotero.File.putContentsAsync(destFile, style);
yield Zotero.Styles.reinit();
-
+
// Refresh preferences windows
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
getService(Components.interfaces.nsIWindowMediator);
@@ -691,7 +691,7 @@ Zotero.Style.prototype.getCiteProc = function(locale, automaticJournalAbbreviati
}
try {
- var citeproc = new Zotero.Cite.AsyncCiteProc(
+ var citeproc = new Zotero.CiteProc.CSL.Engine(
new Zotero.Cite.System(automaticJournalAbbreviations),
xml,
locale,
@@ -832,4 +832,4 @@ Zotero.Style.prototype.remove = Zotero.Promise.coroutine(function* () {
}
return Zotero.Styles.reinit();
-});
-\ No newline at end of file
+});
diff --git a/chrome/content/zotero/xpcom/sync/syncEngine.js b/chrome/content/zotero/xpcom/sync/syncEngine.js
@@ -290,7 +290,7 @@ Zotero.Sync.Data.Engine.prototype._startDownload = Zotero.Promise.coroutine(func
}
if (objectType == 'setting') {
- let meta = yield Zotero.SyncedSettings.getMetadata(this.libraryID, key);
+ let meta = Zotero.SyncedSettings.getMetadata(this.libraryID, key);
if (!meta) {
continue;
}
@@ -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
}
diff --git a/chrome/content/zotero/xpcom/sync/syncLocal.js b/chrome/content/zotero/xpcom/sync/syncLocal.js
@@ -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;
diff --git a/chrome/content/zotero/xpcom/syncedSettings.js b/chrome/content/zotero/xpcom/syncedSettings.js
@@ -27,6 +27,8 @@
* @namespace
*/
Zotero.SyncedSettings = (function () {
+ var _cache = {};
+
//
// Public methods
//
@@ -34,47 +36,95 @@ Zotero.SyncedSettings = (function () {
idColumn: "setting",
table: "syncedSettings",
- get: Zotero.Promise.coroutine(function* (libraryID, setting) {
- var sql = "SELECT value FROM syncedSettings WHERE setting=? AND libraryID=?";
- var json = yield Zotero.DB.valueQueryAsync(sql, [setting, libraryID]);
- if (!json) {
- return false;
+ loadAll: Zotero.Promise.coroutine(function* (libraryID) {
+ Zotero.debug("Loading synced settings for library " + libraryID);
+
+ if (!_cache[libraryID]) {
+ _cache[libraryID] = {};
}
- return JSON.parse(json);
+
+ var invalid = [];
+
+ var sql = "SELECT setting, value, synced, version FROM syncedSettings "
+ + "WHERE libraryID=?";
+ yield Zotero.DB.queryAsync(
+ sql,
+ libraryID,
+ {
+ onRow: function (row) {
+ var setting = row.getResultByIndex(0);
+
+ var value = row.getResultByIndex(1);
+ try {
+ value = JSON.parse(value);
+ }
+ catch (e) {
+ invalid.push([libraryID, setting]);
+ return;
+ }
+
+ _cache[libraryID][setting] = {
+ value,
+ synced: !!row.getResultByIndex(2),
+ version: row.getResultByIndex(3)
+ };
+ }
+ }
+ );
+
+ // TODO: Delete invalid settings
}),
/**
+ * Return settings object
+ *
+ * @return {Object|null}
+ */
+ get: function (libraryID, setting) {
+ if (!_cache[libraryID]) {
+ throw new Zotero.Exception.UnloadedDataException(
+ "Synced settings not loaded for library " + libraryID,
+ "syncedSettings"
+ );
+ }
+
+ if (!_cache[libraryID][setting]) {
+ return null;
+ }
+
+ return JSON.parse(JSON.stringify(_cache[libraryID][setting].value));
+ },
+
+ /**
* Used by sync and tests
*
* @return {Object} - Object with 'synced' and 'version' properties
*/
- getMetadata: Zotero.Promise.coroutine(function* (libraryID, setting) {
- var sql = "SELECT * FROM syncedSettings WHERE setting=? AND libraryID=?";
- var row = yield Zotero.DB.rowQueryAsync(sql, [setting, libraryID]);
- if (!row) {
- return false;
+ getMetadata: function (libraryID, setting) {
+ if (!_cache[libraryID]) {
+ throw new Zotero.Exception.UnloadedDataException(
+ "Synced settings not loaded for library " + libraryID,
+ "syncedSettings"
+ );
+ }
+
+ var o = _cache[libraryID][setting];
+ if (!o) {
+ return null;
}
return {
- synced: !!row.synced,
- version: row.version
+ synced: o.synced,
+ version: o.version
};
- }),
+ },
set: Zotero.Promise.coroutine(function* (libraryID, setting, value, version = 0, synced) {
if (typeof value == undefined) {
throw new Error("Value not provided");
}
- // TODO: get rid of this once we have proper affected rows handling
- var sql = "SELECT value FROM syncedSettings WHERE setting=? AND libraryID=?";
- var currentValue = yield Zotero.DB.valueQueryAsync(sql, [setting, libraryID]);
-
- // Make sure we can tell the difference between a
- // missing setting (FALSE as returned by valueQuery())
- // and a FALSE setting (FALSE as returned by JSON.parse())
- var hasCurrentValue = currentValue !== false;
-
- currentValue = JSON.parse(currentValue);
+ var currentValue = this.get(libraryID, setting);
+ var hasCurrentValue = currentValue !== null;
// Value hasn't changed
if (value === currentValue) {
@@ -93,7 +143,7 @@ Zotero.SyncedSettings = (function () {
};
}
- if (currentValue === false) {
+ if (!hasCurrentValue) {
var event = 'add';
var extraData = {};
}
@@ -102,6 +152,7 @@ Zotero.SyncedSettings = (function () {
}
synced = synced ? 1 : 0;
+ version = parseInt(version);
if (hasCurrentValue) {
var sql = "UPDATE syncedSettings SET value=?, version=?, synced=? "
@@ -117,6 +168,13 @@ Zotero.SyncedSettings = (function () {
sql, [setting, libraryID, JSON.stringify(value), version, synced]
);
}
+
+ _cache[libraryID][setting] = {
+ value,
+ synced: !!synced,
+ version
+ }
+
yield Zotero.Notifier.trigger(event, 'setting', [id], extraData);
return true;
}),
@@ -124,22 +182,16 @@ Zotero.SyncedSettings = (function () {
clear: Zotero.Promise.coroutine(function* (libraryID, setting, options) {
options = options || {};
- // TODO: get rid of this once we have proper affected rows handling
- var sql = "SELECT value FROM syncedSettings WHERE setting=? AND libraryID=?";
- var currentValue = yield Zotero.DB.valueQueryAsync(sql, [setting, libraryID]);
- if (currentValue === false) {
- return false;
- }
- currentValue = JSON.parse(currentValue);
+ var currentValue = this.get(libraryID, setting);
+ var hasCurrentValue = currentValue !== null;
var id = libraryID + '/' + setting;
var extraData = {};
extraData[id] = {
- changed: {}
- };
- extraData[id].changed = {
- value: currentValue
+ changed: {
+ value: currentValue
+ }
};
if (options.skipDeleteLog) {
extraData[id].skipDeleteLog = true;
@@ -148,6 +200,8 @@ Zotero.SyncedSettings = (function () {
var sql = "DELETE FROM syncedSettings WHERE setting=? AND libraryID=?";
yield Zotero.DB.queryAsync(sql, [setting, libraryID]);
+ delete _cache[libraryID][setting];
+
yield Zotero.Notifier.trigger('delete', 'setting', [id], extraData);
return true;
})
diff --git a/chrome/content/zotero/xpcom/timeline.js b/chrome/content/zotero/xpcom/timeline.js
@@ -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;
diff --git a/chrome/content/zotero/xpcom/translation/translate_item.js b/chrome/content/zotero/xpcom/translation/translate_item.js
@@ -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) {
diff --git a/chrome/content/zotero/xpcom/utilities.js b/chrome/content/zotero/xpcom/utilities.js
@@ -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];
diff --git a/chrome/content/zotero/xpcom/utilities_internal.js b/chrome/content/zotero/xpcom/utilities_internal.js
@@ -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;
},
/**
diff --git a/chrome/content/zotero/xpcom/zotero.js b/chrome/content/zotero/xpcom/zotero.js
@@ -621,11 +621,18 @@ 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) {
+ let library = Zotero.Libraries.get(libraryID);
+ yield library.loadAllDataTypes();
+ }
+
yield Zotero.QuickCopy.init();
Zotero.Items.startEmptyTrashTimer();
diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js
@@ -860,7 +860,7 @@ var ZoteroPane = new function()
});
- this.setVirtual = function (libraryID, mode, show) {
+ this.setVirtual = Zotero.Promise.coroutine(function* (libraryID, mode, show) {
switch (mode) {
case 'duplicates':
var prefKey = 'duplicateLibraries';
@@ -873,7 +873,7 @@ var ZoteroPane = new function()
break;
default:
- throw ("Invalid virtual mode '" + mode + "' in ZoteroPane.setVirtual()");
+ throw new Error("Invalid virtual mode '" + mode + "'");
}
try {
@@ -883,10 +883,6 @@ var ZoteroPane = new function()
var ids = [];
}
- if (!libraryID) {
- libraryID = Zotero.Libraries.userLibraryID;
- }
-
var newids = [];
for (let i = 0; i < ids.length; i++) {
let id = ids[i];
@@ -898,8 +894,8 @@ var ZoteroPane = new function()
if (id == libraryID && !show) {
continue;
}
- // Remove libraryIDs that no longer exist
- if (id != 0 && !Zotero.Libraries.exists(id)) {
+ // Remove libraries that no longer exist
+ if (!Zotero.Libraries.exists(id)) {
continue;
}
newids.push(id);
@@ -914,22 +910,19 @@ var ZoteroPane = new function()
Zotero.Prefs.set(prefKey, newids.join());
- this.collectionsView.refresh();
-
- // If group is closed, open it
- this.collectionsView.selectLibrary(libraryID);
- row = this.collectionsView.selection.currentIndex;
- if (!this.collectionsView.isContainerOpen(row)) {
- this.collectionsView.toggleOpenState(row);
- }
+ yield this.collectionsView.refresh();
// Select new row
if (show) {
- Zotero.Prefs.set('lastViewedFolder', lastViewedFolderID);
- var row = this.collectionsView.getLastViewedRow();
- this.collectionsView.selection.select(row);
+ yield this.collectionsView.selectByID(lastViewedFolderID);
}
- }
+ // Select library root when hiding
+ else {
+ yield this.collectionsView.selectLibrary(libraryID);
+ }
+
+ this.collectionsView.selection.selectEventsSuppressed = false;
+ });
this.openLookupWindow = Zotero.Promise.coroutine(function* () {
@@ -1294,7 +1287,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 +1317,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 +1578,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 +3631,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 +3840,7 @@ var ZoteroPane = new function()
});
- this.showPublicationsWizard = Zotero.Promise.coroutine(function* (items) {
+ this.showPublicationsWizard = function (items) {
var io = {
hasFiles: false,
hasNotes: false,
@@ -3863,14 +3852,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 +3874,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;
- });
+ };
/**
diff --git a/components/zotero-protocol-handler.js b/components/zotero-protocol-handler.js
@@ -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;
diff --git a/test/content/runtests.js b/test/content/runtests.js
@@ -137,6 +137,8 @@ function Reporter(runner) {
dump("\r" + indentStr
// Dark red X for errors
+ "\033[31;40m" + Mocha.reporters.Base.symbols.err + " [FAIL]\033[0m"
+ // Trigger bell if interactive
+ + (Zotero.noUserInput ? "" : "\007")
+ " " + test.title + "\n"
+ indentStr + " " + err.toString() + " at\n"
+ err.stack.replace(/^/gm, indentStr + " "));
diff --git a/test/content/support.js b/test/content/support.js
@@ -251,6 +251,33 @@ function waitForCallback(cb, interval, timeout) {
}
+function clickOnItemsRow(itemsView, row, button = 0) {
+ var x = {};
+ var y = {};
+ var width = {};
+ var height = {};
+ itemsView._treebox.getCoordsForCellItem(
+ row,
+ itemsView._treebox.columns.getNamedColumn('zotero-items-column-title'),
+ 'text',
+ x, y, width, height
+ );
+
+ // Select row to trigger multi-select
+ var tree = itemsView._treebox.treeBody;
+ var rect = tree.getBoundingClientRect();
+ var x = rect.left + x.value;
+ var y = rect.top + y.value;
+ tree.dispatchEvent(new MouseEvent("mousedown", {
+ clientX: x,
+ clientY: y,
+ button,
+ detail: 1
+ }));
+}
+
+
+
/**
* Get a default group used by all tests that want one, creating one if necessary
*/
@@ -352,10 +379,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 +392,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 +610,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
diff --git a/test/tests/collectionTest.js b/test/tests/collectionTest.js
@@ -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);
})
})
diff --git a/test/tests/collectionTreeViewTest.js b/test/tests/collectionTreeViewTest.js
@@ -1,12 +1,13 @@
"use strict";
describe("Zotero.CollectionTreeView", function() {
- var win, zp, cv;
+ var win, zp, cv, userLibraryID;
before(function* () {
win = yield loadZoteroPane();
zp = win.ZoteroPane;
cv = zp.collectionsView;
+ userLibraryID = Zotero.Libraries.userLibraryID;
});
beforeEach(function () {
// TODO: Add a selectCollection() function and select a collection instead?
@@ -16,31 +17,52 @@ describe("Zotero.CollectionTreeView", function() {
win.close();
});
+ describe("#refresh()", function () {
+ it("should show Duplicate Items and Unfiled Items in My Library by default", function* () {
+ Zotero.Prefs.clear('duplicateLibraries');
+ Zotero.Prefs.clear('unfiledLibraries');
+ yield cv.refresh();
+ assert.ok(cv.getRowIndexByID("D" + userLibraryID));
+ assert.ok(cv.getRowIndexByID("U" + userLibraryID));
+ assert.equal(Zotero.Prefs.get('duplicateLibraries'), "" + userLibraryID);
+ assert.equal(Zotero.Prefs.get('unfiledLibraries'), "" + userLibraryID);
+ });
+
+ it("shouldn't show Duplicate Items and Unfiled Items if hidden", function* () {
+ Zotero.Prefs.set('duplicateLibraries', "");
+ Zotero.Prefs.set('unfiledLibraries', "");
+ yield cv.refresh();
+ assert.isFalse(cv.getRowIndexByID("D" + userLibraryID));
+ assert.isFalse(cv.getRowIndexByID("U" + userLibraryID));
+ assert.strictEqual(Zotero.Prefs.get('duplicateLibraries'), "");
+ assert.strictEqual(Zotero.Prefs.get('unfiledLibraries'), "");
+ });
+ });
+
describe("collapse/expand", function () {
it("should close and open My Library repeatedly", function* () {
- var libraryID = Zotero.Libraries.userLibraryID;
- yield cv.selectLibrary(libraryID);
+ yield cv.selectLibrary(userLibraryID);
var row = cv.selection.currentIndex;
- cv.collapseLibrary(libraryID);
+ cv.collapseLibrary(userLibraryID);
var nextRow = cv.getRow(row + 1);
assert.equal(cv.selection.currentIndex, row);
assert.ok(nextRow.isSeparator());
assert.isFalse(cv.isContainerOpen(row));
- yield cv.expandLibrary(libraryID);
+ yield cv.expandLibrary(userLibraryID);
nextRow = cv.getRow(row + 1);
assert.equal(cv.selection.currentIndex, row);
assert.ok(!nextRow.isSeparator());
assert.ok(cv.isContainerOpen(row));
- cv.collapseLibrary(libraryID);
+ cv.collapseLibrary(userLibraryID);
nextRow = cv.getRow(row + 1);
assert.equal(cv.selection.currentIndex, row);
assert.ok(nextRow.isSeparator());
assert.isFalse(cv.isContainerOpen(row));
- yield cv.expandLibrary(libraryID);
+ yield cv.expandLibrary(userLibraryID);
nextRow = cv.getRow(row + 1);
assert.equal(cv.selection.currentIndex, row);
assert.ok(!nextRow.isSeparator());
@@ -74,13 +96,13 @@ describe("Zotero.CollectionTreeView", function() {
var row = cv.selection.currentIndex;
var treeRow = cv.getRow(row);
assert.ok(treeRow.isTrash());
- assert.equal(treeRow.ref.libraryID, Zotero.Libraries.userLibraryID);
+ assert.equal(treeRow.ref.libraryID, userLibraryID);
})
})
describe("#selectWait()", function () {
it("shouldn't hang if row is already selected", function* () {
- var row = cv.getRowIndexByID("T" + Zotero.Libraries.userLibraryID);
+ var row = cv.getRowIndexByID("T" + userLibraryID);
cv.selection.select(row);
yield Zotero.Promise.delay(50);
yield cv.selectWait(row);
@@ -108,7 +130,7 @@ describe("Zotero.CollectionTreeView", function() {
});
// Library should still be selected
- assert.equal(cv.getSelectedLibraryID(), Zotero.Libraries.userLibraryID);
+ assert.equal(cv.getSelectedLibraryID(), userLibraryID);
});
it("shouldn't select a new collection if skipSelect is passed", function* () {
@@ -120,7 +142,7 @@ describe("Zotero.CollectionTreeView", function() {
});
// Library should still be selected
- assert.equal(cv.getSelectedLibraryID(), Zotero.Libraries.userLibraryID);
+ assert.equal(cv.getSelectedLibraryID(), userLibraryID);
});
it("shouldn't select a modified collection", function* () {
@@ -135,7 +157,7 @@ describe("Zotero.CollectionTreeView", function() {
yield collection.saveTx();
// Modified collection should not be selected
- assert.equal(cv.getSelectedLibraryID(), Zotero.Libraries.userLibraryID);
+ assert.equal(cv.getSelectedLibraryID(), userLibraryID);
});
it("should maintain selection on a selected modified collection", function* () {
@@ -217,8 +239,8 @@ describe("Zotero.CollectionTreeView", function() {
var collectionRow = cv._rowMap["C" + collectionID];
var searchRow = cv._rowMap["S" + searchID];
- var duplicatesRow = cv._rowMap["D" + Zotero.Libraries.userLibraryID];
- var unfiledRow = cv._rowMap["U" + Zotero.Libraries.userLibraryID];
+ var duplicatesRow = cv._rowMap["D" + userLibraryID];
+ var unfiledRow = cv._rowMap["U" + userLibraryID];
assert.isAbove(searchRow, collectionRow);
// If there's a duplicates row or an unfiled row, add before those.
@@ -230,7 +252,7 @@ describe("Zotero.CollectionTreeView", function() {
assert.isBelow(searchRow, unfiledRow);
}
else {
- var trashRow = cv._rowMap["T" + Zotero.Libraries.userLibraryID];
+ var trashRow = cv._rowMap["T" + userLibraryID];
assert.isBelow(searchRow, trashRow);
}
})
@@ -238,7 +260,7 @@ describe("Zotero.CollectionTreeView", function() {
it("shouldn't select a new group", function* () {
var group = yield createGroup();
// Library should still be selected
- assert.equal(cv.getSelectedLibraryID(), Zotero.Libraries.userLibraryID);
+ assert.equal(cv.getSelectedLibraryID(), userLibraryID);
})
it("should remove a group and all children", function* () {
@@ -390,12 +412,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 +429,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]);
diff --git a/test/tests/creatorsTest.js b/test/tests/creatorsTest.js
@@ -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);
+ });
+ });
+});
diff --git a/test/tests/dataObjectTest.js b/test/tests/dataObjectTest.js
@@ -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(
diff --git a/test/tests/dataObjectUtilitiesTest.js b/test/tests/dataObjectUtilitiesTest.js
@@ -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);
diff --git a/test/tests/dateTest.js b/test/tests/dateTest.js
@@ -1,4 +1,33 @@
describe("Zotero.Date", function() {
+ describe("#sqlToDate()", function () {
+ it("should convert an SQL local date into a JS Date object", function* () {
+ var d1 = new Date();
+ var sqlDate = d1.getFullYear()
+ + '-'
+ + Zotero.Utilities.lpad(d1.getMonth() + 1, '0', 2)
+ + '-'
+ + Zotero.Utilities.lpad(d1.getDate(), '0', 2)
+ + ' '
+ + Zotero.Utilities.lpad(d1.getHours(), '0', 2)
+ + ':'
+ + Zotero.Utilities.lpad(d1.getMinutes(), '0', 2)
+ + ':'
+ + Zotero.Utilities.lpad(d1.getSeconds(), '0', 2);
+ var offset = d1.getTimezoneOffset() * 60 * 1000;
+ var d2 = Zotero.Date.sqlToDate(sqlDate);
+ assert.equal(
+ Zotero.Date.sqlToDate(sqlDate).getTime(),
+ Math.floor(new Date().getTime() / 1000) * 1000
+ );
+ })
+
+ it("should convert an SQL UTC date into a JS Date object", function* () {
+ var date = "2016-02-27 22:00:00";
+ date = Zotero.Date.sqlToDate(date, true);
+ assert.equal(date.getTime(), 1456610400000);
+ })
+ })
+
describe("#isISODate()", function () {
it("should determine whether a date is an ISO 8601 date", function () {
assert.ok(Zotero.Date.isISODate("2015"));
diff --git a/test/tests/duplicatesTest.js b/test/tests/duplicatesTest.js
@@ -0,0 +1,56 @@
+"use strict";
+
+describe("Duplicate Items", function () {
+ var win, zp, cv;
+
+ beforeEach(function* () {
+ Zotero.Prefs.clear('duplicateLibraries');
+ win = yield loadZoteroPane();
+ zp = win.ZoteroPane;
+ cv = zp.collectionsView;
+
+ return selectLibrary(win);
+ })
+ after(function () {
+ if (win) {
+ win.close();
+ }
+ });
+
+ describe("Merging", function () {
+ it("should merge two items in duplicates view", function* () {
+ var item1 = yield createDataObject('item', { setTitle: true });
+ var item2 = item1.clone();
+ yield item2.saveTx();
+ var uri2 = Zotero.URI.getItemURI(item2);
+
+ var userLibraryID = Zotero.Libraries.userLibraryID;
+
+ var selected = yield cv.selectByID('D' + userLibraryID);
+ assert.ok(selected);
+ yield waitForItemsLoad(win);
+
+ // Select the first item, which should select both
+ var iv = zp.itemsView;
+ var row = iv.getRowIndexByID(item1.id);
+ assert.isNumber(row);
+ clickOnItemsRow(iv, row);
+ assert.equal(iv.selection.count, 2);
+
+ // Click merge button
+ var button = win.document.getElementById('zotero-duplicates-merge-button');
+ button.click();
+
+ yield waitForNotifierEvent('refresh', 'trash');
+
+ // Items should be gone
+ assert.isFalse(iv.getRowIndexByID(item1.id));
+ assert.isFalse(iv.getRowIndexByID(item2.id));
+ assert.isTrue(item2.deleted);
+ var rels = item1.getRelations();
+ var pred = Zotero.Relations.replacedItemPredicate;
+ assert.property(rels, pred);
+ assert.equal(rels[pred], uri2);
+ });
+ });
+});
diff --git a/test/tests/fileInterfaceTest.js b/test/tests/fileInterfaceTest.js
@@ -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;
diff --git a/test/tests/itemTest.js b/test/tests/itemTest.js
@@ -135,13 +135,52 @@ describe("Zotero.Item", function () {
item = yield Zotero.Items.getAsync(id);
assert.equal(item.getField("versionNumber"), "1.0");
});
+
+ it("should accept ISO 8601 dates", function* () {
+ var fields = {
+ accessDate: "2015-06-07T20:56:00Z",
+ dateAdded: "2015-06-07T20:57:00Z",
+ dateModified: "2015-06-07T20:58:00Z",
+ };
+ var item = createUnsavedDataObject('item');
+ for (let i in fields) {
+ item.setField(i, fields[i]);
+ }
+ assert.equal(item.getField('accessDate'), '2015-06-07 20:56:00');
+ assert.equal(item.dateAdded, '2015-06-07 20:57:00');
+ assert.equal(item.dateModified, '2015-06-07 20:58:00');
+ })
+
+ it("should accept SQL dates", function* () {
+ var fields = {
+ accessDate: "2015-06-07 20:56:00",
+ dateAdded: "2015-06-07 20:57:00",
+ dateModified: "2015-06-07 20:58:00",
+ };
+ var item = createUnsavedDataObject('item');
+ for (let i in fields) {
+ item.setField(i, fields[i]);
+ item.getField(i, fields[i]);
+ }
+ })
+
+ it("should ignore unknown accessDate values", function* () {
+ var fields = {
+ accessDate: "foo"
+ };
+ var item = createUnsavedDataObject('item');
+ for (let i in fields) {
+ item.setField(i, fields[i]);
+ }
+ assert.strictEqual(item.getField('accessDate'), '');
+ })
})
describe("#dateAdded", 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 +223,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 +237,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 +260,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 +389,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 +412,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);
})
})
@@ -614,11 +648,8 @@ describe("Zotero.Item", function () {
// File should be flagged for upload
// DEBUG: Is this necessary?
- assert.equal(
- (yield Zotero.Sync.Storage.Local.getSyncState(item.id)),
- Zotero.Sync.Storage.Local.SYNC_STATE_TO_UPLOAD
- );
- assert.isNull(yield Zotero.Sync.Storage.Local.getSyncedHash(item.id));
+ assert.equal(item.attachmentSyncState, Zotero.Sync.Storage.Local.SYNC_STATE_TO_UPLOAD);
+ assert.isNull(item.attachmentSyncedHash);
})
})
@@ -686,8 +717,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 +733,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 +750,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));
@@ -825,6 +853,38 @@ describe("Zotero.Item", function () {
})
})
+
+ describe("#multiDiff", function () {
+ it("should return set of alternatives for differing fields in other items", function* () {
+ var type = 'item';
+
+ var dates = ['2016-03-08 17:44:45'];
+ var accessDates = ['2016-03-08T18:44:45Z'];
+ var urls = ['http://www.example.com', 'http://example.net'];
+
+ var obj1 = createUnsavedDataObject(type);
+ obj1.setField('date', '2016-03-07 12:34:56'); // different in 1 and 3, not in 2
+ obj1.setField('url', 'http://example.com'); // different in all three
+ obj1.setField('title', 'Test'); // only in 1
+
+ var obj2 = createUnsavedDataObject(type);
+ obj2.setField('url', urls[0]);
+ obj2.setField('accessDate', accessDates[0]); // only in 2
+
+ var obj3 = createUnsavedDataObject(type);
+ obj3.setField('date', dates[0]);
+ obj3.setField('url', urls[1]);
+
+ var alternatives = obj1.multiDiff([obj2, obj3]);
+
+ assert.sameMembers(Object.keys(alternatives), ['url', 'date', 'accessDate']);
+ assert.sameMembers(alternatives.url, urls);
+ assert.sameMembers(alternatives.date, dates);
+ assert.sameMembers(alternatives.accessDate, accessDates);
+ });
+ });
+
+
describe("#clone()", function () {
// TODO: Expand to other data
it("should copy creators", function* () {
@@ -837,7 +897,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 +911,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 +928,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 +948,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);
@@ -905,24 +965,23 @@ describe("Zotero.Item", function () {
var mtime = new Date().getTime();
var md5 = 'b32e33f529942d73bea4ed112310f804';
- 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 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 +997,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 +1014,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 +1037,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 +1051,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);
diff --git a/test/tests/itemTreeViewTest.js b/test/tests/itemTreeViewTest.js
@@ -1,31 +1,31 @@
"use strict";
describe("Zotero.ItemTreeView", function() {
- var win, zp, itemsView, existingItemID;
+ var win, zp, cv, itemsView, existingItemID;
// Load Zotero pane and select library
before(function* () {
win = yield loadZoteroPane();
zp = win.ZoteroPane;
+ cv = zp.collectionsView;
var item = new Zotero.Item('book');
existingItemID = yield item.saveTx();
});
beforeEach(function* () {
- yield zp.collectionsView.selectLibrary();
- yield waitForItemsLoad(win)
+ yield selectLibrary(win);
itemsView = zp.itemsView;
})
after(function () {
win.close();
});
- it("shouldn't show items in trash", function* () {
+ it("shouldn't show items in trash in library root", function* () {
var item = yield createDataObject('item', { title: "foo" });
var itemID = item.id;
item.deleted = true;
yield item.saveTx();
- assert.notOk(itemsView.getRowIndexByID(itemID));
+ assert.isFalse(itemsView.getRowIndexByID(itemID));
})
describe("#selectItem()", function () {
@@ -45,6 +45,17 @@ describe("Zotero.ItemTreeView", function() {
});
})
+ describe("#getCellText()", function () {
+ it("should return new value after edit", function* () {
+ var str = Zotero.Utilities.randomString();
+ var item = yield createDataObject('item', { title: str });
+ var row = itemsView.getRowIndexByID(item.id);
+ assert.equal(itemsView.getCellText(row, { id: 'zotero-items-column-title' }), str);
+ yield modifyDataObject(item);
+ assert.notEqual(itemsView.getCellText(row, { id: 'zotero-items-column-title' }), str);
+ })
+ })
+
describe("#notify()", function () {
beforeEach(function () {
sinon.spy(win.ZoteroPane, "itemSelected");
@@ -220,6 +231,22 @@ describe("Zotero.ItemTreeView", function() {
yield Zotero.Items.erase(items.map(item => item.id));
})
+
+
+ it("should remove items from Unfiled Items when added to a collection", function* () {
+ var userLibraryID = Zotero.Libraries.userLibraryID;
+ var collection = yield createDataObject('collection');
+ var item = yield createDataObject('item', { title: "Unfiled Item" });
+ yield zp.setVirtual(userLibraryID, 'unfiled', true);
+ var selected = yield cv.selectByID("U" + userLibraryID);
+ assert.ok(selected);
+ yield waitForItemsLoad(win);
+ assert.isNumber(zp.itemsView.getRowIndexByID(item.id));
+ yield Zotero.DB.executeTransaction(function* () {
+ yield collection.addItem(item.id);
+ });
+ assert.isFalse(zp.itemsView.getRowIndexByID(item.id));
+ });
})
describe("#drop()", function () {
diff --git a/test/tests/libraryTest.js b/test/tests/libraryTest.js
@@ -150,6 +150,11 @@ describe("Zotero.Library", function() {
yield library.saveTx();
assert.isFalse(Zotero.Libraries.isEditable(library.libraryID));
});
+
+ it("should initialize library after creation", function* () {
+ let library = yield createGroup({});
+ Zotero.SyncedSettings.get(library.libraryID, "tagColors");
+ });
});
describe("#erase()", function() {
it("should erase a group library", function* () {
diff --git a/test/tests/preferences_syncTest.js b/test/tests/preferences_syncTest.js
@@ -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');
diff --git a/test/tests/relatedboxTest.js b/test/tests/relatedboxTest.js
@@ -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');
diff --git a/test/tests/searchTest.js b/test/tests/searchTest.js
@@ -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");
diff --git a/test/tests/storageLocalTest.js b/test/tests/storageLocalTest.js
@@ -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);
})
})
diff --git a/test/tests/syncEngineTest.js b/test/tests/syncEngineTest.js
@@ -239,10 +239,10 @@ describe("Zotero.Sync.Data.Engine", function () {
assert.equal(Zotero.Libraries.getVersion(userLibraryID), 3);
// Make sure local objects exist
- var setting = yield Zotero.SyncedSettings.get(userLibraryID, "tagColors");
+ var setting = Zotero.SyncedSettings.get(userLibraryID, "tagColors");
assert.lengthOf(setting, 1);
assert.equal(setting[0].name, 'A');
- var settingMetadata = yield Zotero.SyncedSettings.getMetadata(userLibraryID, "tagColors");
+ var settingMetadata = Zotero.SyncedSettings.getMetadata(userLibraryID, "tagColors");
assert.equal(settingMetadata.version, 2);
assert.isTrue(settingMetadata.synced);
@@ -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()
}
]
);
@@ -814,7 +812,7 @@ describe("Zotero.Sync.Data.Engine", function () {
yield engine._startDownload();
// Make sure objects were deleted
- assert.isFalse(yield Zotero.SyncedSettings.get(userLibraryID, 'tagColors'));
+ assert.isNull(Zotero.SyncedSettings.get(userLibraryID, 'tagColors'));
assert.isFalse(Zotero.Collections.exists(collectionID));
assert.isFalse(Zotero.Searches.exists(searchID));
assert.isFalse(Zotero.Items.exists(itemID));
@@ -903,7 +901,7 @@ describe("Zotero.Sync.Data.Engine", function () {
yield engine._startDownload();
// Make sure objects weren't deleted
- assert.ok(yield Zotero.SyncedSettings.get(userLibraryID, 'tagColors'));
+ assert.ok(Zotero.SyncedSettings.get(userLibraryID, 'tagColors'));
assert.ok(Zotero.Collections.exists(collectionID));
assert.ok(Zotero.Searches.exists(searchID));
})
@@ -1214,10 +1212,10 @@ describe("Zotero.Sync.Data.Engine", function () {
yield engine._fullSync();
// Check settings
- var setting = yield Zotero.SyncedSettings.get(userLibraryID, "tagColors");
+ var setting = Zotero.SyncedSettings.get(userLibraryID, "tagColors");
assert.lengthOf(setting, 1);
assert.equal(setting[0].name, 'A');
- var settingMetadata = yield Zotero.SyncedSettings.getMetadata(userLibraryID, "tagColors");
+ var settingMetadata = Zotero.SyncedSettings.getMetadata(userLibraryID, "tagColors");
assert.equal(settingMetadata.version, 2);
assert.isTrue(settingMetadata.synced);
diff --git a/test/tests/syncLocalTest.js b/test/tests/syncLocalTest.js
@@ -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);
})
})
diff --git a/test/tests/tagSelectorTest.js b/test/tests/tagSelectorTest.js
@@ -4,7 +4,7 @@ describe("Tag Selector", function () {
var win, doc, collectionsView;
var clearTagColors = Zotero.Promise.coroutine(function* (libraryID) {
- var tagColors = yield Zotero.Tags.getColors(libraryID);
+ var tagColors = Zotero.Tags.getColors(libraryID);
for (let name of tagColors.keys()) {
yield Zotero.Tags.setColor(libraryID, name, false);
}
@@ -155,6 +155,18 @@ describe("Tag Selector", function () {
assert.equal(getRegularTags().length, 1);
})
+ it("should show a colored tag at the top of the list even when linked to no items", function* () {
+ var libraryID = Zotero.Libraries.userLibraryID;
+
+ var tagSelector = doc.getElementById('zotero-tag-selector');
+ var tagElems = tagSelector.id('tags-box').childNodes;
+ var count = tagElems.length;
+
+ yield Zotero.Tags.setColor(libraryID, "Top", '#AAAAAA');
+
+ assert.equal(tagElems.length, count + 1);
+ });
+
it("shouldn't re-insert a new tag that matches an existing color", function* () {
var libraryID = Zotero.Libraries.userLibraryID;
diff --git a/test/tests/tagsTest.js b/test/tests/tagsTest.js
@@ -64,4 +64,38 @@ describe("Zotero.Tags", function () {
assert.isFalse(yield Zotero.Tags.getName(tagID));
})
})
+
+
+ describe("#setColor()", function () {
+ var libraryID;
+
+ before(function* () {
+ libraryID = Zotero.Libraries.userLibraryID;
+
+ // Clear library tag colors
+ var colors = Zotero.Tags.getColors(libraryID);
+ for (let color of colors.keys()) {
+ yield Zotero.Tags.setColor(libraryID, color);
+ }
+ });
+
+ it("should set color for a tag", function* () {
+ var aColor = '#ABCDEF';
+ var bColor = '#BCDEF0';
+ yield Zotero.Tags.setColor(libraryID, "A", aColor);
+ yield Zotero.Tags.setColor(libraryID, "B", bColor);
+
+ var o = Zotero.Tags.getColor(libraryID, "A")
+ assert.equal(o.color, aColor);
+ assert.equal(o.position, 0);
+ var o = Zotero.Tags.getColor(libraryID, "B")
+ assert.equal(o.color, bColor);
+ assert.equal(o.position, 1);
+
+ var o = Zotero.SyncedSettings.get(libraryID, 'tagColors');
+ assert.isArray(o);
+ assert.lengthOf(o, 2);
+ assert.sameMembers(o.map(c => c.color), [aColor, bColor]);
+ });
+ });
})
diff --git a/test/tests/tagsboxTest.js b/test/tests/tagsboxTest.js
@@ -14,17 +14,6 @@ describe("Item Tags Box", function () {
win.close();
});
- function waitForTagsBox() {
- var deferred = Zotero.Promise.defer();
- var tagsbox = doc.getElementById('zotero-editpane-tags');
- var onRefresh = function (event) {
- tagsbox.removeEventListener('refresh', onRefresh);
- deferred.resolve();
- }
- tagsbox.addEventListener('refresh', onRefresh);
- return deferred.promise;
- }
-
describe("#notify()", function () {
it("should update an existing tag on rename", function* () {
var tag = Zotero.Utilities.randomString();
@@ -43,7 +32,6 @@ describe("Item Tags Box", function () {
var tabbox = doc.getElementById('zotero-view-tabbox');
tabbox.selectedIndex = 2;
- yield waitForTagsBox();
var tagsbox = doc.getElementById('zotero-editpane-tags');
var rows = tagsbox.id('tagRows').getElementsByTagName('row');
assert.equal(rows.length, 1);
@@ -77,7 +65,6 @@ describe("Item Tags Box", function () {
var tabbox = doc.getElementById('zotero-view-tabbox');
tabbox.selectedIndex = 2;
- yield waitForTagsBox();
var tagsbox = doc.getElementById('zotero-editpane-tags');
var rows = tagsbox.id('tagRows').getElementsByTagName('row');
@@ -108,7 +95,6 @@ describe("Item Tags Box", function () {
var tabbox = doc.getElementById('zotero-view-tabbox');
tabbox.selectedIndex = 2;
- yield waitForTagsBox();
var tagsbox = doc.getElementById('zotero-editpane-tags');
var rows = tagsbox.id('tagRows').getElementsByTagName('row');
assert.equal(rows.length, 1);
diff --git a/test/tests/translateTest.js b/test/tests/translateTest.js
@@ -64,13 +64,13 @@ function saveItemsThroughTranslator(translatorType, items) {
* Convert an array of items to an object in which they are indexed by
* their display titles
*/
-var itemsArrayToObject = Zotero.Promise.coroutine(function* itemsArrayToObject(items) {
+function itemsArrayToObject(items) {
var obj = {};
for (let item of items) {
- obj[yield item.loadDisplayTitle(true)] = item;
+ obj[item.getDisplayTitle()] = item;
}
return obj;
-});
+}
const TEST_TAGS = [
"manual tag as string",
@@ -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;
@@ -223,7 +223,7 @@ describe("Zotero.Translate", function() {
}
];
- let newItems = yield itemsArrayToObject(yield saveItemsThroughTranslator("import", myItems));
+ let newItems = itemsArrayToObject(yield saveItemsThroughTranslator("import", myItems));
let noteIDs = newItems["Test Item"].getNotes();
let note1 = yield Zotero.Items.getAsync(noteIDs[0]);
assert.equal(Zotero.ItemTypes.getName(note1.itemTypeID), "note");
@@ -261,7 +261,7 @@ describe("Zotero.Translate", function() {
'}'));
let newItems = yield translate.translate();
assert.equal(newItems.length, 3);
- newItems = yield itemsArrayToObject(newItems);
+ newItems = itemsArrayToObject(newItems);
assert.equal(newItems["Not in Collection"].getCollections().length, 0);
let parentCollection = newItems["In Parent Collection"].getCollections();
@@ -313,7 +313,7 @@ describe("Zotero.Translate", function() {
"attachments":childAttachments
});
- let newItems = yield itemsArrayToObject(yield saveItemsThroughTranslator("import", myItems));
+ let newItems = itemsArrayToObject(yield saveItemsThroughTranslator("import", myItems));
let containedAttachments = yield Zotero.Items.getAsync(newItems["Container Item"].getAttachments());
assert.equal(containedAttachments.length, 3);
@@ -447,7 +447,7 @@ describe("Zotero.Translate", function() {
let newItems = yield saveItemsThroughTranslator("web", myItems);
assert.equal(newItems.length, 1);
- let containedAttachments = yield itemsArrayToObject(yield Zotero.Items.getAsync(newItems[0].getAttachments()));
+ let containedAttachments = itemsArrayToObject(yield Zotero.Items.getAsync(newItems[0].getAttachments()));
let link = containedAttachments["Link to zotero.org"];
assert.equal(link.getField("url"), "http://www.zotero.org/");
diff --git a/test/tests/utilitiesTest.js b/test/tests/utilitiesTest.js
@@ -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');
@@ -359,6 +359,7 @@ describe("Zotero.Utilities", function() {
});
describe("itemFromCSLJSON", function () {
it("should stably perform itemToCSLJSON -> itemFromCSLJSON -> itemToCSLJSON", function* () {
+ this.timeout(10000);
let data = loadSampleData('citeProcJSExport');
for (let i in data) {
@@ -368,7 +369,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 +383,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 +398,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);
diff --git a/test/tests/webdavTest.js b/test/tests/webdavTest.js
@@ -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);
})
})
diff --git a/test/tests/zfsTest.js b/test/tests/zfsTest.js
@@ -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,14 +553,68 @@ 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);
})
+ it("should update local info for remotely updated file that matches local file", function* () {
+ var { engine, client, caller } = yield setup();
+
+ var library = Zotero.Libraries.userLibrary;
+ library.libraryVersion = 5;
+ yield library.saveTx();
+ library.storageDownloadNeeded = true;
+
+ var file = getTestDataDirectory();
+ file.append('test.txt');
+ var item = yield Zotero.Attachments.importFromFile({ file });
+ item.version = 5;
+ item.attachmentSyncState = "to_download";
+ yield item.saveTx();
+ var path = yield item.getFilePathAsync();
+ yield OS.File.setDates(path, null, new Date() - 100000);
+
+ var json = item.toJSON();
+ yield Zotero.Sync.Data.Local.saveCacheObject('item', item.libraryID, json);
+
+ var mtime = (Math.floor(new Date().getTime() / 1000) * 1000) + "";
+ var md5 = Zotero.Utilities.Internal.md5(file)
+
+ var s3Path = `pretend-s3/${item.key}`;
+ this.httpd.registerPathHandler(
+ `/users/1/items/${item.key}/file`,
+ {
+ handle: function (request, response) {
+ if (!request.hasHeader('Zotero-API-Key')) {
+ response.setStatusLine(null, 403, "Forbidden");
+ return;
+ }
+ var key = request.getHeader('Zotero-API-Key');
+ if (key != apiKey) {
+ response.setStatusLine(null, 403, "Invalid key");
+ return;
+ }
+ response.setStatusLine(null, 302, "Found");
+ response.setHeader("Zotero-File-Modification-Time", mtime, false);
+ response.setHeader("Zotero-File-MD5", md5, false);
+ response.setHeader("Zotero-File-Compressed", "No", false);
+ response.setHeader("Location", baseURL + s3Path, false);
+ }
+ }
+ );
+ var result = yield engine.start();
+
+ assert.equal(item.attachmentSyncedModificationTime, mtime);
+ yield assert.eventually.equal(item.attachmentModificationTime, mtime);
+ assert.isTrue(result.localChanges);
+ assert.isFalse(result.remoteChanges);
+ assert.isFalse(result.syncRequired);
+ })
+
it("should update local info for file that already exists on the server", function* () {
var { engine, client, caller } = yield setup();
@@ -569,7 +623,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 +669,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 +689,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 +699,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 +727,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 +747,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 +775,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);
diff --git a/test/tests/zoteroPaneTest.js b/test/tests/zoteroPaneTest.js
@@ -1,13 +1,14 @@
"use strict";
describe("ZoteroPane", function() {
- var win, doc, zp;
+ var win, doc, zp, userLibraryID;
// Load Zotero pane and select library
before(function* () {
win = yield loadZoteroPane();
doc = win.document;
zp = win.ZoteroPane;
+ userLibraryID = Zotero.Libraries.userLibraryID;
});
after(function () {
@@ -157,8 +158,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)
@@ -201,4 +202,92 @@ describe("ZoteroPane", function() {
assert.equal((yield Zotero.File.getContentsAsync(path)), text);
})
})
+
+
+ describe("#setVirtual()", function () {
+ var cv;
+
+ before(function* () {
+ cv = zp.collectionsView;
+ });
+ beforeEach(function () {
+ Zotero.Prefs.clear('duplicateLibraries');
+ Zotero.Prefs.clear('unfiledLibraries');
+ return selectLibrary(win);
+ })
+
+ it("should show a hidden virtual folder", function* () {
+ // Create unfiled, duplicate items
+ var title = Zotero.Utilities.randomString();
+ var item1 = yield createDataObject('item', { title });
+ var item2 = yield createDataObject('item', { title });
+
+ // Start hidden
+ Zotero.Prefs.set('duplicateLibraries', "");
+ Zotero.Prefs.set('unfiledLibraries', "");
+ yield cv.refresh();
+
+ // Show Duplicate Items
+ var id = "D" + userLibraryID;
+ assert.isFalse(cv.getRowIndexByID(id));
+ yield zp.setVirtual(userLibraryID, 'duplicates', true);
+
+ // Clicking should select both items
+ var row = cv.getRowIndexByID(id);
+ assert.ok(row);
+ assert.equal(cv.selection.currentIndex, row);
+ yield waitForItemsLoad(win);
+ var iv = zp.itemsView;
+ row = iv.getRowIndexByID(item1.id);
+ assert.isNumber(row);
+ clickOnItemsRow(iv, row);
+ assert.equal(iv.selection.count, 2);
+
+ // Show Unfiled Items
+ id = "U" + userLibraryID;
+ assert.isFalse(cv.getRowIndexByID(id));
+ yield zp.setVirtual(userLibraryID, 'unfiled', true);
+ assert.ok(cv.getRowIndexByID(id));
+ });
+
+ it("should hide a virtual folder shown by default", function* () {
+ yield cv.refresh();
+
+ // Hide Duplicate Items
+ var id = "D" + userLibraryID;
+ assert.ok(yield cv.selectByID(id));
+ yield zp.setVirtual(userLibraryID, 'duplicates', false);
+ assert.isFalse(cv.getRowIndexByID(id));
+
+ // Hide Unfiled Items
+ id = "U" + userLibraryID;
+ assert.ok(yield cv.selectByID(id));
+ yield zp.setVirtual(userLibraryID, 'unfiled', false);
+ assert.isFalse(cv.getRowIndexByID(id));
+ });
+
+ it("should hide an explicitly shown virtual folder", function* () {
+ // Start shown
+ Zotero.Prefs.set('duplicateLibraries', "" + userLibraryID);
+ Zotero.Prefs.set('unfiledLibraries', "" + userLibraryID);
+ yield cv.refresh();
+
+ // Hide Duplicate Items
+ var id = "D" + userLibraryID;
+ assert.ok(yield cv.selectByID(id));
+ yield waitForItemsLoad(win);
+ yield zp.setVirtual(userLibraryID, 'duplicates', false);
+ assert.isFalse(cv.getRowIndexByID(id));
+ assert.equal(cv.getSelectedLibraryID(), userLibraryID);
+
+
+ // Hide Unfiled Items
+ id = "U" + userLibraryID;
+ assert.ok(yield cv.selectByID(id));
+ yield waitForItemsLoad(win);
+ yield zp.setVirtual(userLibraryID, 'unfiled', false);
+ assert.isFalse(cv.getRowIndexByID(id));
+ assert.equal(cv.getSelectedLibraryID(), userLibraryID);
+ });
+ });
})