Merge branch 'deasyncification'
This commit is contained in:
commit
a949d6bf8d
|
@ -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';
|
||||
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
<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;
|
||||
}
|
||||
icon.setAttribute('src','chrome://zotero/skin/treeitem-' + type + '.png');
|
||||
|
||||
var label = document.createElement("label");
|
||||
label.className = "zotero-box-label";
|
||||
label.setAttribute('value', relatedItem.getDisplayTitle());
|
||||
label.setAttribute('crop','end');
|
||||
label.setAttribute('flex','1');
|
||||
|
||||
var box = document.createElement('box');
|
||||
box.setAttribute('onclick',
|
||||
"document.getBindingParent(this).showItem('" + id + "')");
|
||||
box.setAttribute('class','zotero-clicky');
|
||||
box.setAttribute('flex','1');
|
||||
box.appendChild(icon);
|
||||
box.appendChild(label);
|
||||
|
||||
if (this.editable) {
|
||||
var remove = document.createElement("label");
|
||||
remove.setAttribute('value','-');
|
||||
remove.setAttribute('onclick',
|
||||
"document.getBindingParent(this).remove('" + id + "');");
|
||||
remove.setAttribute('class','zotero-clicky zotero-clicky-minus');
|
||||
}
|
||||
|
||||
var row = document.createElement("row");
|
||||
row.appendChild(box);
|
||||
if (this.editable) {
|
||||
row.appendChild(remove);
|
||||
}
|
||||
rows.appendChild(row);
|
||||
}
|
||||
this.updateCount(relatedKeys.length);
|
||||
icon.setAttribute('src','chrome://zotero/skin/treeitem-' + type + '.png');
|
||||
|
||||
var label = document.createElement("label");
|
||||
label.className = "zotero-box-label";
|
||||
label.setAttribute('value', relatedItem.getDisplayTitle());
|
||||
label.setAttribute('crop','end');
|
||||
label.setAttribute('flex','1');
|
||||
|
||||
var box = document.createElement('box');
|
||||
box.setAttribute('onclick',
|
||||
"document.getBindingParent(this).showItem('" + id + "')");
|
||||
box.setAttribute('class','zotero-clicky');
|
||||
box.setAttribute('flex','1');
|
||||
box.appendChild(icon);
|
||||
box.appendChild(label);
|
||||
|
||||
if (this.editable) {
|
||||
var remove = document.createElement("label");
|
||||
remove.setAttribute('value','-');
|
||||
remove.setAttribute('onclick',
|
||||
"document.getBindingParent(this).remove('" + id + "');");
|
||||
remove.setAttribute('class','zotero-clicky zotero-clicky-minus');
|
||||
}
|
||||
|
||||
var row = document.createElement("row");
|
||||
row.appendChild(box);
|
||||
if (this.editable) {
|
||||
row.appendChild(remove);
|
||||
}
|
||||
rows.appendChild(row);
|
||||
}
|
||||
}, this);
|
||||
]]>
|
||||
</body>
|
||||
this.updateCount(relatedKeys.length);
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="add">
|
||||
<body><![CDATA[
|
||||
|
@ -238,13 +223,11 @@
|
|||
}
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
for (let relItem of relItems) {
|
||||
yield this.item.loadRelations();
|
||||
if (this.item.addRelatedItem(relItem)) {
|
||||
yield this.item.save({
|
||||
skipDateModifiedUpdate: true
|
||||
});
|
||||
}
|
||||
yield relItem.loadRelations();
|
||||
if (relItem.addRelatedItem(this.item)) {
|
||||
yield relItem.save({
|
||||
skipDateModifiedUpdate: true
|
||||
|
@ -267,7 +250,6 @@
|
|||
skipDateModifiedUpdate: true
|
||||
});
|
||||
}
|
||||
yield item.loadRelations();
|
||||
if (item.removeRelatedItem(this.item)) {
|
||||
yield item.save({
|
||||
skipDateModifiedUpdate: true
|
||||
|
|
|
@ -91,24 +91,20 @@
|
|||
|
||||
<property name="summary">
|
||||
<getter><![CDATA[
|
||||
return Zotero.spawn(function* () {
|
||||
var r = "";
|
||||
|
||||
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 = "";
|
||||
|
||||
return r;
|
||||
}, this);
|
||||
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;
|
||||
]]></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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
this._insertClickableTag(tagsBox, tagObj, tagElems[j]);
|
||||
this._updateClickableTag(
|
||||
tagElems[j], tagElems[j].textContent, tagColors
|
||||
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);
|
||||
}
|
||||
continue loop;
|
||||
}
|
||||
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);
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -94,13 +94,11 @@ var ZoteroItemPane = new function() {
|
|||
_notesList.removeChild(_notesList.firstChild);
|
||||
}
|
||||
|
||||
yield item.loadChildItems();
|
||||
let notes = yield Zotero.Items.getAsync(item.getNotes());
|
||||
if (notes.length) {
|
||||
for (var i = 0; i < notes.length; i++) {
|
||||
let note = notes[i];
|
||||
let id = notes[i].id;
|
||||
yield note.loadItemData();
|
||||
|
||||
var icon = document.createElement('image');
|
||||
icon.className = "zotero-box-icon";
|
||||
|
@ -148,7 +146,6 @@ var ZoteroItemPane = new function() {
|
|||
box.mode = 'edit';
|
||||
}
|
||||
|
||||
yield Zotero.Promise.all([item.loadItemData(), item.loadCreators()]);
|
||||
box.item = item;
|
||||
});
|
||||
|
||||
|
|
|
@ -400,7 +400,6 @@ var Zotero_LocateMenu = new function() {
|
|||
}
|
||||
|
||||
if(item.isRegularItem()) {
|
||||
yield item.loadChildItems();
|
||||
var attachments = item.getAttachments();
|
||||
if(attachments) {
|
||||
// look through url fields for non-file:/// attachments
|
||||
|
|
|
@ -395,7 +395,6 @@ var Zotero_RecognizePDF = new function() {
|
|||
}
|
||||
|
||||
// put new item in same collections as the old one
|
||||
yield item.loadCollections();
|
||||
let itemCollections = item.getCollections();
|
||||
for (let i = 0; i < itemCollections.length; i++) {
|
||||
let collection = yield Zotero.Collections.getAsync(itemCollections[i]);
|
||||
|
|
|
@ -56,7 +56,6 @@ Zotero.API = {
|
|||
if (!col) {
|
||||
throw new Error('Invalid collection ID or key');
|
||||
}
|
||||
yield col.loadChildItems();
|
||||
results = col.getChildItems();
|
||||
break;
|
||||
|
||||
|
|
|
@ -1090,8 +1090,7 @@ Zotero.Attachments = new function(){
|
|||
|
||||
Zotero.DB.requireTransaction();
|
||||
|
||||
attachment.loadItemData();
|
||||
var newAttachment = yield attachment.clone(libraryID);
|
||||
var newAttachment = attachment.clone(libraryID);
|
||||
if (attachment.isImportedAttachment()) {
|
||||
// Attachment path isn't copied over by clone() if libraryID is different
|
||||
newAttachment.attachmentPath = attachment.attachmentPath;
|
||||
|
|
|
@ -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,45 +492,9 @@ 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
|
||||
|
@ -538,8 +502,11 @@ Zotero.Cite.System.prototype = {
|
|||
* @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);
|
||||
});
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
catch (e) {
|
||||
// Add to personal library by default
|
||||
Zotero.Prefs.set('duplicateLibraries', '0');
|
||||
this._duplicateLibraries = [0];
|
||||
}
|
||||
}
|
||||
var userLibraryID = Zotero.Libraries.userLibraryID;
|
||||
|
||||
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];
|
||||
var readPref = function (pref) {
|
||||
let ids = Zotero.Prefs.get(pref);
|
||||
if (ids === "") {
|
||||
this["_" + pref] = [];
|
||||
}
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -28,11 +28,8 @@ Zotero.Collection = function(params = {}) {
|
|||
|
||||
this._name = null;
|
||||
|
||||
this._hasChildCollections = null;
|
||||
this._childCollections = [];
|
||||
|
||||
this._hasChildItems = false;
|
||||
this._childItems = [];
|
||||
this._childCollections = new Set();
|
||||
this._childItems = new Set();
|
||||
|
||||
Zotero.Utilities.assignProps(this, params, ['name', 'libraryID', 'parentID',
|
||||
'parentKey', 'lastSync']);
|
||||
|
@ -162,19 +159,13 @@ Zotero.Collection.prototype.loadFromRow = function(row) {
|
|||
|
||||
|
||||
Zotero.Collection.prototype.hasChildCollections = function() {
|
||||
if (this._hasChildCollections !== null) {
|
||||
return this._hasChildCollections;
|
||||
}
|
||||
this._requireData('primaryData');
|
||||
return false;
|
||||
this._requireData('childCollections');
|
||||
return this._childCollections.size > 0;
|
||||
}
|
||||
|
||||
Zotero.Collection.prototype.hasChildItems = function() {
|
||||
if (this._hasChildItems !== null) {
|
||||
return this._hasChildItems;
|
||||
}
|
||||
this._requireData('primaryData');
|
||||
return false;
|
||||
this._requireData('childItems');
|
||||
return this._childItems.size > 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -189,19 +180,11 @@ Zotero.Collection.prototype.getChildCollections = function (asIDs) {
|
|||
|
||||
// Return collectionIDs
|
||||
if (asIDs) {
|
||||
var ids = [];
|
||||
for each(var col in this._childCollections) {
|
||||
ids.push(col.id);
|
||||
}
|
||||
return ids;
|
||||
return this._childCollections.values();
|
||||
}
|
||||
|
||||
// Return Zotero.Collection objects
|
||||
var objs = [];
|
||||
for each(var col in this._childCollections) {
|
||||
objs.push(col);
|
||||
}
|
||||
return objs;
|
||||
return Array.from(this._childCollections).map(id => this.ObjectsClass.get(id));
|
||||
}
|
||||
|
||||
|
||||
|
@ -215,13 +198,14 @@ Zotero.Collection.prototype.getChildCollections = function (asIDs) {
|
|||
Zotero.Collection.prototype.getChildItems = function (asIDs, includeDeleted) {
|
||||
this._requireData('childItems');
|
||||
|
||||
if (this._childItems.length == 0) {
|
||||
if (this._childItems.size == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Remove deleted items if necessary
|
||||
var childItems = [];
|
||||
for each(var item in this._childItems) {
|
||||
for (let itemID of this._childItems) {
|
||||
let item = this.ChildObjects.get(itemID);
|
||||
if (includeDeleted || !item.deleted) {
|
||||
childItems.push(item);
|
||||
}
|
||||
|
@ -229,19 +213,11 @@ Zotero.Collection.prototype.getChildItems = function (asIDs, includeDeleted) {
|
|||
|
||||
// Return itemIDs
|
||||
if (asIDs) {
|
||||
var ids = [];
|
||||
for each(var item in childItems) {
|
||||
ids.push(item.id);
|
||||
}
|
||||
return ids;
|
||||
return childItems.map(item => item.id);
|
||||
}
|
||||
|
||||
// Return Zotero.Item objects
|
||||
var objs = [];
|
||||
for each(var item in childItems) {
|
||||
objs.push(item);
|
||||
}
|
||||
return objs;
|
||||
return childItems.slice();
|
||||
}
|
||||
|
||||
Zotero.Collection.prototype._initSave = Zotero.Promise.coroutine(function* (env) {
|
||||
|
@ -388,7 +364,6 @@ Zotero.Collection.prototype.addItems = Zotero.Promise.coroutine(function* (itemI
|
|||
return;
|
||||
}
|
||||
|
||||
yield this.loadChildItems();
|
||||
var current = this.getChildItems(true);
|
||||
|
||||
Zotero.DB.requireTransaction();
|
||||
|
@ -400,15 +375,14 @@ Zotero.Collection.prototype.addItems = Zotero.Promise.coroutine(function* (itemI
|
|||
continue;
|
||||
}
|
||||
|
||||
let item = yield this.ChildObjects.getAsync(itemID);
|
||||
yield item.loadCollections();
|
||||
let item = this.ChildObjects.get(itemID);
|
||||
item.addToCollection(this.id);
|
||||
yield item.save({
|
||||
skipDateModifiedUpdate: true
|
||||
});
|
||||
}
|
||||
|
||||
yield this.loadChildItems(true);
|
||||
yield this._loadDataType('childItems');
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -434,7 +408,6 @@ Zotero.Collection.prototype.removeItems = Zotero.Promise.coroutine(function* (it
|
|||
return;
|
||||
}
|
||||
|
||||
yield this.loadChildItems();
|
||||
var current = this.getChildItems(true);
|
||||
|
||||
return Zotero.DB.executeTransaction(function* () {
|
||||
|
@ -447,7 +420,6 @@ Zotero.Collection.prototype.removeItems = Zotero.Promise.coroutine(function* (it
|
|||
}
|
||||
|
||||
let item = yield this.ChildObjects.getAsync(itemID);
|
||||
yield item.loadCollections();
|
||||
item.removeFromCollection(this.id);
|
||||
yield item.save({
|
||||
skipDateModifiedUpdate: true
|
||||
|
@ -455,7 +427,7 @@ Zotero.Collection.prototype.removeItems = Zotero.Promise.coroutine(function* (it
|
|||
}
|
||||
}.bind(this));
|
||||
|
||||
yield this.loadChildItems(true);
|
||||
yield this._loadDataType('childItems');
|
||||
});
|
||||
|
||||
|
||||
|
@ -464,13 +436,7 @@ Zotero.Collection.prototype.removeItems = Zotero.Promise.coroutine(function* (it
|
|||
**/
|
||||
Zotero.Collection.prototype.hasItem = function(itemID) {
|
||||
this._requireData('childItems');
|
||||
|
||||
for (let i=0; i<this._childItems.length; i++) {
|
||||
if (this._childItems[i].id == itemID) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return this._childItems.has(itemID);
|
||||
}
|
||||
|
||||
|
||||
|
@ -692,8 +658,8 @@ Zotero.Collection.prototype.fromJSON = function (json) {
|
|||
}
|
||||
|
||||
|
||||
Zotero.Collection.prototype.toResponseJSON = Zotero.Promise.coroutine(function* (options = {}) {
|
||||
var json = yield this.constructor._super.prototype.toResponseJSON.apply(this, options);
|
||||
Zotero.Collection.prototype.toResponseJSON = function (options = {}) {
|
||||
var json = this.constructor._super.prototype.toResponseJSON.apply(this, options);
|
||||
|
||||
// TODO: library block?
|
||||
|
||||
|
@ -713,10 +679,10 @@ Zotero.Collection.prototype.toResponseJSON = Zotero.Promise.coroutine(function*
|
|||
json.meta.numChildren = this.numChildren();
|
||||
}
|
||||
return json;
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
Zotero.Collection.prototype.toJSON = Zotero.Promise.coroutine(function* (options = {}) {
|
||||
Zotero.Collection.prototype.toJSON = function (options = {}) {
|
||||
var env = this._preToJSON(options);
|
||||
var mode = env.mode;
|
||||
|
||||
|
@ -729,7 +695,7 @@ Zotero.Collection.prototype.toJSON = Zotero.Promise.coroutine(function* (options
|
|||
obj.relations = {}; // TEMP
|
||||
|
||||
return this._postToJSON(env);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -865,75 +831,6 @@ Zotero.Collection.prototype.addLinkedCollection = Zotero.Promise.coroutine(funct
|
|||
//
|
||||
// Private methods
|
||||
//
|
||||
Zotero.Collection.prototype.reloadHasChildCollections = Zotero.Promise.coroutine(function* () {
|
||||
var sql = "SELECT COUNT(*) FROM collections WHERE parentCollectionID=?";
|
||||
this._hasChildCollections = !!(yield Zotero.DB.valueQueryAsync(sql, this.id));
|
||||
});
|
||||
|
||||
|
||||
Zotero.Collection.prototype.loadChildCollections = Zotero.Promise.coroutine(function* (reload) {
|
||||
if (this._loaded.childCollections && !reload) {
|
||||
return;
|
||||
}
|
||||
|
||||
var sql = "SELECT collectionID FROM collections WHERE parentCollectionID=?";
|
||||
var ids = yield Zotero.DB.columnQueryAsync(sql, this.id);
|
||||
|
||||
this._childCollections = [];
|
||||
|
||||
if (ids.length) {
|
||||
for each(var id in ids) {
|
||||
var col = yield this.ObjectsClass.getAsync(id);
|
||||
if (!col) {
|
||||
throw new Error('Collection ' + id + ' not found');
|
||||
}
|
||||
this._childCollections.push(col);
|
||||
}
|
||||
this._hasChildCollections = true;
|
||||
}
|
||||
else {
|
||||
this._hasChildCollections = false;
|
||||
}
|
||||
|
||||
this._loaded.childCollections = true;
|
||||
this._clearChanged('childCollections');
|
||||
});
|
||||
|
||||
|
||||
Zotero.Collection.prototype.reloadHasChildItems = Zotero.Promise.coroutine(function* () {
|
||||
var sql = "SELECT COUNT(*) FROM collectionItems WHERE collectionID=?";
|
||||
this._hasChildItems = !!(yield Zotero.DB.valueQueryAsync(sql, this.id));
|
||||
});
|
||||
|
||||
|
||||
Zotero.Collection.prototype.loadChildItems = Zotero.Promise.coroutine(function* (reload) {
|
||||
if (this._loaded.childItems && !reload) {
|
||||
return;
|
||||
}
|
||||
|
||||
var sql = "SELECT itemID FROM collectionItems WHERE collectionID=? "
|
||||
// DEBUG: Fix for child items created via context menu on parent within
|
||||
// a collection being added to the current collection
|
||||
+ "AND itemID NOT IN "
|
||||
+ "(SELECT itemID FROM itemNotes WHERE parentItemID IS NOT NULL) "
|
||||
+ "AND itemID NOT IN "
|
||||
+ "(SELECT itemID FROM itemAttachments WHERE parentItemID IS NOT NULL)";
|
||||
var ids = yield Zotero.DB.columnQueryAsync(sql, this.id);
|
||||
|
||||
this._childItems = [];
|
||||
|
||||
if (ids.length) {
|
||||
var items = yield this.ChildObjects.getAsync(ids);
|
||||
if (items) {
|
||||
this._childItems = items;
|
||||
}
|
||||
}
|
||||
|
||||
this._loaded.childItems = true;
|
||||
this._clearChanged('childItems');
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Add a collection to the cached child collections list if loaded
|
||||
*/
|
||||
|
@ -941,8 +838,7 @@ Zotero.Collection.prototype._registerChildCollection = function (collectionID) {
|
|||
if (this._loaded.childCollections) {
|
||||
let collection = this.ObjectsClass.get(collectionID);
|
||||
if (collection) {
|
||||
this._hasChildCollections = true;
|
||||
this._childCollections.push(collection);
|
||||
this._childCollections.add(collectionID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -953,13 +849,7 @@ Zotero.Collection.prototype._registerChildCollection = function (collectionID) {
|
|||
*/
|
||||
Zotero.Collection.prototype._unregisterChildCollection = function (collectionID) {
|
||||
if (this._loaded.childCollections) {
|
||||
for (let i = 0; i < this._childCollections.length; i++) {
|
||||
if (this._childCollections[i].id == collectionID) {
|
||||
this._childCollections.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._hasChildCollections = this._childCollections.length > 0;
|
||||
this._childCollections.delete(collectionID);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -971,8 +861,7 @@ Zotero.Collection.prototype._registerChildItem = function (itemID) {
|
|||
if (this._loaded.childItems) {
|
||||
let item = this.ChildObjects.get(itemID);
|
||||
if (item) {
|
||||
this._hasChildItems = true;
|
||||
this._childItems.push(item);
|
||||
this._childItems.add(itemID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -983,12 +872,6 @@ Zotero.Collection.prototype._registerChildItem = function (itemID) {
|
|||
*/
|
||||
Zotero.Collection.prototype._unregisterChildItem = function (itemID) {
|
||||
if (this._loaded.childItems) {
|
||||
for (let i = 0; i < this._childItems.length; i++) {
|
||||
if (this._childItems[i].id == itemID) {
|
||||
this._childItems.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._hasChildItems = this._childItems.length > 0;
|
||||
this._childItems.delete(itemID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,8 +85,7 @@ Zotero.Collections = function() {
|
|||
let children;
|
||||
|
||||
if (parentID) {
|
||||
let parent = yield Zotero.Collections.getAsync(parentID);
|
||||
yield parent.loadChildCollections();
|
||||
let parent = Zotero.Collections.get(parentID);
|
||||
children = parent.getChildCollections();
|
||||
} else if (libraryID) {
|
||||
let sql = "SELECT collectionID AS id FROM collections "
|
||||
|
@ -156,6 +155,103 @@ Zotero.Collections = function() {
|
|||
}
|
||||
|
||||
|
||||
this._loadChildCollections = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
|
||||
var sql = "SELECT C1.collectionID, C2.collectionID AS childCollectionID "
|
||||
+ "FROM collections C1 LEFT JOIN collections C2 ON (C1.collectionID=C2.parentCollectionID) "
|
||||
+ "WHERE C1.libraryID=?"
|
||||
+ (ids.length ? " AND C1.collectionID IN (" + ids.map(id => parseInt(id)).join(", ") + ")" : "");
|
||||
var params = [libraryID];
|
||||
var lastID;
|
||||
var rows = [];
|
||||
var setRows = function (collectionID, rows) {
|
||||
var collection = this._objectCache[collectionID];
|
||||
if (!collection) {
|
||||
throw new Error("Collection " + collectionID + " not found");
|
||||
}
|
||||
|
||||
collection._childCollections = new Set(rows);
|
||||
collection._loaded.childCollections = true;
|
||||
collection._clearChanged('childCollections');
|
||||
}.bind(this);
|
||||
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
let collectionID = row.getResultByIndex(0);
|
||||
|
||||
if (lastID && collectionID !== lastID) {
|
||||
setRows(lastID, rows);
|
||||
rows = [];
|
||||
}
|
||||
|
||||
lastID = collectionID;
|
||||
|
||||
let childCollectionID = row.getResultByIndex(1);
|
||||
// No child collections
|
||||
if (childCollectionID === null) {
|
||||
return;
|
||||
}
|
||||
rows.push(childCollectionID);
|
||||
}
|
||||
}
|
||||
);
|
||||
if (lastID) {
|
||||
setRows(lastID, rows);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this._loadChildItems = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
|
||||
var sql = "SELECT collectionID, itemID FROM collections "
|
||||
+ "LEFT JOIN collectionItems USING (collectionID) "
|
||||
+ "WHERE libraryID=?" + idSQL;
|
||||
var params = [libraryID];
|
||||
var lastID;
|
||||
var rows = [];
|
||||
var setRows = function (collectionID, rows) {
|
||||
var collection = this._objectCache[collectionID];
|
||||
if (!collection) {
|
||||
throw new Error("Collection " + collectionID + " not found");
|
||||
}
|
||||
|
||||
collection._childItems = new Set(rows);
|
||||
collection._loaded.childItems = true;
|
||||
collection._clearChanged('childItems');
|
||||
}.bind(this);
|
||||
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
let collectionID = row.getResultByIndex(0);
|
||||
|
||||
if (lastID && collectionID !== lastID) {
|
||||
setRows(lastID, rows);
|
||||
rows = [];
|
||||
}
|
||||
|
||||
lastID = collectionID;
|
||||
|
||||
let itemID = row.getResultByIndex(1);
|
||||
// No child items
|
||||
if (itemID === null) {
|
||||
return;
|
||||
}
|
||||
rows.push(itemID);
|
||||
}
|
||||
}
|
||||
);
|
||||
if (lastID) {
|
||||
setRows(lastID, rows);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this.registerChildCollection = function (collectionID, childCollectionID) {
|
||||
if (this._objectCache[collectionID]) {
|
||||
this._objectCache[collectionID]._registerChildCollection(childCollectionID);
|
||||
|
|
|
@ -30,29 +30,35 @@ Zotero.Creators = new function() {
|
|||
|
||||
var _cache = {};
|
||||
|
||||
this.init = Zotero.Promise.coroutine(function* () {
|
||||
var sql = "SELECT * FROM creators";
|
||||
var rows = yield Zotero.DB.queryAsync(sql);
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
_cache[row.creatorID] = this.cleanData({
|
||||
// Avoid "DB column 'name' not found" warnings from the DB row Proxy
|
||||
firstName: row.firstName,
|
||||
lastName: row.lastName,
|
||||
fieldMode: row.fieldMode
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Returns creator data in internal format for a given creatorID
|
||||
*/
|
||||
this.getAsync = Zotero.Promise.coroutine(function* (creatorID) {
|
||||
this.get = function (creatorID) {
|
||||
if (!creatorID) {
|
||||
throw new Error("creatorID not provided");
|
||||
}
|
||||
|
||||
if (_cache[creatorID]) {
|
||||
return this.cleanData(_cache[creatorID]);
|
||||
}
|
||||
|
||||
var sql = "SELECT * FROM creators WHERE creatorID=?";
|
||||
var row = yield Zotero.DB.rowQueryAsync(sql, creatorID);
|
||||
if (!row) {
|
||||
if (!_cache[creatorID]) {
|
||||
throw new Error("Creator " + creatorID + " not found");
|
||||
}
|
||||
return _cache[creatorID] = this.cleanData({
|
||||
firstName: row.firstName, // avoid "DB column 'name' not found" warnings from the DB row Proxy
|
||||
lastName: row.lastName,
|
||||
fieldMode: row.fieldMode
|
||||
});
|
||||
});
|
||||
|
||||
// Return copy of data
|
||||
return this.cleanData(_cache[creatorID]);
|
||||
};
|
||||
|
||||
|
||||
this.getItemsWithCreator = function (creatorID) {
|
||||
|
@ -87,12 +93,10 @@ Zotero.Creators = new function() {
|
|||
id = yield Zotero.ID.get('creators');
|
||||
let sql = "INSERT INTO creators (creatorID, firstName, lastName, fieldMode) "
|
||||
+ "VALUES (?, ?, ?, ?)";
|
||||
let insertID = yield Zotero.DB.queryAsync(
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql, [id, data.firstName, data.lastName, data.fieldMode]
|
||||
);
|
||||
if (!id) {
|
||||
id = insertID;
|
||||
}
|
||||
_cache[id] = data;
|
||||
}
|
||||
return id;
|
||||
});
|
||||
|
|
|
@ -401,7 +401,7 @@ Zotero.DataObject.prototype.setRelations = function (newRelations) {
|
|||
|
||||
// Relations are stored internally as a flat array with individual predicate-object pairs,
|
||||
// so convert the incoming relations to that
|
||||
var newRelationsFlat = this._flattenRelations(newRelations);
|
||||
var newRelationsFlat = this.ObjectsClass.flattenRelations(newRelations);
|
||||
|
||||
var changed = false;
|
||||
if (oldRelations.length != newRelationsFlat.length) {
|
||||
|
@ -457,8 +457,6 @@ Zotero.DataObject.prototype._getLinkedObject = Zotero.Promise.coroutine(function
|
|||
throw new Error(this._ObjectType + " is already in library " + libraryID);
|
||||
}
|
||||
|
||||
yield this.loadRelations();
|
||||
|
||||
var predicate = Zotero.Relations.linkedObjectPredicate;
|
||||
var libraryObjectPrefix = Zotero.URI.getLibraryURI(libraryID)
|
||||
+ "/" + this._objectTypePlural + "/";
|
||||
|
@ -514,8 +512,6 @@ Zotero.DataObject.prototype._addLinkedObject = Zotero.Promise.coroutine(function
|
|||
throw new Error("Can't add linked " + this._objectType + " in same library");
|
||||
}
|
||||
|
||||
yield this.loadRelations();
|
||||
|
||||
var predicate = Zotero.Relations.linkedObjectPredicate;
|
||||
var thisURI = Zotero.URI['get' + this._ObjectType + 'URI'](this);
|
||||
var objectURI = Zotero.URI['get' + this._ObjectType + 'URI'](object);
|
||||
|
@ -539,7 +535,6 @@ Zotero.DataObject.prototype._addLinkedObject = Zotero.Promise.coroutine(function
|
|||
});
|
||||
}
|
||||
else {
|
||||
yield object.loadRelations();
|
||||
object.addRelation(predicate, thisURI);
|
||||
yield object.save({
|
||||
skipDateModifiedUpdate: true,
|
||||
|
@ -551,9 +546,11 @@ Zotero.DataObject.prototype._addLinkedObject = Zotero.Promise.coroutine(function
|
|||
});
|
||||
|
||||
|
||||
/*
|
||||
* Build object from database
|
||||
*/
|
||||
//
|
||||
// Bulk data loading functions
|
||||
//
|
||||
// These are called by Zotero.DataObjects.prototype._loadDataType().
|
||||
//
|
||||
Zotero.DataObject.prototype.loadPrimaryData = Zotero.Promise.coroutine(function* (reload, failOnMissing) {
|
||||
if (this._loaded.primaryData && !reload) return;
|
||||
|
||||
|
@ -610,65 +607,6 @@ Zotero.DataObject.prototype.loadPrimaryData = Zotero.Promise.coroutine(function*
|
|||
});
|
||||
|
||||
|
||||
Zotero.DataObject.prototype.loadRelations = Zotero.Promise.coroutine(function* (reload) {
|
||||
if (!this.ObjectsClass._relationsTable) {
|
||||
throw new Error("Relations not supported for " + this._objectTypePlural);
|
||||
}
|
||||
|
||||
if (this._loaded.relations && !reload) {
|
||||
return;
|
||||
}
|
||||
|
||||
Zotero.debug("Loading relations for " + this._objectType + " " + this.libraryKey);
|
||||
|
||||
this._requireData('primaryData');
|
||||
|
||||
var sql = "SELECT predicate, object FROM " + this.ObjectsClass._relationsTable + " "
|
||||
+ "JOIN relationPredicates USING (predicateID) "
|
||||
+ "WHERE " + this.ObjectsClass.idColumn + "=?";
|
||||
var rows = yield Zotero.DB.queryAsync(sql, this.id);
|
||||
|
||||
var relations = {};
|
||||
function addRel(predicate, object) {
|
||||
if (!relations[predicate]) {
|
||||
relations[predicate] = [];
|
||||
}
|
||||
relations[predicate].push(object);
|
||||
}
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
addRel(row.predicate, row.object);
|
||||
}
|
||||
|
||||
/*if (this._objectType == 'item') {
|
||||
let getURI = Zotero.URI["get" + this._ObjectType + "URI"].bind(Zotero.URI);
|
||||
let objectURI = getURI(this);
|
||||
|
||||
// Related items are bidirectional, so include any pointing to this object
|
||||
let objects = yield Zotero.Relations.getByPredicateAndObject(
|
||||
Zotero.Relations.relatedItemPredicate, objectURI
|
||||
);
|
||||
for (let i = 0; i < objects.length; i++) {
|
||||
addRel(Zotero.Relations.relatedItemPredicate, getURI(objects[i]));
|
||||
}
|
||||
|
||||
// Also include any owl:sameAs relations pointing to this object
|
||||
objects = yield Zotero.Relations.getByPredicateAndObject(
|
||||
Zotero.Relations.linkedObjectPredicate, objectURI
|
||||
);
|
||||
for (let i = 0; i < objects.length; i++) {
|
||||
addRel(Zotero.Relations.linkedObjectPredicate, getURI(objects[i]));
|
||||
}
|
||||
}*/
|
||||
|
||||
// Relations are stored as predicate-object pairs
|
||||
this._relations = this._flattenRelations(relations);
|
||||
this._loaded.relations = true;
|
||||
this._clearChanged('relations');
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Reloads loaded, changed data
|
||||
*
|
||||
|
@ -735,7 +673,7 @@ Zotero.DataObject.prototype._requireData = function (dataType) {
|
|||
* @param {Boolean} reload
|
||||
*/
|
||||
Zotero.DataObject.prototype._loadDataType = function (dataType, reload) {
|
||||
return this["load" + dataType[0].toUpperCase() + dataType.substr(1)](reload);
|
||||
return this._ObjectsClass._loadDataType(dataType, this.libraryID, [this.id]);
|
||||
}
|
||||
|
||||
Zotero.DataObject.prototype.loadAllData = function (reload) {
|
||||
|
@ -868,6 +806,16 @@ Zotero.DataObject.prototype.save = Zotero.Promise.coroutine(function* (options)
|
|||
Zotero.debug('Updating database with new ' + this._objectType + ' data', 4);
|
||||
}
|
||||
|
||||
if (env.options.skipAll) {
|
||||
[
|
||||
'skipDateModifiedUpdate',
|
||||
'skipClientDateModifiedUpdate',
|
||||
'skipSyncedUpdate',
|
||||
'skipEditCheck',
|
||||
'skipSelect'
|
||||
].forEach(x => env.options[x] = true);
|
||||
}
|
||||
|
||||
try {
|
||||
if (Zotero.DataObject.prototype._finalizeSave == this._finalizeSave) {
|
||||
throw new Error("_finalizeSave not implemented for Zotero." + this._ObjectType);
|
||||
|
@ -1214,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;
|
||||
}
|
||||
|
|
|
@ -336,6 +336,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
|
||||
*
|
||||
|
@ -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];
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
let otherData = otherItems[i].toJSON();
|
||||
let changeset = Zotero.DataObjectUtilities.diff(thisData, otherData, ignoreFields);
|
||||
|
||||
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);
|
||||
}
|
||||
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
|
||||
|
|
|
@ -84,7 +84,10 @@ Zotero.Items = function() {
|
|||
attachmentCharset: "CS.charset AS attachmentCharset",
|
||||
attachmentLinkMode: "IA.linkMode AS attachmentLinkMode",
|
||||
attachmentContentType: "IA.contentType AS attachmentContentType",
|
||||
attachmentPath: "IA.path AS attachmentPath"
|
||||
attachmentPath: "IA.path AS attachmentPath",
|
||||
attachmentSyncState: "IA.syncState AS attachmentSyncState",
|
||||
attachmentSyncedModificationTime: "IA.storageModTime AS attachmentSyncedModificationTime",
|
||||
attachmentSyncedHash: "IA.storageHash AS attachmentSyncedHash"
|
||||
};
|
||||
}
|
||||
}, {lazy: true});
|
||||
|
@ -204,7 +207,7 @@ Zotero.Items = function() {
|
|||
for (let i=0; i<ids.length; i++) {
|
||||
let prefix = i > 0 ? ',\n' : '';
|
||||
let item = yield this.getAsync(ids[i], { noCache: true });
|
||||
var json = yield item.toResponseJSON();
|
||||
var json = item.toResponseJSON();
|
||||
yield prefix + JSON.stringify(json, null, 4);
|
||||
}
|
||||
|
||||
|
@ -212,105 +215,24 @@ Zotero.Items = function() {
|
|||
};
|
||||
|
||||
|
||||
this._cachedFields = {};
|
||||
this.cacheFields = Zotero.Promise.coroutine(function* (libraryID, fields, items) {
|
||||
if (items && items.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var t = new Date;
|
||||
|
||||
fields = fields.concat();
|
||||
|
||||
// Needed for display titles for some item types
|
||||
if (fields.indexOf('title') != -1) {
|
||||
fields.push('reporter', 'court');
|
||||
}
|
||||
|
||||
Zotero.debug("Caching fields [" + fields.join() + "]"
|
||||
+ (items ? " for " + items.length + " items" : '')
|
||||
+ " in library " + libraryID);
|
||||
|
||||
if (items && items.length > 0) {
|
||||
yield this._load(libraryID, items);
|
||||
}
|
||||
else {
|
||||
yield this._load(libraryID);
|
||||
}
|
||||
|
||||
var primaryFields = [];
|
||||
var fieldIDs = [];
|
||||
for each(var field in fields) {
|
||||
// Check if field already cached
|
||||
if (this._cachedFields[libraryID] && this._cachedFields[libraryID].indexOf(field) != -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!this._cachedFields[libraryID]) {
|
||||
this._cachedFields[libraryID] = [];
|
||||
}
|
||||
this._cachedFields[libraryID].push(field);
|
||||
|
||||
if (this.isPrimaryField(field)) {
|
||||
primaryFields.push(field);
|
||||
}
|
||||
else {
|
||||
fieldIDs.push(Zotero.ItemFields.getID(field));
|
||||
if (Zotero.ItemFields.isBaseField(field)) {
|
||||
fieldIDs = fieldIDs.concat(Zotero.ItemFields.getTypeFieldsFromBase(field));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (primaryFields.length) {
|
||||
var sql = "SELECT O.itemID, "
|
||||
+ primaryFields.map((val) => this.getPrimaryDataSQLPart(val)).join(', ')
|
||||
+ this.primaryDataSQLFrom + " AND O.libraryID=?";
|
||||
var params = [libraryID];
|
||||
if (items) {
|
||||
sql += " AND O.itemID IN (" + items.join() + ")";
|
||||
}
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
onRow: function (row) {
|
||||
let obj = {
|
||||
itemID: row.getResultByIndex(0)
|
||||
};
|
||||
for (let i=0; i<primaryFields.length; i++) {
|
||||
obj[primaryFields[i]] = row.getResultByIndex(i);
|
||||
}
|
||||
Zotero.debug(obj.itemID);
|
||||
Zotero.debug(Object.keys(this._objectCache));
|
||||
this._objectCache[obj.itemID].loadFromRow(obj);
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// All fields already cached
|
||||
if (!fieldIDs.length) {
|
||||
Zotero.debug('All fields already cached');
|
||||
return;
|
||||
}
|
||||
|
||||
var sql = "SELECT itemID FROM items WHERE libraryID=?";
|
||||
var params = [libraryID];
|
||||
var allItemIDs = yield Zotero.DB.columnQueryAsync(sql, params);
|
||||
//
|
||||
// Bulk data loading functions
|
||||
//
|
||||
// These are called by Zotero.DataObjects.prototype._loadDataType().
|
||||
//
|
||||
this._loadItemData = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
|
||||
var missingItems = {};
|
||||
var itemFieldsCached = {};
|
||||
|
||||
var sql = "SELECT itemID, fieldID, value FROM items JOIN itemData USING (itemID) "
|
||||
+ "JOIN itemDataValues USING (valueID) WHERE libraryID=?";
|
||||
var params = [libraryID];
|
||||
if (items) {
|
||||
sql += " AND itemID IN (" + items.join() + ")";
|
||||
}
|
||||
sql += " AND fieldID IN (" + fieldIDs.join() + ")";
|
||||
var sql = "SELECT itemID, fieldID, value FROM items "
|
||||
+ "JOIN itemData USING (itemID) "
|
||||
+ "JOIN itemDataValues USING (valueID) WHERE libraryID=? AND itemTypeID!=?" + idSQL;
|
||||
var params = [libraryID, Zotero.ItemTypes.getID('note')];
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
let itemID = row.getResultByIndex(0);
|
||||
let fieldID = row.getResultByIndex(1);
|
||||
|
@ -318,19 +240,17 @@ Zotero.Items = function() {
|
|||
|
||||
//Zotero.debug('Setting field ' + fieldID + ' for item ' + itemID);
|
||||
if (this._objectCache[itemID]) {
|
||||
if (value === null) {
|
||||
value = false;
|
||||
}
|
||||
this._objectCache[itemID].setField(fieldID, value, true);
|
||||
}
|
||||
else {
|
||||
if (!missingItems) {
|
||||
var missingItems = {};
|
||||
}
|
||||
if (!missingItems[itemID]) {
|
||||
missingItems[itemID] = true;
|
||||
Zotero.debug("itemData row references nonexistent item " + itemID);
|
||||
Components.utils.reportError("itemData row references nonexistent item " + itemID);
|
||||
Zotero.logError("itemData row references nonexistent item " + itemID);
|
||||
}
|
||||
}
|
||||
|
||||
if (!itemFieldsCached[itemID]) {
|
||||
itemFieldsCached[itemID] = {};
|
||||
}
|
||||
|
@ -339,68 +259,463 @@ Zotero.Items = function() {
|
|||
}
|
||||
);
|
||||
|
||||
// Set nonexistent fields in the cache list to false (instead of null)
|
||||
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)
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
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)" + idSQL;
|
||||
var params = [libraryID];
|
||||
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
onRow: function (row) {
|
||||
let itemID = row.getResultByIndex(0);
|
||||
let title = row.getResultByIndex(1);
|
||||
|
||||
//Zotero.debug('Setting title for note ' + row.itemID);
|
||||
if (this._objectCache[itemID]) {
|
||||
this._objectCache[itemID].setField(titleFieldID, title, true);
|
||||
}
|
||||
else {
|
||||
if (!missingItems[itemID]) {
|
||||
missingItems[itemID] = true;
|
||||
Zotero.logError("itemData row references nonexistent item " + itemID);
|
||||
}
|
||||
}
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
|
||||
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 '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');
|
||||
let item = this._objectCache[itemID];
|
||||
|
||||
// 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() + ")";
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
);
|
||||
// Mark as loaded
|
||||
item._loaded.itemData = true;
|
||||
item._clearChanged('itemData');
|
||||
|
||||
// Display titles
|
||||
for (let i=0; i<allItemIDs.length; i++) {
|
||||
let itemID = allItemIDs[i];
|
||||
let item = this._objectCache[itemID];
|
||||
yield item.loadDisplayTitle()
|
||||
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);
|
||||
|
||||
// Mark creator indexes above the number of creators as changed,
|
||||
// so that they're cleared if the item is saved
|
||||
var fixIncorrectIndexes = function (item, numCreators, maxOrderIndex) {
|
||||
Zotero.debug("Fixing incorrect creator indexes for item " + item.libraryKey
|
||||
+ " (" + numCreators + ", " + maxOrderIndex + ")", 2);
|
||||
var i = numCreators;
|
||||
while (i <= maxOrderIndex) {
|
||||
item._changed.creators[i] = true;
|
||||
i++;
|
||||
}
|
||||
};
|
||||
|
||||
var lastItemID;
|
||||
var item;
|
||||
var index = 0;
|
||||
var maxOrderIndex = -1;
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
let itemID = row.itemID;
|
||||
|
||||
if (itemID != lastItemID) {
|
||||
if (!this._objectCache[itemID]) {
|
||||
throw new Error("Item " + itemID + " not loaded");
|
||||
}
|
||||
item = this._objectCache[itemID];
|
||||
|
||||
item._creators = [];
|
||||
item._creatorIDs = [];
|
||||
item._loaded.creators = true;
|
||||
item._clearChanged('creators');
|
||||
|
||||
if (!row.creatorID) {
|
||||
lastItemID = row.itemID;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (index <= maxOrderIndex) {
|
||||
fixIncorrectIndexes(item, index, maxOrderIndex);
|
||||
}
|
||||
|
||||
index = 0;
|
||||
maxOrderIndex = -1;
|
||||
}
|
||||
|
||||
lastItemID = row.itemID;
|
||||
|
||||
if (row.orderIndex > maxOrderIndex) {
|
||||
maxOrderIndex = row.orderIndex;
|
||||
}
|
||||
|
||||
let creatorData = Zotero.Creators.get(row.creatorID);
|
||||
creatorData.creatorTypeID = row.creatorTypeID;
|
||||
item._creators[index] = creatorData;
|
||||
item._creatorIDs[index] = row.creatorID;
|
||||
index++;
|
||||
}
|
||||
|
||||
Zotero.debug("Cached fields in " + ((new Date) - t) + "ms");
|
||||
if (index <= maxOrderIndex) {
|
||||
fixIncorrectIndexes(item, index, maxOrderIndex);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this._loadNotes = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
|
||||
var notesToUpdate = [];
|
||||
|
||||
var sql = "SELECT itemID, note FROM items "
|
||||
+ "JOIN itemNotes USING (itemID) "
|
||||
+ "WHERE libraryID=?" + idSQL;
|
||||
var params = [libraryID];
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
let itemID = row.getResultByIndex(0);
|
||||
let item = this._objectCache[itemID];
|
||||
if (!item) {
|
||||
throw new Error("Item " + itemID + " not found");
|
||||
}
|
||||
let note = row.getResultByIndex(1);
|
||||
|
||||
// Convert non-HTML notes on-the-fly
|
||||
if (note !== "") {
|
||||
if (!note.substr(0, 36).match(/^<div class="zotero-note znv[0-9]+">/)) {
|
||||
note = Zotero.Utilities.htmlSpecialChars(note);
|
||||
note = Zotero.Notes.notePrefix + '<p>'
|
||||
+ note.replace(/\n/g, '</p><p>')
|
||||
.replace(/\t/g, ' ')
|
||||
.replace(/ /g, ' ')
|
||||
+ '</p>' + Zotero.Notes.noteSuffix;
|
||||
note = note.replace(/<p>\s*<\/p>/g, '<p> </p>');
|
||||
notesToUpdate.push([item.id, note]);
|
||||
}
|
||||
|
||||
// Don't include <div> wrapper when returning value
|
||||
let startLen = note.substr(0, 36).match(/^<div class="zotero-note znv[0-9]+">/)[0].length;
|
||||
let endLen = 6; // "</div>".length
|
||||
note = note.substr(startLen, note.length - startLen - endLen);
|
||||
}
|
||||
|
||||
item._noteText = note ? note : '';
|
||||
item._loaded.note = true;
|
||||
item._clearChanged('note');
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
|
||||
if (notesToUpdate.length) {
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
for (let i = 0; i < notesToUpdate.length; i++) {
|
||||
let row = notesToUpdate[i];
|
||||
let sql = "UPDATE itemNotes SET note=? WHERE itemID=?";
|
||||
yield Zotero.DB.queryAsync(sql, [row[1], row[0]]);
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
// Mark notes and attachments without notes as loaded
|
||||
sql = "SELECT itemID FROM items WHERE libraryID=?" + idSQL
|
||||
+ " AND itemTypeID IN (?, ?) AND itemID NOT IN (SELECT itemID FROM itemNotes)";
|
||||
params = [libraryID, Zotero.ItemTypes.getID('note'), Zotero.ItemTypes.getID('attachment')];
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
let itemID = row.getResultByIndex(0);
|
||||
let item = this._objectCache[itemID];
|
||||
if (!item) {
|
||||
throw new Error("Item " + itemID + " not loaded");
|
||||
}
|
||||
|
||||
item._noteText = '';
|
||||
item._loaded.note = true;
|
||||
item._clearChanged('note');
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
this._loadChildItems = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
|
||||
var params = [libraryID];
|
||||
var rows = [];
|
||||
var onRow = function (row, setFunc) {
|
||||
var itemID = row.getResultByIndex(0);
|
||||
|
||||
if (lastItemID && itemID !== lastItemID) {
|
||||
setFunc(lastItemID, rows);
|
||||
rows = [];
|
||||
}
|
||||
|
||||
lastItemID = itemID;
|
||||
rows.push({
|
||||
itemID: row.getResultByIndex(1),
|
||||
title: row.getResultByIndex(2),
|
||||
trashed: row.getResultByIndex(3)
|
||||
});
|
||||
};
|
||||
|
||||
var sql = "SELECT parentItemID, A.itemID, value AS title, "
|
||||
+ "CASE WHEN DI.itemID IS NULL THEN 0 ELSE 1 END AS trashed "
|
||||
+ "FROM itemAttachments A "
|
||||
+ "JOIN items I ON (A.parentItemID=I.itemID) "
|
||||
+ "LEFT JOIN itemData ID ON (fieldID=110 AND A.itemID=ID.itemID) "
|
||||
+ "LEFT JOIN itemDataValues IDV USING (valueID) "
|
||||
+ "LEFT JOIN deletedItems DI USING (itemID) "
|
||||
+ "WHERE libraryID=?"
|
||||
+ (ids.length ? " AND parentItemID IN (" + ids.map(id => parseInt(id)).join(", ") + ")" : "");
|
||||
// Since we do the sort here and cache these results, a restart will be required
|
||||
// if this pref (off by default) is turned on, but that's OK
|
||||
if (Zotero.Prefs.get('sortAttachmentsChronologically')) {
|
||||
sql += " ORDER BY parentItemID, dateAdded";
|
||||
}
|
||||
var setAttachmentItem = function (itemID, rows) {
|
||||
var item = this._objectCache[itemID];
|
||||
if (!item) {
|
||||
throw new Error("Item " + itemID + " not loaded");
|
||||
}
|
||||
|
||||
item._attachments = {
|
||||
rows,
|
||||
chronologicalWithTrashed: null,
|
||||
chronologicalWithoutTrashed: null,
|
||||
alphabeticalWithTrashed: null,
|
||||
alphabeticalWithoutTrashed: null
|
||||
};
|
||||
}.bind(this);
|
||||
var lastItemID = null;
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
onRow(row, setAttachmentItem);
|
||||
}
|
||||
}
|
||||
);
|
||||
if (lastItemID) {
|
||||
setAttachmentItem(lastItemID, rows);
|
||||
}
|
||||
|
||||
//
|
||||
// Notes
|
||||
//
|
||||
sql = "SELECT parentItemID, N.itemID, title, "
|
||||
+ "CASE WHEN DI.itemID IS NULL THEN 0 ELSE 1 END AS trashed "
|
||||
+ "FROM itemNotes N "
|
||||
+ "JOIN items I ON (N.parentItemID=I.itemID) "
|
||||
+ "LEFT JOIN deletedItems DI USING (itemID) "
|
||||
+ "WHERE libraryID=?"
|
||||
+ (ids.length ? " AND parentItemID IN (" + ids.map(id => parseInt(id)).join(", ") + ")" : "");
|
||||
if (Zotero.Prefs.get('sortNotesChronologically')) {
|
||||
sql += " ORDER BY parentItemID, dateAdded";
|
||||
}
|
||||
var setNoteItem = function (itemID, rows) {
|
||||
var item = this._objectCache[itemID];
|
||||
if (!item) {
|
||||
throw new Error("Item " + itemID + " not loaded");
|
||||
}
|
||||
|
||||
item._notes = {
|
||||
rows,
|
||||
rowsEmbedded: null,
|
||||
chronologicalWithTrashed: null,
|
||||
chronologicalWithoutTrashed: null,
|
||||
alphabeticalWithTrashed: null,
|
||||
alphabeticalWithoutTrashed: null,
|
||||
numWithTrashed: null,
|
||||
numWithoutTrashed: null,
|
||||
numWithTrashedWithEmbedded: null,
|
||||
numWithoutTrashedWithoutEmbedded: null
|
||||
};
|
||||
}.bind(this);
|
||||
lastItemID = null;
|
||||
rows = [];
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
onRow(row, setNoteItem);
|
||||
}
|
||||
}
|
||||
);
|
||||
if (lastItemID) {
|
||||
setNoteItem(lastItemID, rows);
|
||||
}
|
||||
|
||||
// Mark all top-level items as having child items loaded
|
||||
sql = "SELECT itemID FROM items I WHERE libraryID=?" + idSQL + " AND itemID NOT IN "
|
||||
+ "(SELECT itemID FROM itemAttachments UNION SELECT itemID FROM itemNotes)";
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
var itemID = row.getResultByIndex(0);
|
||||
var item = this._objectCache[itemID];
|
||||
if (!item) {
|
||||
throw new Error("Item " + itemID + " not loaded");
|
||||
}
|
||||
item._loaded.childItems = true;
|
||||
item._clearChanged('childItems');
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
this._loadTags = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
|
||||
var sql = "SELECT itemID, name, type FROM items "
|
||||
+ "LEFT JOIN itemTags USING (itemID) "
|
||||
+ "LEFT JOIN tags USING (tagID) WHERE libraryID=?" + idSQL;
|
||||
var params = [libraryID];
|
||||
|
||||
var lastItemID;
|
||||
var rows = [];
|
||||
var setRows = function (itemID, rows) {
|
||||
var item = this._objectCache[itemID];
|
||||
if (!item) {
|
||||
throw new Error("Item " + itemID + " not found");
|
||||
}
|
||||
|
||||
item._tags = [];
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
item._tags.push(Zotero.Tags.cleanData(row));
|
||||
}
|
||||
|
||||
item._loaded.tags = true;
|
||||
item._clearChanged('tags');
|
||||
}.bind(this);
|
||||
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
let itemID = row.getResultByIndex(0);
|
||||
|
||||
if (lastItemID && itemID !== lastItemID) {
|
||||
setRows(lastItemID, rows);
|
||||
rows = [];
|
||||
}
|
||||
|
||||
lastItemID = itemID;
|
||||
|
||||
// Item has no tags
|
||||
let tag = row.getResultByIndex(1);
|
||||
if (tag === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
rows.push({
|
||||
tag: tag,
|
||||
type: row.getResultByIndex(2)
|
||||
});
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
if (lastItemID) {
|
||||
setRows(lastItemID, rows);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this._loadCollections = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
|
||||
var sql = "SELECT itemID, collectionID FROM items "
|
||||
+ "LEFT JOIN collectionItems USING (itemID) "
|
||||
+ "WHERE libraryID=?" + idSQL;
|
||||
var params = [libraryID];
|
||||
|
||||
var lastItemID;
|
||||
var rows = [];
|
||||
var setRows = function (itemID, rows) {
|
||||
var item = this._objectCache[itemID];
|
||||
if (!item) {
|
||||
throw new Error("Item " + itemID + " not found");
|
||||
}
|
||||
|
||||
item._collections = rows;
|
||||
item._loaded.collections = true;
|
||||
item._clearChanged('collections');
|
||||
}.bind(this);
|
||||
|
||||
yield Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
{
|
||||
noCache: ids.length != 1,
|
||||
onRow: function (row) {
|
||||
let itemID = row.getResultByIndex(0);
|
||||
|
||||
if (lastItemID && itemID !== lastItemID) {
|
||||
setRows(lastItemID, rows);
|
||||
rows = [];
|
||||
}
|
||||
|
||||
lastItemID = itemID;
|
||||
let collectionID = row.getResultByIndex(1);
|
||||
// No collections
|
||||
if (collectionID === null) {
|
||||
return;
|
||||
}
|
||||
rows.push(collectionID);
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
if (lastItemID) {
|
||||
setRows(lastItemID, rows);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
@ -409,17 +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
|
||||
*
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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,20 +421,19 @@ 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) {
|
||||
this.getColors = function (libraryID) {
|
||||
if (!libraryID) {
|
||||
throw new Error("libraryID not provided");
|
||||
}
|
||||
|
||||
if (_libraryColorsByName[libraryID]) {
|
||||
return _libraryColorsByName[libraryID];
|
||||
}
|
||||
|
||||
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 || [];
|
||||
|
||||
|
@ -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 = [];
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
},
|
||||
|
||||
|
||||
|
|
|
@ -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 + "$");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -65,9 +65,9 @@ Zotero.Sync.Storage = new function () {
|
|||
return false;
|
||||
}
|
||||
|
||||
var syncModTime = Zotero.Sync.Storage.getSyncedModificationTime(itemID);
|
||||
var syncModTime = item.attachmentSyncedModificationTime;
|
||||
if (fileModTime != syncModTime) {
|
||||
var syncHash = Zotero.Sync.Storage.getSyncedHash(itemID);
|
||||
var syncHash = item.attachmentSyncedHash;
|
||||
if (syncHash) {
|
||||
var fileHash = item.attachmentHash;
|
||||
if (fileHash && fileHash == syncHash) {
|
||||
|
|
|
@ -273,7 +273,7 @@ Zotero.Sync.Storage.Engine.prototype.stop = function () {
|
|||
}
|
||||
|
||||
Zotero.Sync.Storage.Engine.prototype.queueItem = Zotero.Promise.coroutine(function* (item) {
|
||||
switch (yield this.local.getSyncState(item.id)) {
|
||||
switch (item.attachmentSyncState) {
|
||||
case Zotero.Sync.Storage.Local.SYNC_STATE_TO_DOWNLOAD:
|
||||
case Zotero.Sync.Storage.Local.SYNC_STATE_FORCE_DOWNLOAD:
|
||||
var type = 'download';
|
||||
|
@ -295,7 +295,7 @@ Zotero.Sync.Storage.Engine.prototype.queueItem = Zotero.Promise.coroutine(functi
|
|||
return;
|
||||
|
||||
default:
|
||||
throw new Error("Invalid sync state " + (yield this.local.getSyncState(item.id)));
|
||||
throw new Error("Invalid sync state " + item.attachmentSyncState);
|
||||
}
|
||||
|
||||
var request = new Zotero.Sync.Storage.Request({
|
||||
|
|
|
@ -239,7 +239,8 @@ Zotero.Sync.Storage.Local = {
|
|||
// TODO: Catch error?
|
||||
let state = yield this._checkForUpdatedFile(item, attachmentData[item.id]);
|
||||
if (state !== false) {
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, state);
|
||||
item.attachmentSyncState = state;
|
||||
yield item.saveTx({ skipAll: true });
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
@ -282,7 +283,7 @@ Zotero.Sync.Storage.Local = {
|
|||
// If file is already marked for upload, skip check. Even if the file was changed
|
||||
// both locally and remotely, conflicts are checked at upload time, so we don't need
|
||||
// to worry about it here.
|
||||
if ((yield this.getSyncState(item.id)) == this.SYNC_STATE_TO_UPLOAD) {
|
||||
if (item.attachmentSyncState == this.SYNC_STATE_TO_UPLOAD) {
|
||||
Zotero.debug("File is already marked for upload");
|
||||
return false;
|
||||
}
|
||||
|
@ -298,7 +299,7 @@ Zotero.Sync.Storage.Local = {
|
|||
Zotero.debug(`Remote mod time for item ${lk} is ${remoteModTime}`);
|
||||
|
||||
// Ignore attachments whose stored mod times haven't changed
|
||||
mtime = mtime !== false ? mtime : (yield this.getSyncedModificationTime(item.id));
|
||||
mtime = mtime !== false ? mtime : item.attachmentSyncedModificationTime;
|
||||
if (mtime == remoteModTime) {
|
||||
Zotero.debug(`Synced mod time (${mtime}) hasn't changed for item ${lk}`);
|
||||
return false;
|
||||
|
@ -474,125 +475,23 @@ Zotero.Sync.Storage.Local = {
|
|||
},
|
||||
|
||||
|
||||
/**
|
||||
* @param {Integer} itemID
|
||||
*/
|
||||
getSyncState: function (itemID) {
|
||||
var sql = "SELECT syncState FROM itemAttachments WHERE itemID=?";
|
||||
return Zotero.DB.valueQueryAsync(sql, itemID);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @param {Integer} itemID
|
||||
* @param {Integer|String} syncState - Zotero.Sync.Storage.Local.SYNC_STATE_* or last part
|
||||
* as string (e.g., "TO_UPLOAD")
|
||||
*/
|
||||
setSyncState: Zotero.Promise.method(function (itemID, syncState) {
|
||||
if (typeof syncState == 'string') {
|
||||
syncState = this["SYNC_STATE_" + syncState.toUpperCase()];
|
||||
resetModeSyncStates: Zotero.Promise.coroutine(function* () {
|
||||
var sql = "SELECT itemID FROM items JOIN itemAttachments USING (itemID) "
|
||||
+ "WHERE libraryID=? AND itemTypeID=? AND linkMode IN (?, ?)";
|
||||
var params = [
|
||||
Zotero.Libraries.userLibraryID,
|
||||
Zotero.ItemTypes.getID('attachment'),
|
||||
Zotero.Attachments.LINK_MODE_IMPORTED_FILE,
|
||||
Zotero.Attachments.LINK_MODE_IMPORTED_URL,
|
||||
];
|
||||
var itemIDs = yield Zotero.DB.columnQueryAsync(sql, params);
|
||||
for (let itemID of items) {
|
||||
let item = Zotero.Items.get(itemID);
|
||||
item._attachmentSyncState = this.SYNC_STATE_TO_UPLOAD;
|
||||
}
|
||||
|
||||
switch (syncState) {
|
||||
case this.SYNC_STATE_TO_UPLOAD:
|
||||
case this.SYNC_STATE_TO_DOWNLOAD:
|
||||
case this.SYNC_STATE_IN_SYNC:
|
||||
case this.SYNC_STATE_FORCE_UPLOAD:
|
||||
case this.SYNC_STATE_FORCE_DOWNLOAD:
|
||||
case this.SYNC_STATE_IN_CONFLICT:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("Invalid sync state " + syncState);
|
||||
}
|
||||
|
||||
var sql = "UPDATE itemAttachments SET syncState=? WHERE itemID=?";
|
||||
return Zotero.DB.valueQueryAsync(sql, [syncState, itemID]);
|
||||
}),
|
||||
|
||||
|
||||
resetModeSyncStates: Zotero.Promise.coroutine(function* (mode) {
|
||||
var sql = "UPDATE itemAttachments SET syncState=? "
|
||||
+ "WHERE itemID IN (SELECT itemID FROM items WHERE libraryID=?)";
|
||||
var params = [this.SYNC_STATE_TO_UPLOAD, Zotero.Libraries.userLibraryID];
|
||||
yield Zotero.DB.queryAsync(sql, params);
|
||||
}),
|
||||
|
||||
|
||||
/**
|
||||
* @param {Integer} itemID
|
||||
* @return {Integer|NULL} Mod time as timestamp in ms,
|
||||
* or NULL if never synced
|
||||
*/
|
||||
getSyncedModificationTime: Zotero.Promise.coroutine(function* (itemID) {
|
||||
var sql = "SELECT storageModTime FROM itemAttachments WHERE itemID=?";
|
||||
var mtime = yield Zotero.DB.valueQueryAsync(sql, itemID);
|
||||
if (mtime === false) {
|
||||
throw new Error("Item " + itemID + " not found")
|
||||
}
|
||||
return mtime;
|
||||
}),
|
||||
|
||||
|
||||
/**
|
||||
* @param {Integer} itemID
|
||||
* @param {Integer} mtime - File modification time as timestamp in ms
|
||||
* @param {Boolean} [updateItem=FALSE] - Mark attachment item as unsynced
|
||||
*/
|
||||
setSyncedModificationTime: Zotero.Promise.coroutine(function* (itemID, mtime, updateItem) {
|
||||
mtime = parseInt(mtime)
|
||||
if (isNaN(mtime) || mtime < 0) {
|
||||
Components.utils.reportError("Invalid file mod time " + mtime
|
||||
+ " in Zotero.Storage.setSyncedModificationTime()");
|
||||
mtime = 0;
|
||||
}
|
||||
|
||||
Zotero.DB.requireTransaction();
|
||||
|
||||
var sql = "UPDATE itemAttachments SET storageModTime=? WHERE itemID=?";
|
||||
yield Zotero.DB.queryAsync(sql, [mtime, itemID]);
|
||||
|
||||
if (updateItem) {
|
||||
let item = yield Zotero.Items.getAsync(itemID);
|
||||
yield item.updateSynced(false);
|
||||
}
|
||||
}),
|
||||
|
||||
|
||||
/**
|
||||
* @param {Integer} itemID
|
||||
* @return {Promise<String|null|false>} - File hash, null if never synced, if false if
|
||||
* file doesn't exist
|
||||
*/
|
||||
getSyncedHash: Zotero.Promise.coroutine(function* (itemID) {
|
||||
var sql = "SELECT storageHash FROM itemAttachments WHERE itemID=?";
|
||||
var hash = yield Zotero.DB.valueQueryAsync(sql, itemID);
|
||||
if (hash === false) {
|
||||
throw new Error("Item " + itemID + " not found");
|
||||
}
|
||||
return hash;
|
||||
}),
|
||||
|
||||
|
||||
/**
|
||||
* @param {Integer} itemID
|
||||
* @param {String} hash File hash
|
||||
* @param {Boolean} [updateItem=FALSE] - Mark attachment item as unsynced
|
||||
*/
|
||||
setSyncedHash: Zotero.Promise.coroutine(function* (itemID, hash, updateItem) {
|
||||
if (hash !== null && hash.length != 32) {
|
||||
throw new Error("Invalid file hash '" + hash + "'");
|
||||
}
|
||||
|
||||
Zotero.DB.requireTransaction();
|
||||
|
||||
var sql = "UPDATE itemAttachments SET storageHash=? WHERE itemID=?";
|
||||
yield Zotero.DB.queryAsync(sql, [hash, itemID]);
|
||||
|
||||
if (updateItem) {
|
||||
let item = yield Zotero.Items.getAsync(itemID);
|
||||
yield item.updateSynced(false);
|
||||
}
|
||||
sql = "UPDATE itemAttachments SET syncState=? WHERE itemID IN (" + sql + ")";
|
||||
yield Zotero.DB.queryAsync(sql, [this.SYNC_STATE_TO_UPLOAD].concat(params));
|
||||
}),
|
||||
|
||||
|
||||
|
@ -678,11 +577,10 @@ Zotero.Sync.Storage.Local = {
|
|||
// Set the file mtime to the time from the server
|
||||
yield OS.File.setDates(path, null, new Date(parseInt(mtime)));
|
||||
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield this.setSyncedHash(item.id, md5);
|
||||
yield this.setSyncState(item.id, this.SYNC_STATE_IN_SYNC);
|
||||
yield this.setSyncedModificationTime(item.id, mtime);
|
||||
}.bind(this));
|
||||
item.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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
})
|
||||
|
|
|
@ -31,7 +31,6 @@ Zotero.Timeline = {
|
|||
yield '<data>\n';
|
||||
for (let i=0; i<items.length; i++) {
|
||||
let item = items[i];
|
||||
yield item.loadItemData();
|
||||
var date = item.getField(dateType, true, true);
|
||||
if (date) {
|
||||
let sqlDate = (dateType == 'date') ? Zotero.Date.multipartToSQL(date) : date;
|
||||
|
|
|
@ -659,7 +659,6 @@ Zotero.Translate.ItemGetter.prototype = {
|
|||
|
||||
"setCollection": Zotero.Promise.coroutine(function* (collection, getChildCollections) {
|
||||
// get items in this collection
|
||||
yield collection.loadChildItems();
|
||||
var items = new Set(collection.getChildItems());
|
||||
|
||||
if(getChildCollections) {
|
||||
|
@ -668,7 +667,6 @@ Zotero.Translate.ItemGetter.prototype = {
|
|||
|
||||
// get items in child collections
|
||||
for (let collection of this._collectionsLeft) {
|
||||
yield collection.loadChildItems();
|
||||
var childItems = collection.getChildItems();
|
||||
childItems.forEach(item => items.add(item));
|
||||
}
|
||||
|
@ -720,7 +718,7 @@ Zotero.Translate.ItemGetter.prototype = {
|
|||
* Converts an attachment to array format and copies it to the export folder if desired
|
||||
*/
|
||||
"_attachmentToArray":Zotero.Promise.coroutine(function* (attachment) {
|
||||
var attachmentArray = yield Zotero.Utilities.Internal.itemToExportFormat(attachment, this.legacy);
|
||||
var attachmentArray = Zotero.Utilities.Internal.itemToExportFormat(attachment, this.legacy);
|
||||
var linkMode = attachment.attachmentLinkMode;
|
||||
if(linkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) {
|
||||
var attachFile = attachment.getFile();
|
||||
|
@ -864,13 +862,13 @@ Zotero.Translate.ItemGetter.prototype = {
|
|||
var returnItemArray = yield this._attachmentToArray(returnItem);
|
||||
if(returnItemArray) return returnItemArray;
|
||||
} else {
|
||||
var returnItemArray = yield Zotero.Utilities.Internal.itemToExportFormat(returnItem, this.legacy);
|
||||
var returnItemArray = Zotero.Utilities.Internal.itemToExportFormat(returnItem, this.legacy);
|
||||
|
||||
// get attachments, although only urls will be passed if exportFileData is off
|
||||
returnItemArray.attachments = [];
|
||||
var attachments = returnItem.getAttachments();
|
||||
for each(var attachmentID in attachments) {
|
||||
var attachment = yield Zotero.Items.getAsync(attachmentID);
|
||||
var attachment = Zotero.Items.get(attachmentID);
|
||||
var attachmentInfo = yield this._attachmentToArray(attachment);
|
||||
|
||||
if(attachmentInfo) {
|
||||
|
|
|
@ -1591,8 +1591,9 @@ Zotero.Utilities = {
|
|||
*/
|
||||
"itemToCSLJSON":function(zoteroItem) {
|
||||
if (zoteroItem instanceof Zotero.Item) {
|
||||
return Zotero.Utilities.Internal.itemToExportFormat(zoteroItem).
|
||||
then(Zotero.Utilities.itemToCSLJSON);
|
||||
return this.itemToCSLJSON(
|
||||
Zotero.Utilities.Internal.itemToExportFormat(zoteroItem)
|
||||
);
|
||||
}
|
||||
|
||||
var cslType = CSL_TYPE_MAPPINGS[zoteroItem.itemType];
|
||||
|
|
|
@ -610,44 +610,7 @@ Zotero.Utilities.Internal = {
|
|||
* @param {Boolean} legacy Add mappings for legacy (pre-4.0.27) translators
|
||||
* @return {Promise<Object>}
|
||||
*/
|
||||
"itemToExportFormat": new function() {
|
||||
return Zotero.Promise.coroutine(function* (zoteroItem, legacy) {
|
||||
var item = yield zoteroItem.toJSON();
|
||||
|
||||
item.uri = Zotero.URI.getItemURI(zoteroItem);
|
||||
delete item.key;
|
||||
|
||||
if (!zoteroItem.isAttachment() && !zoteroItem.isNote()) {
|
||||
yield zoteroItem.loadChildItems();
|
||||
|
||||
// Include attachments
|
||||
item.attachments = [];
|
||||
let attachments = zoteroItem.getAttachments();
|
||||
for (let i=0; i<attachments.length; i++) {
|
||||
let zoteroAttachment = yield Zotero.Items.getAsync(attachments[i]),
|
||||
attachment = yield zoteroAttachment.toJSON();
|
||||
if (legacy) addCompatibilityMappings(attachment, zoteroAttachment);
|
||||
|
||||
item.attachments.push(attachment);
|
||||
}
|
||||
|
||||
// Include notes
|
||||
item.notes = [];
|
||||
let notes = zoteroItem.getNotes();
|
||||
for (let i=0; i<notes.length; i++) {
|
||||
let zoteroNote = yield Zotero.Items.getAsync(notes[i]),
|
||||
note = yield zoteroNote.toJSON();
|
||||
if (legacy) addCompatibilityMappings(note, zoteroNote);
|
||||
|
||||
item.notes.push(note);
|
||||
}
|
||||
}
|
||||
|
||||
if (legacy) addCompatibilityMappings(item, zoteroItem);
|
||||
|
||||
return item;
|
||||
});
|
||||
|
||||
itemToExportFormat: function (zoteroItem, legacy) {
|
||||
function addCompatibilityMappings(item, zoteroItem) {
|
||||
item.uniqueFields = {};
|
||||
|
||||
|
@ -735,6 +698,39 @@ Zotero.Utilities.Internal = {
|
|||
|
||||
return item;
|
||||
}
|
||||
|
||||
var item = zoteroItem.toJSON();
|
||||
|
||||
item.uri = Zotero.URI.getItemURI(zoteroItem);
|
||||
delete item.key;
|
||||
|
||||
if (!zoteroItem.isAttachment() && !zoteroItem.isNote()) {
|
||||
// Include attachments
|
||||
item.attachments = [];
|
||||
let attachments = zoteroItem.getAttachments();
|
||||
for (let i=0; i<attachments.length; i++) {
|
||||
let zoteroAttachment = Zotero.Items.get(attachments[i]),
|
||||
attachment = zoteroAttachment.toJSON();
|
||||
if (legacy) addCompatibilityMappings(attachment, zoteroAttachment);
|
||||
|
||||
item.attachments.push(attachment);
|
||||
}
|
||||
|
||||
// Include notes
|
||||
item.notes = [];
|
||||
let notes = zoteroItem.getNotes();
|
||||
for (let i=0; i<notes.length; i++) {
|
||||
let zoteroNote = Zotero.Items.get(notes[i]),
|
||||
note = zoteroNote.toJSON();
|
||||
if (legacy) addCompatibilityMappings(note, zoteroNote);
|
||||
|
||||
item.notes.push(note);
|
||||
}
|
||||
}
|
||||
|
||||
if (legacy) addCompatibilityMappings(item, zoteroItem);
|
||||
|
||||
return item;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -621,11 +621,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();
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 + " "));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit b369f252432c3486a66a0e93f441e4abb133d229
|
||||
Subproject commit 775281e138df26101fba1e554c516f47438851b5
|
|
@ -1 +1 @@
|
|||
Subproject commit 2a8594424c73ffeca41ef1668446372160528b4a
|
||||
Subproject commit 44b0045463907b1d7963a2e9560c24d9552aac5d
|
|
@ -152,7 +152,6 @@ describe("Zotero.Collection", function() {
|
|||
var collection2 = yield createDataObject('collection', { parentID: collection1.id });
|
||||
yield collection1.saveTx();
|
||||
|
||||
yield collection1.loadChildCollections();
|
||||
var childCollections = collection1.getChildCollections();
|
||||
assert.lengthOf(childCollections, 1);
|
||||
assert.equal(childCollections[0].id, collection2.id);
|
||||
|
@ -163,8 +162,6 @@ describe("Zotero.Collection", function() {
|
|||
var collection2 = yield createDataObject('collection', { parentID: collection1.id });
|
||||
yield collection1.saveTx();
|
||||
|
||||
yield collection1.loadChildCollections();
|
||||
|
||||
collection2.parentID = false;
|
||||
yield collection2.save()
|
||||
|
||||
|
@ -180,7 +177,6 @@ describe("Zotero.Collection", function() {
|
|||
item.addToCollection(collection.key);
|
||||
yield item.saveTx();
|
||||
|
||||
yield collection.loadChildItems();
|
||||
assert.lengthOf(collection.getChildItems(), 1);
|
||||
})
|
||||
|
||||
|
@ -191,7 +187,6 @@ describe("Zotero.Collection", function() {
|
|||
item.addToCollection(collection.key);
|
||||
yield item.saveTx();
|
||||
|
||||
yield collection.loadChildItems();
|
||||
assert.lengthOf(collection.getChildItems(), 0);
|
||||
})
|
||||
|
||||
|
@ -202,7 +197,6 @@ describe("Zotero.Collection", function() {
|
|||
item.addToCollection(collection.key);
|
||||
yield item.saveTx();
|
||||
|
||||
yield collection.loadChildItems();
|
||||
assert.lengthOf(collection.getChildItems(false, true), 1);
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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]);
|
||||
|
|
21
test/tests/creatorsTest.js
Normal file
21
test/tests/creatorsTest.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
"use strict";
|
||||
|
||||
describe("Zotero.Creators", function() {
|
||||
describe("#getIDFromData()", function () {
|
||||
it("should create creator and cache data", function* () {
|
||||
var data1 = {
|
||||
firstName: "First",
|
||||
lastName: "Last"
|
||||
};
|
||||
var creatorID;
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
creatorID = yield Zotero.Creators.getIDFromData(data1, true);
|
||||
});
|
||||
assert.typeOf(creatorID, 'number');
|
||||
var data2 = Zotero.Creators.get(creatorID);
|
||||
assert.isObject(data2);
|
||||
assert.propertyVal(data2, "firstName", data1.firstName);
|
||||
assert.propertyVal(data2, "lastName", data1.lastName);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -56,7 +56,6 @@ describe("Zotero.DataObject", function() {
|
|||
yield obj.saveTx();
|
||||
|
||||
if (type == 'item') {
|
||||
yield obj.loadItemData();
|
||||
obj.setField('title', Zotero.Utilities.randomString());
|
||||
}
|
||||
else {
|
||||
|
@ -131,7 +130,6 @@ describe("Zotero.DataObject", function() {
|
|||
yield obj.saveTx();
|
||||
|
||||
if (type == 'item') {
|
||||
yield obj.loadItemData();
|
||||
obj.setField('title', Zotero.Utilities.randomString());
|
||||
}
|
||||
else {
|
||||
|
@ -294,7 +292,7 @@ describe("Zotero.DataObject", function() {
|
|||
let obj = yield createDataObject(type);
|
||||
let libraryID = obj.libraryID;
|
||||
let key = obj.key;
|
||||
let json = yield obj.toJSON();
|
||||
let json = obj.toJSON();
|
||||
yield Zotero.Sync.Data.Local.saveCacheObjects(type, libraryID, [json]);
|
||||
yield obj.eraseTx();
|
||||
let versions = yield Zotero.Sync.Data.Local.getCacheObjectVersions(
|
||||
|
|
|
@ -25,11 +25,11 @@ describe("Zotero.DataObjectUtilities", function() {
|
|||
yield Zotero.DB.executeTransaction(function* () {
|
||||
var item = new Zotero.Item('book');
|
||||
id1 = yield item.save();
|
||||
json1 = yield item.toJSON();
|
||||
json1 = item.toJSON();
|
||||
|
||||
var item = new Zotero.Item('book');
|
||||
id2 = yield item.save();
|
||||
json2 = yield item.toJSON();
|
||||
json2 = item.toJSON();
|
||||
});
|
||||
|
||||
var changes = Zotero.DataObjectUtilities.diff(json1, json2);
|
||||
|
|
|
@ -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"));
|
||||
|
|
56
test/tests/duplicatesTest.js
Normal file
56
test/tests/duplicatesTest.js
Normal file
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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* () {
|
||||
|
|
|
@ -148,7 +148,7 @@ describe("Sync Preferences", function () {
|
|||
var cont = yield win.Zotero_Preferences.Sync.checkUser(1, "A");
|
||||
assert.isTrue(cont);
|
||||
|
||||
var json = yield item1.toJSON();
|
||||
var json = item1.toJSON();
|
||||
var uri = json.relations[Zotero.Relations.linkedObjectPredicate][0];
|
||||
assert.notInclude(uri, 'users/local');
|
||||
assert.include(uri, 'users/1/publications');
|
||||
|
|
|
@ -71,12 +71,10 @@ describe("Related Box", function () {
|
|||
var item1 = yield createDataObject('item');
|
||||
var item2 = yield createDataObject('item');
|
||||
|
||||
yield item1.loadRelations();
|
||||
item1.addRelatedItem(item2);
|
||||
yield item1.save();
|
||||
yield item2.loadRelations();
|
||||
yield item1.saveTx();
|
||||
item2.addRelatedItem(item1);
|
||||
yield item2.save();
|
||||
yield item2.saveTx();
|
||||
|
||||
// Select the Related pane
|
||||
var tabbox = doc.getElementById('zotero-view-tabbox');
|
||||
|
|
|
@ -14,16 +14,20 @@ describe("Zotero.Search", function() {
|
|||
var s = new Zotero.Search;
|
||||
s.name = "Test";
|
||||
s.addCondition('title', 'is', 'test');
|
||||
Zotero.debug("BEFORE SAVING");
|
||||
Zotero.debug(s._conditions);
|
||||
var id = yield s.saveTx();
|
||||
Zotero.debug("DONE SAVING");
|
||||
Zotero.debug(s._conditions);
|
||||
assert.typeOf(id, 'number');
|
||||
|
||||
// Check saved search
|
||||
s = yield Zotero.Searches.getAsync(id);
|
||||
s = Zotero.Searches.get(id);
|
||||
assert.ok(s);
|
||||
assert.instanceOf(s, Zotero.Search);
|
||||
assert.equal(s.libraryID, Zotero.Libraries.userLibraryID);
|
||||
assert.equal(s.name, "Test");
|
||||
yield s.loadConditions();
|
||||
Zotero.debug("GETTING CONDITIONS");
|
||||
var conditions = s.getConditions();
|
||||
assert.lengthOf(Object.keys(conditions), 1);
|
||||
assert.property(conditions, "0");
|
||||
|
@ -45,14 +49,12 @@ describe("Zotero.Search", function() {
|
|||
|
||||
// Add condition
|
||||
s = yield Zotero.Searches.getAsync(id);
|
||||
yield s.loadConditions();
|
||||
s.addCondition('title', 'contains', 'foo');
|
||||
var saved = yield s.saveTx();
|
||||
assert.isTrue(saved);
|
||||
|
||||
// Check saved search
|
||||
s = yield Zotero.Searches.getAsync(id);
|
||||
yield s.loadConditions();
|
||||
var conditions = s.getConditions();
|
||||
assert.lengthOf(Object.keys(conditions), 2);
|
||||
});
|
||||
|
@ -69,14 +71,12 @@ describe("Zotero.Search", function() {
|
|||
|
||||
// Remove condition
|
||||
s = yield Zotero.Searches.getAsync(id);
|
||||
yield s.loadConditions();
|
||||
s.removeCondition(0);
|
||||
var saved = yield s.saveTx();
|
||||
assert.isTrue(saved);
|
||||
|
||||
// Check saved search
|
||||
s = yield Zotero.Searches.getAsync(id);
|
||||
yield s.loadConditions();
|
||||
var conditions = s.getConditions();
|
||||
assert.lengthOf(Object.keys(conditions), 1);
|
||||
assert.property(conditions, "0");
|
||||
|
|
|
@ -28,11 +28,10 @@ describe("Zotero.Sync.Storage.Local", function () {
|
|||
yield OS.File.setDates((yield item.getFilePathAsync()), null, mtime);
|
||||
|
||||
// Mark as synced, so it will be checked
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.Sync.Storage.Local.setSyncedHash(item.id, hash);
|
||||
yield Zotero.Sync.Storage.Local.setSyncedModificationTime(item.id, mtime);
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "in_sync");
|
||||
});
|
||||
item.attachmentSyncedModificationTime = mtime;
|
||||
item.attachmentSyncedHash = hash;
|
||||
item.attachmentSyncState = "in_sync";
|
||||
yield item.saveTx({ skipAll: true });
|
||||
|
||||
// Update mtime and contents
|
||||
var path = yield item.getFilePathAsync();
|
||||
|
@ -46,10 +45,7 @@ describe("Zotero.Sync.Storage.Local", function () {
|
|||
yield item.eraseTx();
|
||||
|
||||
assert.equal(changed, true);
|
||||
assert.equal(
|
||||
(yield Zotero.Sync.Storage.Local.getSyncState(item.id)),
|
||||
Zotero.Sync.Storage.Local.SYNC_STATE_TO_UPLOAD
|
||||
);
|
||||
assert.equal(item.attachmentSyncState, Zotero.Sync.Storage.Local.SYNC_STATE_TO_UPLOAD);
|
||||
})
|
||||
|
||||
it("should skip a file if mod time hasn't changed", function* () {
|
||||
|
@ -59,15 +55,14 @@ describe("Zotero.Sync.Storage.Local", function () {
|
|||
var mtime = yield item.attachmentModificationTime;
|
||||
|
||||
// Mark as synced, so it will be checked
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.Sync.Storage.Local.setSyncedHash(item.id, hash);
|
||||
yield Zotero.Sync.Storage.Local.setSyncedModificationTime(item.id, mtime);
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "in_sync");
|
||||
});
|
||||
item.attachmentSyncedModificationTime = mtime;
|
||||
item.attachmentSyncedHash = hash;
|
||||
item.attachmentSyncState = "in_sync";
|
||||
yield item.saveTx({ skipAll: true });
|
||||
|
||||
var libraryID = Zotero.Libraries.userLibraryID;
|
||||
var changed = yield Zotero.Sync.Storage.Local.checkForUpdatedFiles(libraryID);
|
||||
var syncState = yield Zotero.Sync.Storage.Local.getSyncState(item.id);
|
||||
var syncState = item.attachmentSyncState;
|
||||
|
||||
yield item.eraseTx();
|
||||
|
||||
|
@ -84,11 +79,10 @@ describe("Zotero.Sync.Storage.Local", function () {
|
|||
yield OS.File.setDates((yield item.getFilePathAsync()), null, mtime);
|
||||
|
||||
// Mark as synced, so it will be checked
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.Sync.Storage.Local.setSyncedHash(item.id, hash);
|
||||
yield Zotero.Sync.Storage.Local.setSyncedModificationTime(item.id, mtime);
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "in_sync");
|
||||
});
|
||||
item.attachmentSyncedModificationTime = mtime;
|
||||
item.attachmentSyncedHash = hash;
|
||||
item.attachmentSyncState = "in_sync";
|
||||
yield item.saveTx({ skipAll: true });
|
||||
|
||||
// Update mtime, but not contents
|
||||
var path = yield item.getFilePathAsync();
|
||||
|
@ -96,8 +90,8 @@ describe("Zotero.Sync.Storage.Local", function () {
|
|||
|
||||
var libraryID = Zotero.Libraries.userLibraryID;
|
||||
var changed = yield Zotero.Sync.Storage.Local.checkForUpdatedFiles(libraryID);
|
||||
var syncState = yield Zotero.Sync.Storage.Local.getSyncState(item.id);
|
||||
var syncedModTime = yield Zotero.Sync.Storage.Local.getSyncedModificationTime(item.id);
|
||||
var syncState = item.attachmentSyncState;
|
||||
var syncedModTime = item.attachmentSyncedModificationTime;
|
||||
var newModTime = yield item.attachmentModificationTime;
|
||||
|
||||
yield item.eraseTx();
|
||||
|
@ -202,8 +196,8 @@ describe("Zotero.Sync.Storage.Local", function () {
|
|||
item3.version = 11;
|
||||
yield item3.saveTx();
|
||||
|
||||
var json1 = yield item1.toJSON();
|
||||
var json3 = yield item3.toJSON();
|
||||
var json1 = item1.toJSON();
|
||||
var json3 = item3.toJSON();
|
||||
// Change remote mtimes
|
||||
// Round to nearest second because OS X doesn't support ms resolution
|
||||
var now = Math.round(new Date().getTime() / 1000) * 1000;
|
||||
|
@ -211,8 +205,10 @@ describe("Zotero.Sync.Storage.Local", function () {
|
|||
json3.mtime = now - 20000;
|
||||
yield Zotero.Sync.Data.Local.saveCacheObjects('item', libraryID, [json1, json3]);
|
||||
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item1.id, "in_conflict");
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item3.id, "in_conflict");
|
||||
item1.attachmentSyncState = "in_conflict";
|
||||
yield item1.saveTx({ skipAll: true });
|
||||
item3.attachmentSyncState = "in_conflict";
|
||||
yield item3.saveTx({ skipAll: true });
|
||||
|
||||
var conflicts = yield Zotero.Sync.Storage.Local.getConflicts(libraryID);
|
||||
assert.lengthOf(conflicts, 2);
|
||||
|
@ -251,19 +247,17 @@ describe("Zotero.Sync.Storage.Local", function () {
|
|||
item3.version = 11;
|
||||
yield item3.saveTx();
|
||||
|
||||
var json1 = yield item1.toJSON();
|
||||
var json3 = yield item3.toJSON();
|
||||
var json1 = item1.toJSON();
|
||||
var json3 = item3.toJSON();
|
||||
// Change remote mtimes
|
||||
json1.mtime = new Date().getTime() + 10000;
|
||||
json3.mtime = new Date().getTime() - 10000;
|
||||
yield Zotero.Sync.Data.Local.saveCacheObjects('item', libraryID, [json1, json3]);
|
||||
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(
|
||||
item1.id, "in_conflict"
|
||||
);
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(
|
||||
item3.id, "in_conflict"
|
||||
);
|
||||
item1.attachmentSyncState = "in_conflict";
|
||||
yield item1.saveTx({ skipAll: true });
|
||||
item3.attachmentSyncState = "in_conflict";
|
||||
yield item3.saveTx({ skipAll: true });
|
||||
|
||||
var promise = waitForWindow('chrome://zotero/content/merge.xul', function (dialog) {
|
||||
var doc = dialog.document;
|
||||
|
@ -305,14 +299,8 @@ describe("Zotero.Sync.Storage.Local", function () {
|
|||
yield Zotero.Sync.Storage.Local.resolveConflicts(libraryID);
|
||||
yield promise;
|
||||
|
||||
yield assert.eventually.equal(
|
||||
Zotero.Sync.Storage.Local.getSyncState(item1.id),
|
||||
Zotero.Sync.Storage.Local.SYNC_STATE_FORCE_UPLOAD
|
||||
);
|
||||
yield assert.eventually.equal(
|
||||
Zotero.Sync.Storage.Local.getSyncState(item3.id),
|
||||
Zotero.Sync.Storage.Local.SYNC_STATE_FORCE_DOWNLOAD
|
||||
);
|
||||
assert.equal(item1.attachmentSyncState, Zotero.Sync.Storage.Local.SYNC_STATE_FORCE_UPLOAD);
|
||||
assert.equal(item3.attachmentSyncState, Zotero.Sync.Storage.Local.SYNC_STATE_FORCE_DOWNLOAD);
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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]);
|
||||
});
|
||||
});
|
||||
})
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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/");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -186,8 +186,8 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
|||
var item = new Zotero.Item("attachment");
|
||||
item.attachmentLinkMode = 'imported_file';
|
||||
item.attachmentPath = 'storage:test.txt';
|
||||
item.attachmentSyncState = "to_download";
|
||||
yield item.saveTx();
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "to_download");
|
||||
|
||||
setResponse({
|
||||
method: "GET",
|
||||
|
@ -217,8 +217,8 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
|||
var item = new Zotero.Item("attachment");
|
||||
item.attachmentLinkMode = 'imported_file';
|
||||
item.attachmentPath = 'storage:test.txt';
|
||||
item.attachmentSyncState = "to_download";
|
||||
yield item.saveTx();
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "to_download");
|
||||
|
||||
setResponse({
|
||||
method: "GET",
|
||||
|
@ -251,8 +251,8 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
|||
var item = new Zotero.Item("attachment");
|
||||
item.attachmentLinkMode = 'imported_file';
|
||||
item.attachmentPath = 'storage:test.txt';
|
||||
item.attachmentSyncState = "to_download";
|
||||
yield item.saveTx();
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "to_download");
|
||||
|
||||
setResponse({
|
||||
method: "GET",
|
||||
|
@ -298,8 +298,8 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
|||
item.attachmentPath = 'storage:' + fileName;
|
||||
// TODO: Test binary data
|
||||
var text = Zotero.Utilities.randomString();
|
||||
item.attachmentSyncState = "to_download";
|
||||
yield item.saveTx();
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "to_download");
|
||||
|
||||
// Create ZIP file containing above text file
|
||||
var tmpPath = Zotero.getTempDirectory().path;
|
||||
|
@ -447,8 +447,8 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
|||
assert.isTrue(result.syncRequired);
|
||||
|
||||
// Check local objects
|
||||
assert.equal((yield Zotero.Sync.Storage.Local.getSyncedModificationTime(item.id)), mtime);
|
||||
assert.equal((yield Zotero.Sync.Storage.Local.getSyncedHash(item.id)), hash);
|
||||
assert.equal(item.attachmentSyncedModificationTime, mtime);
|
||||
assert.equal(item.attachmentSyncedHash, hash);
|
||||
assert.isFalse(item.synced);
|
||||
})
|
||||
|
||||
|
@ -464,12 +464,9 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
|||
var syncedModTime = Date.now() - 10000;
|
||||
var syncedHash = "3a2f092dd62178eb8bbfda42e07e64da";
|
||||
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
// Set an mtime in the past
|
||||
yield Zotero.Sync.Storage.Local.setSyncedModificationTime(item.id, syncedModTime);
|
||||
// And a different hash
|
||||
yield Zotero.Sync.Storage.Local.setSyncedHash(item.id, syncedHash);
|
||||
});
|
||||
item.attachmentSyncedModificationTime = syncedModTime;
|
||||
item.attachmentSyncedHash = syncedHash;
|
||||
yield item.saveTx({ skipAll: true });
|
||||
|
||||
var mtime = yield item.attachmentModificationTime;
|
||||
var hash = yield item.attachmentHash;
|
||||
|
@ -507,8 +504,8 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
|||
assert.isFalse(result.fileSyncRequired);
|
||||
|
||||
// Check local objects
|
||||
assert.equal((yield Zotero.Sync.Storage.Local.getSyncedModificationTime(item.id)), mtime);
|
||||
assert.equal((yield Zotero.Sync.Storage.Local.getSyncedHash(item.id)), hash);
|
||||
assert.equal(item.attachmentSyncedModificationTime, mtime);
|
||||
assert.equal(item.attachmentSyncedHash, hash);
|
||||
assert.isFalse(item.synced);
|
||||
})
|
||||
|
||||
|
@ -547,8 +544,8 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
|||
assert.isFalse(result.syncRequired);
|
||||
|
||||
// Check local object
|
||||
assert.equal((yield Zotero.Sync.Storage.Local.getSyncedModificationTime(item.id)), mtime);
|
||||
assert.equal((yield Zotero.Sync.Storage.Local.getSyncedHash(item.id)), hash);
|
||||
assert.equal(item.attachmentSyncedModificationTime, mtime);
|
||||
assert.equal(item.attachmentSyncedHash, hash);
|
||||
assert.isFalse(item.synced);
|
||||
})
|
||||
|
||||
|
@ -593,15 +590,10 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
|||
// Check local object
|
||||
//
|
||||
// Item should be marked as in conflict
|
||||
assert.equal(
|
||||
(yield Zotero.Sync.Storage.Local.getSyncState(item.id)),
|
||||
Zotero.Sync.Storage.Local.SYNC_STATE_IN_CONFLICT
|
||||
);
|
||||
assert.equal(item.attachmentSyncState, Zotero.Sync.Storage.Local.SYNC_STATE_IN_CONFLICT);
|
||||
// Synced mod time should have been changed, because that's what's shown in the
|
||||
// conflict dialog
|
||||
assert.equal(
|
||||
(yield Zotero.Sync.Storage.Local.getSyncedModificationTime(item.id)), newModTime
|
||||
);
|
||||
assert.equal(item.attachmentSyncedModificationTime, newModTime);
|
||||
assert.isTrue(item.synced);
|
||||
})
|
||||
})
|
||||
|
|
|
@ -143,8 +143,8 @@ describe("Zotero.Sync.Storage.Mode.ZFS", function () {
|
|||
var item = new Zotero.Item("attachment");
|
||||
item.attachmentLinkMode = 'imported_file';
|
||||
item.attachmentPath = 'storage:test.txt';
|
||||
item.attachmentSyncState = "to_download";
|
||||
yield item.saveTx();
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "to_download");
|
||||
|
||||
this.httpd.registerPathHandler(
|
||||
`/users/1/items/${item.key}/file`,
|
||||
|
@ -175,8 +175,8 @@ describe("Zotero.Sync.Storage.Mode.ZFS", function () {
|
|||
var item = new Zotero.Item("attachment");
|
||||
item.attachmentLinkMode = 'imported_file';
|
||||
item.attachmentPath = 'storage:test.txt';
|
||||
item.attachmentSyncState = "to_download";
|
||||
yield item.saveTx();
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "to_download");
|
||||
|
||||
this.httpd.registerPathHandler(
|
||||
`/users/1/items/${item.key}/file`,
|
||||
|
@ -208,8 +208,8 @@ describe("Zotero.Sync.Storage.Mode.ZFS", function () {
|
|||
item.attachmentPath = 'storage:test.txt';
|
||||
// TODO: Test binary data
|
||||
var text = Zotero.Utilities.randomString();
|
||||
item.attachmentSyncState = "to_download";
|
||||
yield item.saveTx();
|
||||
yield Zotero.Sync.Storage.Local.setSyncState(item.id, "to_download");
|
||||
|
||||
var mtime = "1441252524905";
|
||||
var md5 = Zotero.Utilities.Internal.md5(text)
|
||||
|
@ -553,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);
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue
Block a user