/)) {
+ note = Zotero.Utilities.htmlSpecialChars(note);
+ note = Zotero.Notes.notePrefix + '
');
+ notesToUpdate.push([item.id, note]);
+ }
+
+ // Don't include
wrapper when returning value
+ let startLen = note.substr(0, 36).match(/^
/)[0].length;
+ let endLen = 6; // "
".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
*
diff --git a/chrome/content/zotero/xpcom/data/library.js b/chrome/content/zotero/xpcom/data/library.js
index f9b769a63..d9b28e9ae 100644
--- a/chrome/content/zotero/xpcom/data/library.js
+++ b/chrome/content/zotero/xpcom/data/library.js
@@ -315,6 +315,16 @@ Zotero.Library.prototype._reloadFromDB = Zotero.Promise.coroutine(function* () {
this._loadDataFromRow(row);
});
+/**
+ * Load object data in this library
+ */
+Zotero.Library.prototype.loadAllDataTypes = Zotero.Promise.coroutine(function* () {
+ yield Zotero.SyncedSettings.loadAll(this.libraryID);
+ yield Zotero.Collections.loadAll(this.libraryID);
+ yield Zotero.Searches.loadAll(this.libraryID);
+ yield Zotero.Items.loadAll(this.libraryID);
+});
+
Zotero.Library.prototype.isChildObjectAllowed = function(type) {
return this._childObjectTypes.indexOf(type) != -1;
};
@@ -461,6 +471,8 @@ Zotero.Library.prototype._finalizeSave = Zotero.Promise.coroutine(function* (env
yield this._reloadFromDB();
Zotero.Libraries.register(this);
+
+ yield this.loadAllDataTypes();
}
});
diff --git a/chrome/content/zotero/xpcom/data/tags.js b/chrome/content/zotero/xpcom/data/tags.js
index d3ed75052..18a141816 100644
--- a/chrome/content/zotero/xpcom/data/tags.js
+++ b/chrome/content/zotero/xpcom/data/tags.js
@@ -77,7 +77,7 @@ Zotero.Tags = new function() {
/**
- * Get all tags indexed by tagID
+ * Get all tags in library
*
* @param {Number} libraryID
* @param {Array} [types] Tag types to fetch
@@ -181,7 +181,7 @@ Zotero.Tags = new function() {
// We need to know if the old tag has a color assigned so that
// we can assign it to the new name
- var oldColorData = yield this.getColor(libraryID, oldName);
+ var oldColorData = this.getColor(libraryID, oldName);
yield Zotero.DB.executeTransaction(function* () {
var oldItemIDs = yield this.getTagItems(libraryID, oldTagID);
@@ -393,13 +393,13 @@ Zotero.Tags = new function() {
*
* @param {Integer} libraryID
* @param {String} name Tag name
- * @return {Promise} A Q promise for the tag color as a hex string (e.g., '#990000')
+ * @return {Object|false} An object containing 'color' as a hex string (e.g., '#990000') and
+ * 'position', or false if no colored tag with that name
*/
this.getColor = function (libraryID, name) {
- return this.getColors(libraryID)
- .then(function () {
- return _libraryColorsByName[libraryID].get(name) || false;
- });
+ // Cache colors
+ this.getColors(libraryID);
+ return _libraryColorsByName[libraryID].get(name) || false;
}
@@ -408,14 +408,12 @@ Zotero.Tags = new function() {
*
* @param {Integer} libraryID
* @param {Integer} position The position of the tag, starting at 0
- * @return {Promise} A promise for an object containing 'name' and 'color'
+ * @return {Object|false} An object containing 'name' and 'color', or false if no color at
+ * the given position
*/
this.getColorByPosition = function (libraryID, position) {
- return this.getColors(libraryID)
- .then(function () {
- return _libraryColors[libraryID][position]
- ? _libraryColors[libraryID][position] : false;
- });
+ this.getColors(libraryID);
+ return _libraryColors[libraryID][position] ? _libraryColors[libraryID][position] : false;
}
@@ -423,20 +421,19 @@ Zotero.Tags = new function() {
* Get colored tags within a given library
*
* @param {Integer} libraryID
- * @return {Promise