Closes #711, Remove support for nested transactions
This commit is contained in:
parent
e584dbf5dd
commit
14d435b8d8
23 changed files with 862 additions and 856 deletions
|
@ -1109,7 +1109,7 @@
|
|||
this.item.setType(itemTypeID);
|
||||
|
||||
if (this.saveOnEdit) {
|
||||
this.item.save();
|
||||
this.item.saveTx();
|
||||
}
|
||||
else {
|
||||
this.refresh();
|
||||
|
@ -1357,7 +1357,7 @@
|
|||
return;
|
||||
}
|
||||
this.item.removeCreator(index);
|
||||
this.item.save();
|
||||
this.item.saveTx();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
@ -1766,7 +1766,7 @@
|
|||
}
|
||||
|
||||
if (this.saveOnEdit) {
|
||||
yield this.item.save();
|
||||
yield this.item.saveTx();
|
||||
}
|
||||
|
||||
var val = this.item.getCreator(creatorIndex);
|
||||
|
@ -1915,7 +1915,7 @@
|
|||
return Zotero.spawn(function* () {
|
||||
this.item.setField(field, value);
|
||||
if (save) {
|
||||
yield this.item.save();
|
||||
yield this.item.saveTx();
|
||||
}
|
||||
}, this);
|
||||
]]></body>
|
||||
|
@ -2034,14 +2034,14 @@
|
|||
}
|
||||
this.item.removeCreator(index);
|
||||
if (this.saveOnEdit && !skipSave) {
|
||||
return this.item.save();
|
||||
return this.item.saveTx();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var changed = this.item.setCreator(index, fields);
|
||||
if (changed && this.saveOnEdit && !skipSave) {
|
||||
return this.item.save();
|
||||
return this.item.saveTx();
|
||||
}
|
||||
}.bind(this));
|
||||
]]></body>
|
||||
|
@ -2091,7 +2091,7 @@
|
|||
this.item.setCreator(newIndex, a);
|
||||
this.item.setCreator(index, b);
|
||||
if (this.saveOnEdit) {
|
||||
return this.item.save();
|
||||
return this.item.saveTx();
|
||||
}
|
||||
}.bind(this));
|
||||
]]>
|
||||
|
|
|
@ -239,7 +239,7 @@
|
|||
|
||||
let changed = this.item.setNote(noteField.value);
|
||||
if (changed && this.saveOnEdit) {
|
||||
yield this.item.save();
|
||||
yield this.item.saveTx();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -254,7 +254,7 @@
|
|||
item.parentKey = this.parentItem.key;
|
||||
}
|
||||
if (this.saveOnEdit) {
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
|
||||
if (!this.parentItem && this.collection) {
|
||||
this.collection.addItem(id);
|
||||
|
|
|
@ -333,7 +333,7 @@
|
|||
if (tagData) {
|
||||
let item = document.getBindingParent(this).item
|
||||
item.removeTag(tagName);
|
||||
yield item.save()
|
||||
yield item.saveTx()
|
||||
}
|
||||
|
||||
// Return focus to items pane
|
||||
|
@ -682,13 +682,13 @@
|
|||
if (oldValue !== value) {
|
||||
// The existing textbox will be removed in notify()
|
||||
this.item.replaceTag(oldValue, value);
|
||||
yield this.item.save();
|
||||
yield this.item.saveTx();
|
||||
}
|
||||
}
|
||||
// Existing tag cleared
|
||||
else {
|
||||
this.item.removeTag(oldValue);
|
||||
yield this.item.save();
|
||||
yield this.item.saveTx();
|
||||
}
|
||||
}
|
||||
// Multiple tags
|
||||
|
@ -710,7 +710,7 @@
|
|||
}
|
||||
|
||||
this.item.addTags(tags);
|
||||
yield this.item.save();
|
||||
yield this.item.saveTx();
|
||||
|
||||
if (lastTag) {
|
||||
this._lastTabIndex = this.item.getTags().length;
|
||||
|
@ -724,7 +724,7 @@
|
|||
// if it doesn't already exist.
|
||||
row.parentNode.removeChild(row);
|
||||
this.item.addTag(value);
|
||||
yield this.item.save();
|
||||
yield this.item.saveTx();
|
||||
}
|
||||
}.bind(this));
|
||||
]]></body>
|
||||
|
|
|
@ -45,7 +45,7 @@ Zotero.Attachments = new function(){
|
|||
throw ("'" + file.leafName + "' must be a file in Zotero.Attachments.importFromFile()");
|
||||
}
|
||||
|
||||
var itemID, newFile;
|
||||
var itemID, newFile, contentType;
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
// Create a new attachment
|
||||
var attachmentItem = new Zotero.Item('attachment');
|
||||
|
@ -73,15 +73,15 @@ Zotero.Attachments = new function(){
|
|||
// Copy file to unique filename, which automatically shortens long filenames
|
||||
newFile = Zotero.File.copyToUnique(file, newFile);
|
||||
|
||||
var contentType = yield Zotero.MIME.getMIMETypeFromFile(newFile);
|
||||
contentType = yield Zotero.MIME.getMIMETypeFromFile(newFile);
|
||||
|
||||
attachmentItem.attachmentContentType = contentType;
|
||||
attachmentItem.attachmentPath = this.getPath(newFile, this.LINK_MODE_IMPORTED_FILE);
|
||||
yield attachmentItem.save();
|
||||
|
||||
// Determine charset and build fulltext index
|
||||
yield _postProcessFile(itemID, newFile, contentType);
|
||||
}.bind(this))
|
||||
.then(function () {
|
||||
return _postProcessFile(itemID, newFile, contentType);
|
||||
})
|
||||
.catch(function (e) {
|
||||
Zotero.debug(e, 1);
|
||||
var msg = "Failed importing file " + file.path;
|
||||
|
@ -112,21 +112,21 @@ Zotero.Attachments = new function(){
|
|||
|
||||
var title = file.leafName;
|
||||
var contentType = yield Zotero.MIME.getMIMETypeFromFile(file);
|
||||
var itemID;
|
||||
|
||||
return Zotero.DB.executeTransaction(function* () {
|
||||
var itemID = yield _addToDB({
|
||||
itemID = yield _addToDB({
|
||||
file: file,
|
||||
title: title,
|
||||
linkMode: this.LINK_MODE_LINKED_FILE,
|
||||
contentType: contentType,
|
||||
parentItemID: parentItemID
|
||||
});
|
||||
|
||||
// Determine charset and build fulltext index
|
||||
yield _postProcessFile(itemID, file, contentType);
|
||||
|
||||
return itemID;
|
||||
}.bind(this));
|
||||
}.bind(this))
|
||||
.then(function () {
|
||||
return _postProcessFile(itemID, file, contentType);
|
||||
})
|
||||
.then(() => itemID);
|
||||
});
|
||||
|
||||
|
||||
|
@ -148,7 +148,7 @@ Zotero.Attachments = new function(){
|
|||
throw new Error("parentItemID not provided");
|
||||
}
|
||||
|
||||
var destDir;
|
||||
var itemID, destDir, newFile;
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
// Create a new attachment
|
||||
var attachmentItem = new Zotero.Item('attachment');
|
||||
|
@ -164,7 +164,7 @@ Zotero.Attachments = new function(){
|
|||
// DEBUG: this should probably insert access date too so as to
|
||||
// create a proper item, but at the moment this is only called by
|
||||
// translate.js, which sets the metadata fields itself
|
||||
var itemID = yield attachmentItem.save();
|
||||
itemID = yield attachmentItem.save();
|
||||
attachmentItem = yield Zotero.Items.getAsync(itemID)
|
||||
|
||||
destDir = this.getStorageDirectory(attachmentItem);
|
||||
|
@ -172,15 +172,15 @@ Zotero.Attachments = new function(){
|
|||
file.parent.copyTo(storageDir, destDir.leafName);
|
||||
|
||||
// Point to copied file
|
||||
var newFile = destDir.clone();
|
||||
newFile = destDir.clone();
|
||||
newFile.append(file.leafName);
|
||||
|
||||
attachmentItem.attachmentPath = this.getPath(newFile, this.LINK_MODE_IMPORTED_URL);
|
||||
yield attachmentItem.save();
|
||||
|
||||
// Determine charset and build fulltext index
|
||||
yield _postProcessFile(itemID, newFile, contentType);
|
||||
}.bind(this))
|
||||
.then(function () {
|
||||
return _postProcessFile(itemID, newFile, contentType);
|
||||
})
|
||||
.catch(function (e) {
|
||||
Zotero.debug(e, 1);
|
||||
|
||||
|
@ -1130,7 +1130,7 @@ Zotero.Attachments = new function(){
|
|||
if (parentItemID) {
|
||||
newAttachment.parentID = parentItemID;
|
||||
}
|
||||
yield newAttachment.save();
|
||||
yield newAttachment.saveTx();
|
||||
|
||||
// Copy over files if they exist
|
||||
if (newAttachment.isImportedAttachment() && attachment.getFile()) {
|
||||
|
@ -1362,7 +1362,7 @@ Zotero.Attachments = new function(){
|
|||
var item = yield Zotero.Items.getAsync(itemID);
|
||||
charset = yield Zotero.CharacterSets.add(charset);
|
||||
item.attachmentCharset = charset;
|
||||
yield item.save();
|
||||
yield item.saveTx();
|
||||
|
||||
if (disabled) {
|
||||
Zotero.Notifier.enable();
|
||||
|
|
|
@ -70,13 +70,14 @@ Zotero.Creators = new function() {
|
|||
/**
|
||||
* Returns the creatorID matching given fields, or creates a new creator and returns its id
|
||||
*
|
||||
* @requireTransaction
|
||||
* @param {Object} data Creator data in API JSON format
|
||||
* @param {Boolean} [create=false] If no matching creator, create one
|
||||
* @return {Promise<Integer>} creatorID
|
||||
*/
|
||||
this.getIDFromData = Zotero.Promise.method(function (data, create) {
|
||||
this.getIDFromData = Zotero.Promise.coroutine(function* (data, create) {
|
||||
Zotero.DB.requireTransaction();
|
||||
data = this.cleanData(data);
|
||||
return Zotero.DB.executeTransaction(function* () {
|
||||
var sql = "SELECT creatorID FROM creators WHERE "
|
||||
+ "firstName=? AND lastName=? AND fieldMode=?";
|
||||
var id = yield Zotero.DB.valueQueryAsync(
|
||||
|
@ -95,7 +96,6 @@ Zotero.Creators = new function() {
|
|||
}
|
||||
return id;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
this.updateCreator = Zotero.Promise.coroutine(function* (creatorID, creatorData) {
|
||||
|
|
|
@ -547,11 +547,19 @@ Zotero.DataObject.prototype.editCheck = function () {
|
|||
* TRUE on item update, or FALSE if item was unchanged
|
||||
*/
|
||||
Zotero.DataObject.prototype.save = Zotero.Promise.coroutine(function* (options) {
|
||||
options = options || {};
|
||||
var env = {
|
||||
options: options || {},
|
||||
options: options,
|
||||
transactionOptions: {}
|
||||
};
|
||||
|
||||
if (!env.options.tx && !Zotero.DB.inTransaction()) {
|
||||
Zotero.logError("save() called on Zotero." + this._ObjectType + " without a wrapping "
|
||||
+ "transaction -- use saveTx() instead");
|
||||
Zotero.debug((new Error).stack, 2);
|
||||
env.options.tx = true;
|
||||
}
|
||||
|
||||
var proceed = yield this._initSave(env);
|
||||
if (!proceed) return false;
|
||||
|
||||
|
@ -562,7 +570,7 @@ Zotero.DataObject.prototype.save = Zotero.Promise.coroutine(function* (options)
|
|||
Zotero.debug('Updating database with new ' + this._objectType + ' data', 4);
|
||||
}
|
||||
|
||||
return Zotero.DB.executeTransaction(function* () {
|
||||
try {
|
||||
if (Zotero.DataObject.prototype._finalizeSave == this._finalizeSave) {
|
||||
throw new Error("_finalizeSave not implement for Zotero." + this._ObjectType);
|
||||
}
|
||||
|
@ -574,26 +582,48 @@ Zotero.DataObject.prototype.save = Zotero.Promise.coroutine(function* (options)
|
|||
if (!env.isNew) {
|
||||
env.changed = this._previousData;
|
||||
}
|
||||
|
||||
// Create transaction
|
||||
if (env.options.tx) {
|
||||
let result = yield Zotero.DB.executeTransaction(function* () {
|
||||
yield this._saveData(env);
|
||||
yield Zotero.DataObject.prototype._finalizeSave.call(this, env);
|
||||
return this._finalizeSave(env);
|
||||
}.bind(this), env.transactionOptions)
|
||||
.catch(e => {
|
||||
}.bind(this), env.transactionOptions);
|
||||
return result;
|
||||
}
|
||||
// Use existing transaction
|
||||
else {
|
||||
Zotero.DB.requireTransaction();
|
||||
yield this._saveData(env);
|
||||
yield Zotero.DataObject.prototype._finalizeSave.call(this, env);
|
||||
return this._finalizeSave(env);
|
||||
}
|
||||
}
|
||||
catch(e) {
|
||||
return this._recoverFromSaveError(env, e)
|
||||
.catch(function(e2) {
|
||||
Zotero.debug(e2, 1);
|
||||
})
|
||||
.then(function() {
|
||||
if (options.errorHandler) {
|
||||
options.errorHandler(e);
|
||||
if (env.options.errorHandler) {
|
||||
env.options.errorHandler(e);
|
||||
}
|
||||
else {
|
||||
Zotero.debug(e, 1);
|
||||
}
|
||||
throw e;
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Zotero.DataObject.prototype.saveTx = function (options) {
|
||||
options = options || {};
|
||||
options.tx = true;
|
||||
return this.save(options);
|
||||
}
|
||||
|
||||
|
||||
Zotero.DataObject.prototype.hasChanged = function() {
|
||||
Zotero.debug(this._changed);
|
||||
|
@ -618,9 +648,15 @@ Zotero.DataObject.prototype._initSave = Zotero.Promise.coroutine(function* (env)
|
|||
}
|
||||
|
||||
// Undo registerIdentifiers() on failure
|
||||
env.transactionOptions.onRollback = function () {
|
||||
var func = function () {
|
||||
this.ObjectsClass.unload(env.id);
|
||||
}.bind(this);
|
||||
if (env.options.tx) {
|
||||
env.transactionOptions.onRollback = func;
|
||||
}
|
||||
else {
|
||||
Zotero.DB.addCurrentCallback("rollback", func);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
@ -653,11 +689,9 @@ Zotero.DataObject.prototype.erase = Zotero.Promise.coroutine(function* () {
|
|||
|
||||
Zotero.debug('Deleting ' + this.objectType + ' ' + this.id);
|
||||
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
Zotero.DB.requireTransaction();
|
||||
yield this._eraseData(env);
|
||||
yield this._erasePreCommit(env);
|
||||
}.bind(this));
|
||||
|
||||
return this._erasePostCommit(env);
|
||||
});
|
||||
|
||||
|
|
|
@ -1173,6 +1173,11 @@ Zotero.Item.prototype.isEditable = function() {
|
|||
}
|
||||
|
||||
Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
|
||||
// Sanity check
|
||||
if (!Zotero.DB.inTransaction()) {
|
||||
throw new Error("Not in transaction saving item " + this.libraryKey);
|
||||
}
|
||||
|
||||
var isNew = env.isNew;
|
||||
var options = env.options;
|
||||
|
||||
|
@ -1740,6 +1745,11 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
|
|||
parentItem.clearBestAttachmentState();
|
||||
}
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (!Zotero.DB.inTransaction()) {
|
||||
throw new Error("Not in transaction saving item " + this.libraryKey);
|
||||
}
|
||||
});
|
||||
|
||||
Zotero.Item.prototype._finalizeSave = Zotero.Promise.coroutine(function* (env) {
|
||||
|
|
|
@ -169,8 +169,8 @@ Zotero.Relations = function () {
|
|||
* @param {String} prefix
|
||||
* @param {String[]} ignorePredicates
|
||||
*/
|
||||
this.eraseByURIPrefix = function (prefix, ignorePredicates) {
|
||||
return Zotero.DB.executeTransaction(function* () {
|
||||
this.eraseByURIPrefix = Zotero.Promise.coroutine(function* (prefix, ignorePredicates) {
|
||||
Zotero.DB.requireTransaction();
|
||||
prefix = prefix + '%';
|
||||
var sql = "SELECT ROWID FROM relations WHERE (subject LIKE ? OR object LIKE ?)";
|
||||
var params = [prefix, prefix];
|
||||
|
@ -187,15 +187,14 @@ Zotero.Relations = function () {
|
|||
yield relation.load();
|
||||
yield relation.erase();
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* @return {Promise}
|
||||
*/
|
||||
this.eraseByURI = function (uri, ignorePredicates) {
|
||||
return Zotero.DB.executeTransaction(function* () {
|
||||
this.eraseByURI = Zotero.Promise.coroutine(function* (uri, ignorePredicates) {
|
||||
Zotero.DB.requireTransaction();
|
||||
var sql = "SELECT ROWID FROM relations WHERE (subject=? OR object=?)";
|
||||
var params = [uri, uri];
|
||||
if (ignorePredicates) {
|
||||
|
@ -211,8 +210,7 @@ Zotero.Relations = function () {
|
|||
yield relation.load();
|
||||
yield relation.erase();
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this.purge = Zotero.Promise.coroutine(function* () {
|
||||
|
|
|
@ -76,16 +76,17 @@ Zotero.Tags = new function() {
|
|||
/**
|
||||
* Returns the tagID matching given fields, or creates a new tag and returns its id
|
||||
*
|
||||
* @requireTransaction
|
||||
* @param {Number} libraryID
|
||||
* @param {String} name - Tag data in API JSON format
|
||||
* @param {Boolean} [create=false] - If no matching tag, create one
|
||||
* @return {Promise<Integer>} tagID
|
||||
*/
|
||||
this.getIDFromName = Zotero.Promise.method(function (libraryID, name, create) {
|
||||
this.getIDFromName = Zotero.Promise.coroutine(function* (libraryID, name, create) {
|
||||
Zotero.DB.requireTransaction();
|
||||
data = this.cleanData({
|
||||
tag: name
|
||||
});
|
||||
return Zotero.DB.executeTransaction(function* () {
|
||||
var sql = "SELECT tagID FROM tags WHERE libraryID=? AND name=?";
|
||||
var id = yield Zotero.DB.valueQueryAsync(sql, [libraryID, data.tag]);
|
||||
if (!id && create) {
|
||||
|
@ -99,7 +100,6 @@ Zotero.Tags = new function() {
|
|||
}
|
||||
return id;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
|
|
|
@ -297,6 +297,12 @@ Zotero.DBConnection.prototype.addCallback = function (type, cb) {
|
|||
}
|
||||
|
||||
|
||||
Zotero.DBConnection.prototype.addCurrentCallback = function (type, cb) {
|
||||
this.requireTransaction();
|
||||
this._callbacks.current[type].push(cb);
|
||||
}
|
||||
|
||||
|
||||
Zotero.DBConnection.prototype.removeCallback = function (type, id) {
|
||||
switch (type) {
|
||||
case 'begin':
|
||||
|
@ -453,41 +459,16 @@ Zotero.DBConnection.prototype.executeTransaction = Zotero.Promise.coroutine(func
|
|||
}
|
||||
}
|
||||
|
||||
if ((options.exclusive && this._inTransaction) || this._inExclusiveTransaction) {
|
||||
yield Zotero.DB.waitForTransaction();
|
||||
}
|
||||
var startedTransaction = false;
|
||||
|
||||
try {
|
||||
if (this._inTransaction) {
|
||||
Zotero.debug("Async DB transaction in progress -- increasing level to "
|
||||
+ ++this._asyncTransactionNestingLevel, 5);
|
||||
while (this._inTransaction) {
|
||||
yield Zotero.DB.waitForTransaction().timeout(options.waitTimeout || 10000);
|
||||
}
|
||||
this._inTransaction = startedTransaction = true;
|
||||
|
||||
if (options.onCommit) {
|
||||
this._callbacks.current.commit.push(options.onCommit);
|
||||
}
|
||||
if (options.onRollback) {
|
||||
this._callbacks.current.rollback.push(options.onRollback);
|
||||
}
|
||||
|
||||
try {
|
||||
var result = yield Zotero.Promise.coroutine(func)();
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.debug("Rolling back nested async DB transaction", 5);
|
||||
this._asyncTransactionNestingLevel = 0;
|
||||
throw e;
|
||||
}
|
||||
|
||||
Zotero.debug("Decreasing async DB transaction level to "
|
||||
+ --this._asyncTransactionNestingLevel, 5);
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
Zotero.debug("Beginning async DB transaction", 5);
|
||||
|
||||
this._inTransaction = true;
|
||||
this._inExclusiveTransaction = options.exclusive;
|
||||
|
||||
this._transactionPromise = new Zotero.Promise(function () {
|
||||
resolve = arguments[0];
|
||||
});
|
||||
|
@ -504,14 +485,19 @@ Zotero.DBConnection.prototype.executeTransaction = Zotero.Promise.coroutine(func
|
|||
var conn = this._getConnection(options) || (yield this._getConnectionAsync(options));
|
||||
var result = yield conn.executeTransaction(func);
|
||||
Zotero.debug("Committed async DB transaction", 5);
|
||||
this._inTransaction = false;
|
||||
|
||||
// Clear transaction time
|
||||
if (this._transactionDate) {
|
||||
this._transactionDate = null;
|
||||
}
|
||||
|
||||
if (options) {
|
||||
if (options.vacuumOnCommit) {
|
||||
Zotero.debug('Vacuuming database');
|
||||
yield Zotero.DB.queryAsync('VACUUM');
|
||||
}
|
||||
|
||||
this._inTransaction = false;
|
||||
|
||||
// Function to run once transaction has been committed but before any
|
||||
// permanent callbacks
|
||||
if (options.onCommit) {
|
||||
|
@ -519,12 +505,6 @@ Zotero.DBConnection.prototype.executeTransaction = Zotero.Promise.coroutine(func
|
|||
}
|
||||
this._callbacks.current.rollback = [];
|
||||
|
||||
if (options.vacuumOnCommit) {
|
||||
Zotero.debug('Vacuuming database');
|
||||
yield Zotero.DB.queryAsync('VACUUM');
|
||||
}
|
||||
}
|
||||
|
||||
// Run temporary commit callbacks
|
||||
var f;
|
||||
while (f = this._callbacks.current.commit.shift()) {
|
||||
|
@ -540,20 +520,23 @@ Zotero.DBConnection.prototype.executeTransaction = Zotero.Promise.coroutine(func
|
|||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
if (e.name == "TimeoutError") {
|
||||
Zotero.debug("Timed out waiting for transaction", 1);
|
||||
}
|
||||
else {
|
||||
Zotero.debug("Rolled back async DB transaction", 5);
|
||||
Zotero.debug(e, 1);
|
||||
}
|
||||
if (startedTransaction) {
|
||||
this._inTransaction = false;
|
||||
this._inExclusiveTransaction = false;
|
||||
}
|
||||
|
||||
if (options) {
|
||||
// Function to run once transaction has been committed but before any
|
||||
// permanent callbacks
|
||||
if (options.onRollback) {
|
||||
this._callbacks.current.rollback.push(options.onRollback);
|
||||
}
|
||||
}
|
||||
|
||||
// Run temporary commit callbacks
|
||||
var f;
|
||||
|
@ -586,15 +569,28 @@ Zotero.DBConnection.prototype.executeTransaction = Zotero.Promise.coroutine(func
|
|||
});
|
||||
|
||||
|
||||
Zotero.DBConnection.prototype.inTransaction = function () {
|
||||
return this._inTransaction;
|
||||
}
|
||||
|
||||
|
||||
Zotero.DBConnection.prototype.waitForTransaction = function () {
|
||||
if (!this._inTransaction) {
|
||||
return Zotero.Promise.resolve();
|
||||
return Zotero.Promise.resolve().cancellable();
|
||||
}
|
||||
Zotero.debug("Waiting for transaction to finish");
|
||||
Zotero.debug((new Error).stack);
|
||||
return this._transactionPromise;
|
||||
};
|
||||
|
||||
|
||||
Zotero.DBConnection.prototype.requireTransaction = function () {
|
||||
if (!this._inTransaction) {
|
||||
throw new Error("Not in transaction");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {String} sql SQL statement to run
|
||||
* @param {Array|String|Integer} [params] SQL parameters to bind
|
||||
|
@ -818,7 +814,7 @@ Zotero.DBConnection.prototype.tableExists = function (table) {
|
|||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
Zotero.DBConnection.prototype.executeSQLFile = function (sql) {
|
||||
Zotero.DBConnection.prototype.executeSQLFile = Zotero.Promise.coroutine(function* (sql) {
|
||||
var nonCommentRE = /^[^-]/;
|
||||
var trailingCommentRE = /^(.*?)(?:--.+)?$/;
|
||||
|
||||
|
@ -836,13 +832,13 @@ Zotero.DBConnection.prototype.executeSQLFile = function (sql) {
|
|||
var statements = sql.split(";")
|
||||
.map(function (x) x.replace(/TEMPSEMI/g, ";"));
|
||||
|
||||
return this.executeTransaction(function* () {
|
||||
this.requireTransaction();
|
||||
|
||||
var statement;
|
||||
while (statement = statements.shift()) {
|
||||
yield Zotero.DB.queryAsync(statement);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
|
|
@ -403,13 +403,14 @@ Zotero.Fulltext = new function(){
|
|||
/**
|
||||
* Index multiple words at once
|
||||
*
|
||||
* @requireTransaction
|
||||
* @param {Number} itemID
|
||||
* @param {Array<string>} words
|
||||
* @return {Promise}
|
||||
*/
|
||||
function indexWords(itemID, words) {
|
||||
var indexWords = Zotero.Promise.coroutine(function* (itemID, words) {
|
||||
Zotero.DB.requireTransaction();
|
||||
let chunk;
|
||||
return Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.DB.queryAsync("DELETE FROM indexing.fulltextWords");
|
||||
while (words.length > 0) {
|
||||
chunk = words.splice(0, 100);
|
||||
|
@ -420,8 +421,7 @@ Zotero.Fulltext = new function(){
|
|||
yield Zotero.DB.queryAsync('INSERT OR IGNORE INTO fulltextItemWords (wordID, itemID) SELECT wordID, ? FROM fulltextWords JOIN indexing.fulltextWords USING(word)', [itemID]);
|
||||
yield Zotero.DB.queryAsync("REPLACE INTO fulltextItems (itemID, version) VALUES (?,?)", [itemID, 0]);
|
||||
yield Zotero.DB.queryAsync("DELETE FROM indexing.fulltextWords");
|
||||
}.bind(this));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
|
@ -559,7 +559,6 @@ Zotero.Fulltext = new function(){
|
|||
}
|
||||
}
|
||||
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield indexString(text, charset, itemID);
|
||||
|
||||
// Record the number of characters indexed (unless we're indexing a (PDF) cache file,
|
||||
|
@ -567,7 +566,6 @@ Zotero.Fulltext = new function(){
|
|||
if (!isCacheFile) {
|
||||
yield setChars(itemID, { indexed: text.length, total: totalChars });
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}.bind(this));
|
||||
|
@ -690,10 +688,8 @@ Zotero.Fulltext = new function(){
|
|||
return false;
|
||||
}
|
||||
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield indexFile(cacheFilePath, 'text/plain', 'utf-8', itemID, true, true);
|
||||
yield setPages(itemID, { indexed: pagesIndexed, total: totalPages });
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
|
@ -827,7 +823,9 @@ Zotero.Fulltext = new function(){
|
|||
+ libraryKey, 2);
|
||||
|
||||
// Delete rows for items that weren't supposed to be indexed
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield this.clearItemWords(itemID);
|
||||
}.bind(this));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1291,16 +1289,18 @@ Zotero.Fulltext = new function(){
|
|||
});
|
||||
|
||||
|
||||
/**
|
||||
* @requireTransaction
|
||||
*/
|
||||
this.clearItemWords = Zotero.Promise.coroutine(function* (itemID, skipCacheClear) {
|
||||
var indexed = yield Zotero.DB.executeTransaction(function* () {
|
||||
Zotero.DB.requireTransaction();
|
||||
|
||||
var sql = "SELECT rowid FROM fulltextItems WHERE itemID=? LIMIT 1";
|
||||
var indexed = yield Zotero.DB.valueQueryAsync(sql, itemID);
|
||||
if (indexed) {
|
||||
yield Zotero.DB.queryAsync("DELETE FROM fulltextItemWords WHERE itemID=?", itemID);
|
||||
yield Zotero.DB.queryAsync("DELETE FROM fulltextItems WHERE itemID=?", itemID);
|
||||
}
|
||||
return indexed;
|
||||
}.bind(this));
|
||||
|
||||
if (indexed) {
|
||||
Zotero.Prefs.set('purge.fulltext', true);
|
||||
|
|
|
@ -123,21 +123,19 @@ Zotero.Schema = new function(){
|
|||
// Update custom tables if they exist so that changes are in
|
||||
// place before user data migration
|
||||
if (Zotero.DB.tableExists('customItemTypes')) {
|
||||
yield Zotero.Schema.updateCustomTables(updated);
|
||||
yield _updateCustomTables(updated);
|
||||
}
|
||||
updated = yield _migrateUserDataSchema(userdata);
|
||||
yield _updateSchema('triggers');
|
||||
|
||||
// Populate combined tables for custom types and fields -- this is likely temporary
|
||||
//
|
||||
// We do this again in case custom fields were changed during user data migration
|
||||
yield _updateCustomTables()
|
||||
|
||||
return updated;
|
||||
}.bind(this));
|
||||
|
||||
// Populate combined tables for custom types and fields
|
||||
// -- this is likely temporary
|
||||
//
|
||||
// We do this even if updated in case custom fields were
|
||||
// changed during user data migration
|
||||
yield Zotero.Schema.updateCustomTables()
|
||||
|
||||
if (updated) {
|
||||
// Upgrade seems to have been a success -- delete any previous backups
|
||||
var maxPrevious = userdata - 1;
|
||||
|
@ -318,7 +316,7 @@ Zotero.Schema = new function(){
|
|||
});
|
||||
|
||||
var _reloadSchema = Zotero.Promise.coroutine(function* () {
|
||||
yield Zotero.Schema.updateCustomTables();
|
||||
yield _updateCustomTables();
|
||||
yield Zotero.ItemTypes.load();
|
||||
yield Zotero.ItemFields.load();
|
||||
yield Zotero.SearchConditions.init();
|
||||
|
@ -335,10 +333,11 @@ Zotero.Schema = new function(){
|
|||
});
|
||||
|
||||
|
||||
this.updateCustomTables = function (skipDelete, skipSystem) {
|
||||
return Zotero.DB.executeTransaction(function* (conn) {
|
||||
var _updateCustomTables = Zotero.Promise.coroutine(function* (skipDelete, skipSystem) {
|
||||
Zotero.debug("Updating custom tables");
|
||||
|
||||
Zotero.DB.requireTransaction();
|
||||
|
||||
if (!skipDelete) {
|
||||
yield Zotero.DB.queryAsync("DELETE FROM itemTypesCombined");
|
||||
yield Zotero.DB.queryAsync("DELETE FROM fieldsCombined WHERE fieldID NOT IN (SELECT fieldID FROM itemData)");
|
||||
|
@ -386,7 +385,6 @@ Zotero.Schema = new function(){
|
|||
+ "customFieldID + " + offset + " AS fieldID FROM customBaseFieldMappings"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -1454,7 +1452,7 @@ Zotero.Schema = new function(){
|
|||
yield _getSchemaSQL('triggers').then(function (sql) {
|
||||
return Zotero.DB.executeSQLFile(sql);
|
||||
});
|
||||
yield Zotero.Schema.updateCustomTables(true);
|
||||
yield _updateCustomTables(true);
|
||||
|
||||
yield _getSchemaSQLVersion('system').then(function (version) {
|
||||
return _updateDBVersion('system', version);
|
||||
|
@ -1918,16 +1916,17 @@ Zotero.Schema = new function(){
|
|||
//
|
||||
// If libraryID set, make sure no relations still use a local user key, and then remove on-error code in sync.js
|
||||
|
||||
function _migrateUserDataSchema(fromVersion) {
|
||||
return _getSchemaSQLVersion('userdata')
|
||||
.then(function (toVersion) {
|
||||
var _migrateUserDataSchema = Zotero.Promise.coroutine(function* (fromVersion) {
|
||||
var toVersion = yield _getSchemaSQLVersion('userdata');
|
||||
|
||||
if (fromVersion >= toVersion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Zotero.debug('Updating user data tables from version ' + fromVersion + ' to ' + toVersion);
|
||||
|
||||
return Zotero.DB.executeTransaction(function* (conn) {
|
||||
Zotero.DB.requireTransaction();
|
||||
|
||||
// Step through version changes until we reach the current version
|
||||
//
|
||||
// Each block performs the changes necessary to move from the
|
||||
|
@ -2270,11 +2269,10 @@ Zotero.Schema = new function(){
|
|||
yield Zotero.DB.queryAsync("DROP TABLE itemsOld");
|
||||
yield Zotero.DB.queryAsync("DROP TABLE tagsOld");
|
||||
}
|
||||
}
|
||||
|
||||
yield _updateDBVersion('userdata', toVersion);
|
||||
})
|
||||
.return(true);
|
||||
})
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -947,7 +947,7 @@ Zotero.Search.idsToTempTable = function (ids) {
|
|||
yield Zotero.DB.queryAsync(sql);
|
||||
|
||||
return tmpTable;
|
||||
}, { exclusive: true });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -740,8 +740,6 @@ var ZoteroPane = new function()
|
|||
*/
|
||||
this.newItem = Zotero.Promise.coroutine(function* (typeID, data, row, manual)
|
||||
{
|
||||
yield Zotero.DB.waitForTransaction();
|
||||
|
||||
if ((row === undefined || row === null) && this.collectionsView.selection) {
|
||||
row = this.collectionsView.selection.currentIndex;
|
||||
|
||||
|
@ -810,8 +808,6 @@ var ZoteroPane = new function()
|
|||
|
||||
|
||||
this.newCollection = Zotero.Promise.coroutine(function* (parentKey) {
|
||||
yield Zotero.DB.waitForTransaction();
|
||||
|
||||
if (!this.canEditLibrary()) {
|
||||
this.displayCannotEditLibraryMessage();
|
||||
return;
|
||||
|
@ -847,7 +843,7 @@ var ZoteroPane = new function()
|
|||
collection.libraryID = libraryID;
|
||||
collection.name = newName.value;
|
||||
collection.parentKey = parentKey;
|
||||
return collection.save();
|
||||
return collection.saveTx();
|
||||
});
|
||||
|
||||
|
||||
|
@ -1234,6 +1230,15 @@ var ZoteroPane = new function()
|
|||
return Zotero.spawn(function* () {
|
||||
yield Zotero.DB.waitForTransaction();
|
||||
|
||||
// Don't select item until items list has loaded
|
||||
//
|
||||
// This avoids an error if New Item is used while the pane is first loading.
|
||||
var deferred = Zotero.Promise.defer();
|
||||
this.itemsView.addEventListener('load', function () {
|
||||
deferred.resolve();
|
||||
});
|
||||
yield deferred.promise;
|
||||
|
||||
var selectedItems = this.itemsView.getSelectedItems();
|
||||
|
||||
// Check if selection has actually changed. The onselect event that calls this
|
||||
|
@ -1863,7 +1868,7 @@ var ZoteroPane = new function()
|
|||
|
||||
if (result && newName.value) {
|
||||
row.ref.name = newName.value;
|
||||
row.ref.save();
|
||||
row.ref.saveTx();
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -3055,8 +3060,6 @@ var ZoteroPane = new function()
|
|||
* @return {Promise}
|
||||
*/
|
||||
this.newNote = Zotero.Promise.coroutine(function* (popup, parentKey, text, citeURI) {
|
||||
yield Zotero.DB.waitForTransaction();
|
||||
|
||||
if (!this.canEdit()) {
|
||||
this.displayCannotEditLibraryMessage();
|
||||
return;
|
||||
|
@ -3081,7 +3084,7 @@ var ZoteroPane = new function()
|
|||
if (parentKey) {
|
||||
item.parentKey = parentKey;
|
||||
}
|
||||
var itemID = yield item.save();
|
||||
var itemID = yield item.saveTx();
|
||||
|
||||
if (!parentKey && this.itemsView && this.collectionsView.selectedTreeRow.isCollection()) {
|
||||
yield this.collectionsView.selectedTreeRow.ref.addItem(itemID);
|
||||
|
@ -3146,7 +3149,7 @@ var ZoteroPane = new function()
|
|||
var note = items[0].getNote()
|
||||
|
||||
items[0].setNote(note + text);
|
||||
yield items[0].save();
|
||||
yield items[0].saveTx();
|
||||
|
||||
var noteElem = document.getElementById('zotero-note-editor')
|
||||
noteElem.focus();
|
||||
|
@ -3537,7 +3540,7 @@ var ZoteroPane = new function()
|
|||
item.setField('title', attachmentItem.getField('title'));
|
||||
item.setField('url', attachmentItem.getField('url'));
|
||||
item.setField('accessDate', attachmentItem.getField('accessDate'));
|
||||
yield item.save();
|
||||
yield item.saveTx();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4106,7 +4109,7 @@ var ZoteroPane = new function()
|
|||
}
|
||||
|
||||
item.setField('title', newName);
|
||||
yield item.save();
|
||||
yield item.saveTx();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -24,7 +24,7 @@ describe("Zotero.Attachments", function() {
|
|||
|
||||
// Create parent item
|
||||
var item = new Zotero.Item('book');
|
||||
var parentItemID = yield item.save();
|
||||
var parentItemID = yield item.saveTx();
|
||||
|
||||
// Create attachment and compare content
|
||||
var itemID = yield Zotero.Attachments.importFromFile(tmpFile, parentItemID);
|
||||
|
@ -43,7 +43,7 @@ describe("Zotero.Attachments", function() {
|
|||
|
||||
// Create parent item
|
||||
var item = new Zotero.Item('book');
|
||||
var parentItemID = yield item.save();
|
||||
var parentItemID = yield item.saveTx();
|
||||
|
||||
// Create attachment and compare content
|
||||
var itemID = yield Zotero.Attachments.importFromFile(file, parentItemID);
|
||||
|
|
|
@ -6,7 +6,7 @@ describe("Zotero.Collection", function() {
|
|||
var name = "Test";
|
||||
var collection = new Zotero.Collection;
|
||||
collection.name = name;
|
||||
var id = yield collection.save();
|
||||
var id = yield collection.saveTx();
|
||||
collection = yield Zotero.Collections.getAsync(id);
|
||||
assert.equal(collection.name, name);
|
||||
});
|
||||
|
@ -18,7 +18,7 @@ describe("Zotero.Collection", function() {
|
|||
var collection = new Zotero.Collection
|
||||
collection.version = version;
|
||||
collection.name = "Test";
|
||||
var id = yield collection.save();
|
||||
var id = yield collection.saveTx();
|
||||
collection = yield Zotero.Collections.getAsync(id);
|
||||
assert.equal(collection.version, version);
|
||||
});
|
||||
|
@ -28,13 +28,13 @@ describe("Zotero.Collection", function() {
|
|||
it("should set parent collection for new collections", function* () {
|
||||
var parentCol = new Zotero.Collection
|
||||
parentCol.name = "Parent";
|
||||
var parentID = yield parentCol.save();
|
||||
var parentID = yield parentCol.saveTx();
|
||||
var {libraryID, key: parentKey} = Zotero.Collections.getLibraryAndKeyFromID(parentID);
|
||||
|
||||
var col = new Zotero.Collection
|
||||
col.name = "Child";
|
||||
col.parentKey = parentKey;
|
||||
var id = yield col.save();
|
||||
var id = yield col.saveTx();
|
||||
col = yield Zotero.Collections.getAsync(id);
|
||||
assert.equal(col.parentKey, parentKey);
|
||||
});
|
||||
|
@ -43,25 +43,25 @@ describe("Zotero.Collection", function() {
|
|||
// Create initial parent collection
|
||||
var parentCol = new Zotero.Collection
|
||||
parentCol.name = "Parent";
|
||||
var parentID = yield parentCol.save();
|
||||
var parentID = yield parentCol.saveTx();
|
||||
var {libraryID, key: parentKey} = Zotero.Collections.getLibraryAndKeyFromID(parentID);
|
||||
|
||||
// Create subcollection
|
||||
var col = new Zotero.Collection
|
||||
col.name = "Child";
|
||||
col.parentKey = parentKey;
|
||||
var id = yield col.save();
|
||||
var id = yield col.saveTx();
|
||||
col = yield Zotero.Collections.getAsync(id);
|
||||
|
||||
// Create new parent collection
|
||||
var newParentCol = new Zotero.Collection
|
||||
newParentCol.name = "New Parent";
|
||||
var newParentID = yield newParentCol.save();
|
||||
var newParentID = yield newParentCol.saveTx();
|
||||
var {libraryID, key: newParentKey} = Zotero.Collections.getLibraryAndKeyFromID(newParentID);
|
||||
|
||||
// Change parent collection
|
||||
col.parentKey = newParentKey;
|
||||
yield col.save();
|
||||
yield col.saveTx();
|
||||
col = yield Zotero.Collections.getAsync(id);
|
||||
assert.equal(col.parentKey, newParentKey);
|
||||
});
|
||||
|
@ -70,14 +70,14 @@ describe("Zotero.Collection", function() {
|
|||
// Create initial parent collection
|
||||
var parentCol = new Zotero.Collection
|
||||
parentCol.name = "Parent";
|
||||
var parentID = yield parentCol.save();
|
||||
var parentID = yield parentCol.saveTx();
|
||||
var {libraryID, key: parentKey} = Zotero.Collections.getLibraryAndKeyFromID(parentID);
|
||||
|
||||
// Create subcollection
|
||||
var col = new Zotero.Collection
|
||||
col.name = "Child";
|
||||
col.parentKey = parentKey;
|
||||
var id = yield col.save();
|
||||
var id = yield col.saveTx();
|
||||
col = yield Zotero.Collections.getAsync(id);
|
||||
|
||||
// Set to existing parent
|
||||
|
@ -88,11 +88,11 @@ describe("Zotero.Collection", function() {
|
|||
it("should not resave a collection with no parent if set to false", function* () {
|
||||
var col = new Zotero.Collection
|
||||
col.name = "Test";
|
||||
var id = yield col.save();
|
||||
var id = yield col.saveTx();
|
||||
col = yield Zotero.Collections.getAsync(id);
|
||||
|
||||
col.parentKey = false;
|
||||
var ret = yield col.save();
|
||||
var ret = yield col.saveTx();
|
||||
assert.isFalse(ret);
|
||||
});
|
||||
})
|
||||
|
|
|
@ -26,7 +26,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
// Create collection
|
||||
var collection = new Zotero.Collection;
|
||||
collection.name = "Select new collection";
|
||||
var id = yield collection.save();
|
||||
var id = yield collection.saveTx();
|
||||
|
||||
// New collection should be selected
|
||||
var selected = collectionsView.getSelectedCollection(true);
|
||||
|
@ -39,7 +39,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
// Create collection with skipNotifier flag
|
||||
var collection = new Zotero.Collection;
|
||||
collection.name = "No select on skipNotifier";
|
||||
var id = yield collection.save({
|
||||
var id = yield collection.saveTx({
|
||||
skipNotifier: true
|
||||
});
|
||||
|
||||
|
@ -53,7 +53,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
// Create collection with skipSelect flag
|
||||
var collection = new Zotero.Collection;
|
||||
collection.name = "No select on skipSelect";
|
||||
var id = yield collection.save({
|
||||
var id = yield collection.saveTx({
|
||||
skipSelect: true
|
||||
});
|
||||
|
||||
|
@ -65,13 +65,13 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
// Create collection
|
||||
var collection = new Zotero.Collection;
|
||||
collection.name = "No select on modify";
|
||||
var id = yield collection.save();
|
||||
var id = yield collection.saveTx();
|
||||
collection = yield Zotero.Collections.getAsync(id);
|
||||
|
||||
resetSelection();
|
||||
|
||||
collection.name = "No select on modify 2";
|
||||
yield collection.save();
|
||||
yield collection.saveTx();
|
||||
|
||||
// Modified collection should not be selected
|
||||
assert.equal(collectionsView.getSelectedLibraryID(), Zotero.Libraries.userLibraryID);
|
||||
|
@ -81,14 +81,14 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
// Create collection
|
||||
var collection = new Zotero.Collection;
|
||||
collection.name = "Reselect on modify";
|
||||
var id = yield collection.save();
|
||||
var id = yield collection.saveTx();
|
||||
collection = yield Zotero.Collections.getAsync(id);
|
||||
|
||||
var selected = collectionsView.getSelectedCollection(true);
|
||||
assert.equal(selected, id);
|
||||
|
||||
collection.name = "Reselect on modify 2";
|
||||
yield collection.save();
|
||||
yield collection.saveTx();
|
||||
|
||||
// Modified collection should still be selected
|
||||
selected = collectionsView.getSelectedCollection(true);
|
||||
|
@ -98,13 +98,13 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
it("should add a saved search after collections", function* () {
|
||||
var collection = new Zotero.Collection;
|
||||
collection.name = "Test";
|
||||
var collectionID = yield collection.save();
|
||||
var collectionID = yield collection.saveTx();
|
||||
var cv = win.ZoteroPane.collectionsView;
|
||||
|
||||
var search = new Zotero.Search;
|
||||
search.name = "A Test Search";
|
||||
search.addCondition('title', 'contains', 'test');
|
||||
var searchID = yield search.save();
|
||||
var searchID = yield search.saveTx();
|
||||
|
||||
var collectionRow = cv._rowMap["C" + collectionID];
|
||||
var searchRow = cv._rowMap["S" + searchID];
|
||||
|
|
|
@ -6,7 +6,7 @@ describe("Zotero.DataObject", function() {
|
|||
describe("#loadAllData()", function () {
|
||||
it("should load data on a regular item", function* () {
|
||||
var item = new Zotero.Item('book');
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
yield item.loadAllData();
|
||||
assert.throws(item.getNote.bind(item), 'getNote() can only be called on notes and attachments');
|
||||
|
@ -14,7 +14,7 @@ describe("Zotero.DataObject", function() {
|
|||
|
||||
it("should load data on an attachment item", function* () {
|
||||
var item = new Zotero.Item('attachment');
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
yield item.loadAllData();
|
||||
assert.equal(item.getNote(), '');
|
||||
|
@ -22,7 +22,7 @@ describe("Zotero.DataObject", function() {
|
|||
|
||||
it("should load data on a note item", function* () {
|
||||
var item = new Zotero.Item('note');
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
yield item.loadAllData();
|
||||
assert.equal(item.getNote(), '');
|
||||
|
@ -35,7 +35,7 @@ describe("Zotero.DataObject", function() {
|
|||
var objectsClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType('collection');
|
||||
var obj = new Zotero.Collection;
|
||||
obj.name = "Test";
|
||||
var id = yield obj.save();
|
||||
var id = yield obj.saveTx();
|
||||
var { libraryID, key } = objectsClass.getLibraryAndKeyFromID(id);
|
||||
assert.typeOf(key, 'string');
|
||||
assert.equal(objectsClass.getIDFromLibraryAndKey(libraryID, key), id);
|
||||
|
@ -44,7 +44,7 @@ describe("Zotero.DataObject", function() {
|
|||
var objectsClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType('search');
|
||||
var obj = new Zotero.Search;
|
||||
obj.name = "Test";
|
||||
var id = yield obj.save();
|
||||
var id = yield obj.saveTx();
|
||||
var { libraryID, key } = objectsClass.getLibraryAndKeyFromID(id);
|
||||
assert.typeOf(key, 'string');
|
||||
assert.equal(objectsClass.getIDFromLibraryAndKey(libraryID, key), id);
|
||||
|
@ -52,7 +52,7 @@ describe("Zotero.DataObject", function() {
|
|||
// Item
|
||||
var objectsClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType('item');
|
||||
var obj = new Zotero.Item('book');
|
||||
var id = yield obj.save();
|
||||
var id = yield obj.saveTx();
|
||||
var { libraryID, key } = objectsClass.getLibraryAndKeyFromID(id);
|
||||
assert.typeOf(key, 'string');
|
||||
assert.equal(objectsClass.getIDFromLibraryAndKey(libraryID, key), id);
|
||||
|
|
|
@ -9,14 +9,15 @@ describe("Zotero.DB", function() {
|
|||
});
|
||||
beforeEach(function* () {
|
||||
yield Zotero.DB.queryAsync("DROP TABLE IF EXISTS " + tmpTable);
|
||||
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
|
||||
});
|
||||
after(function* () {
|
||||
yield Zotero.DB.queryAsync("DROP TABLE IF EXISTS " + tmpTable);
|
||||
});
|
||||
|
||||
describe("#executeTransaction()", function () {
|
||||
it("should nest concurrent transactions", Zotero.Promise.coroutine(function* () {
|
||||
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
|
||||
it("should serialize concurrent transactions", Zotero.Promise.coroutine(function* () {
|
||||
this.timeout(1000);
|
||||
|
||||
var resolve1, resolve2, reject1, reject2;
|
||||
var promise1 = new Promise(function (resolve, reject) {
|
||||
|
@ -29,18 +30,21 @@ describe("Zotero.DB", function() {
|
|||
});
|
||||
|
||||
Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.Promise.delay(100);
|
||||
assert.equal(Zotero.DB._asyncTransactionNestingLevel, 0);
|
||||
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (2)");
|
||||
Zotero.DB.transactionDate; // Make sure we're still in a transaction
|
||||
yield Zotero.Promise.delay(250);
|
||||
var num = yield Zotero.DB.valueQueryAsync("SELECT COUNT(*) FROM " + tmpTable);
|
||||
assert.equal(num, 0);
|
||||
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)");
|
||||
assert.ok(Zotero.DB.inTransaction());
|
||||
})
|
||||
.then(resolve1)
|
||||
.catch(reject1);
|
||||
|
||||
Zotero.DB.executeTransaction(function* () {
|
||||
assert.equal(Zotero.DB._asyncTransactionNestingLevel, 1);
|
||||
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)");
|
||||
Zotero.DB.transactionDate; // Make sure we're still in a transaction
|
||||
var num = yield Zotero.DB.valueQueryAsync("SELECT COUNT(*) FROM " + tmpTable);
|
||||
assert.equal(num, 1);
|
||||
yield Zotero.Promise.delay(500);
|
||||
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (2)");
|
||||
assert.ok(Zotero.DB.inTransaction());
|
||||
})
|
||||
.then(resolve2)
|
||||
.catch(reject2);
|
||||
|
@ -48,10 +52,8 @@ describe("Zotero.DB", function() {
|
|||
yield Zotero.Promise.all([promise1, promise2]);
|
||||
}));
|
||||
|
||||
it("shouldn't nest transactions if an exclusive transaction is open", Zotero.Promise.coroutine(function* () {
|
||||
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
|
||||
|
||||
var resolve1, resolve2, reject1, reject2;
|
||||
it("should serialize queued transactions", function* () {
|
||||
var resolve1, resolve2, reject1, reject2, resolve3, reject3;
|
||||
var promise1 = new Promise(function (resolve, reject) {
|
||||
resolve1 = resolve;
|
||||
reject1 = reject;
|
||||
|
@ -60,66 +62,48 @@ describe("Zotero.DB", function() {
|
|||
resolve2 = resolve;
|
||||
reject2 = reject;
|
||||
});
|
||||
var promise3 = new Promise(function (resolve, reject) {
|
||||
resolve3 = resolve;
|
||||
reject3 = reject;
|
||||
});
|
||||
|
||||
// Start a transaction and have it delay
|
||||
Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.Promise.delay(100);
|
||||
assert.equal(Zotero.DB._asyncTransactionNestingLevel, 0);
|
||||
var num = yield Zotero.DB.valueQueryAsync("SELECT COUNT(*) FROM " + tmpTable);
|
||||
assert.equal(num, 0);
|
||||
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)");
|
||||
Zotero.DB.transactionDate; // Make sure we're still in a transaction
|
||||
}, { exclusive: true })
|
||||
assert.ok(Zotero.DB.inTransaction());
|
||||
})
|
||||
.then(resolve1)
|
||||
.catch(reject1);
|
||||
|
||||
// Start two more transactions, which should wait on the first
|
||||
Zotero.DB.executeTransaction(function* () {
|
||||
assert.equal(Zotero.DB._asyncTransactionNestingLevel, 0);
|
||||
var num = yield Zotero.DB.valueQueryAsync("SELECT COUNT(*) FROM " + tmpTable);
|
||||
assert.equal(num, 1);
|
||||
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (2)");
|
||||
Zotero.DB.transactionDate; // Make sure we're still in a transaction
|
||||
assert.ok(Zotero.DB.inTransaction());
|
||||
})
|
||||
.then(resolve2)
|
||||
.catch(reject2);
|
||||
|
||||
yield Zotero.Promise.all([promise1, promise2]);
|
||||
}));
|
||||
|
||||
it("shouldn't nest an exclusive transaction if another transaction is open", Zotero.Promise.coroutine(function* () {
|
||||
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
|
||||
|
||||
var resolve1, resolve2, reject1, reject2;
|
||||
var promise1 = new Promise(function (resolve, reject) {
|
||||
resolve1 = resolve;
|
||||
reject1 = reject;
|
||||
});
|
||||
var promise2 = new Promise(function (resolve, reject) {
|
||||
resolve2 = resolve;
|
||||
reject2 = reject;
|
||||
});
|
||||
|
||||
Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.Promise.delay(100);
|
||||
assert.equal(Zotero.DB._asyncTransactionNestingLevel, 0);
|
||||
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)");
|
||||
Zotero.DB.transactionDate; // Make sure we're still in a transaction
|
||||
})
|
||||
.then(resolve1)
|
||||
.catch(reject1);
|
||||
|
||||
Zotero.DB.executeTransaction(function* () {
|
||||
assert.equal(Zotero.DB._asyncTransactionNestingLevel, 0);
|
||||
var num = yield Zotero.DB.valueQueryAsync("SELECT COUNT(*) FROM " + tmpTable);
|
||||
assert.equal(num, 1);
|
||||
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (2)");
|
||||
Zotero.DB.transactionDate; // Make sure we're still in a transaction
|
||||
}, { exclusive: true })
|
||||
.then(resolve2)
|
||||
.catch(reject2);
|
||||
assert.equal(num, 2);
|
||||
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (3)");
|
||||
// But make sure the second queued transaction doesn't start at the same time,
|
||||
// such that the first queued transaction gets closed while the second is still
|
||||
// running
|
||||
assert.ok(Zotero.DB.inTransaction());
|
||||
})
|
||||
.then(resolve3)
|
||||
.catch(reject3);
|
||||
|
||||
yield Zotero.Promise.all([promise1, promise2]);
|
||||
}));
|
||||
yield Zotero.Promise.all([promise1, promise2, promise3]);
|
||||
})
|
||||
|
||||
it("should roll back on error", function* () {
|
||||
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
|
||||
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)");
|
||||
try {
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
|
@ -141,7 +125,6 @@ describe("Zotero.DB", function() {
|
|||
|
||||
it("should run onRollback callbacks", function* () {
|
||||
var callbackRan = false;
|
||||
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
|
||||
try {
|
||||
yield Zotero.DB.executeTransaction(
|
||||
function* () {
|
||||
|
@ -163,25 +146,31 @@ describe("Zotero.DB", function() {
|
|||
yield Zotero.DB.queryAsync("DROP TABLE " + tmpTable);
|
||||
});
|
||||
|
||||
it("should run onRollback callbacks for nested transactions", function* () {
|
||||
it("should time out on nested transactions", function* () {
|
||||
var e;
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
e = yield getPromiseError(
|
||||
Zotero.DB.executeTransaction(function* () {}).timeout(250)
|
||||
);
|
||||
});
|
||||
assert.ok(e);
|
||||
assert.equal(e.name, "TimeoutError");
|
||||
});
|
||||
|
||||
it("should run onRollback callbacks for timed-out nested transactions", function* () {
|
||||
var callback1Ran = false;
|
||||
var callback2Ran = false;
|
||||
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
|
||||
try {
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)");
|
||||
yield Zotero.DB.executeTransaction(
|
||||
function* () {
|
||||
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (2)");
|
||||
|
||||
throw 'Aborting transaction -- ignore';
|
||||
},
|
||||
function* () {},
|
||||
{
|
||||
waitTimeout: 100,
|
||||
onRollback: function () {
|
||||
callback1Ran = true;
|
||||
}
|
||||
}
|
||||
);
|
||||
)
|
||||
},
|
||||
{
|
||||
onRollback: function () {
|
||||
|
@ -190,32 +179,10 @@ describe("Zotero.DB", function() {
|
|||
});
|
||||
}
|
||||
catch (e) {
|
||||
if (typeof e != 'string' || !e.startsWith('Aborting transaction')) throw e;
|
||||
if (e.name != "TimeoutError") throw e;
|
||||
}
|
||||
assert.ok(callback1Ran);
|
||||
assert.ok(callback2Ran);
|
||||
|
||||
yield Zotero.DB.queryAsync("DROP TABLE " + tmpTable);
|
||||
});
|
||||
|
||||
it("should not commit nested transactions", function* () {
|
||||
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
|
||||
try {
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)");
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (2)");
|
||||
throw 'Aborting transaction -- ignore';
|
||||
});
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
if (typeof e != 'string' || !e.startsWith('Aborting transaction')) throw e;
|
||||
}
|
||||
var count = yield Zotero.DB.valueQueryAsync("SELECT COUNT(*) FROM " + tmpTable);
|
||||
assert.equal(count, 0);
|
||||
|
||||
yield Zotero.DB.queryAsync("DROP TABLE " + tmpTable);
|
||||
});
|
||||
})
|
||||
});
|
||||
|
|
|
@ -36,7 +36,7 @@ describe("Zotero.Item", function () {
|
|||
var fieldID = Zotero.ItemFields.getID(field);
|
||||
var item = new Zotero.Item('book');
|
||||
item.setField(field, 'Foo');
|
||||
id = yield item.save();
|
||||
id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
|
||||
item.setField(field, "");
|
||||
|
@ -57,7 +57,7 @@ describe("Zotero.Item", function () {
|
|||
assert.ok(item._changed.itemData[fieldID]);
|
||||
assert.ok(item.hasChanged());
|
||||
|
||||
yield item.save();
|
||||
yield item.saveTx();
|
||||
assert.isFalse(item.getField(fieldID));
|
||||
})
|
||||
|
||||
|
@ -70,7 +70,7 @@ describe("Zotero.Item", function () {
|
|||
it("should save version as object version", function* () {
|
||||
var item = new Zotero.Item('book');
|
||||
item.setField("version", 1);
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
assert.equal(item.getField("version"), 1);
|
||||
});
|
||||
|
@ -78,7 +78,7 @@ describe("Zotero.Item", function () {
|
|||
it("should save versionNumber for computerProgram", function () {
|
||||
var item = new Zotero.Item('computerProgram');
|
||||
item.setField("versionNumber", "1.0");
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
assert.equal(item.getField("versionNumber"), "1.0");
|
||||
});
|
||||
|
@ -89,7 +89,7 @@ describe("Zotero.Item", function () {
|
|||
var dateModified = "2015-05-05 17:18:12";
|
||||
var item = new Zotero.Item('book');
|
||||
item.dateModified = dateModified;
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
assert.equal(item.dateModified, dateModified);
|
||||
})
|
||||
|
@ -98,7 +98,7 @@ describe("Zotero.Item", function () {
|
|||
var dateModified = "2015-05-05 17:18:12";
|
||||
var item = new Zotero.Item('book');
|
||||
item.dateModified = dateModified;
|
||||
var id = yield item.save({
|
||||
var id = yield item.saveTx({
|
||||
skipDateModifiedUpdate: true
|
||||
});
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
|
@ -109,13 +109,13 @@ describe("Zotero.Item", function () {
|
|||
var dateModified = "2015-05-05 17:18:12";
|
||||
var item = new Zotero.Item('book');
|
||||
item.dateModified = dateModified;
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
|
||||
// Save again without changing Date Modified
|
||||
yield item.loadItemData();
|
||||
item.setField('title', 'Test');
|
||||
yield item.save()
|
||||
yield item.saveTx()
|
||||
|
||||
assert.closeTo(Zotero.Date.sqlToDate(item.dateModified, true).getTime(), Date.now(), 1000);
|
||||
})
|
||||
|
@ -124,20 +124,20 @@ describe("Zotero.Item", function () {
|
|||
var dateModified = "2015-05-05 17:18:12";
|
||||
var item = new Zotero.Item('book');
|
||||
item.dateModified = dateModified;
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
|
||||
// Set Date Modified to existing value
|
||||
yield item.loadItemData();
|
||||
item.setField('title', 'Test');
|
||||
item.dateModified = dateModified;
|
||||
yield item.save()
|
||||
yield item.saveTx()
|
||||
assert.closeTo(Zotero.Date.sqlToDate(item.dateModified, true).getTime(), Date.now(), 1000);
|
||||
})
|
||||
|
||||
it("should use current time if Date Modified is not given when skipDateModifiedUpdate is set for a new item", function* () {
|
||||
var item = new Zotero.Item('book');
|
||||
var id = yield item.save({
|
||||
var id = yield item.saveTx({
|
||||
skipDateModifiedUpdate: true
|
||||
});
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
|
@ -148,13 +148,13 @@ describe("Zotero.Item", function () {
|
|||
var dateModified = "2015-05-05 17:18:12";
|
||||
var item = new Zotero.Item('book');
|
||||
item.dateModified = dateModified;
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
|
||||
// Resave with skipDateModifiedUpdate
|
||||
yield item.loadItemData();
|
||||
item.setField('title', 'Test');
|
||||
yield item.save({
|
||||
yield item.saveTx({
|
||||
skipDateModifiedUpdate: true
|
||||
})
|
||||
assert.equal(item.dateModified, dateModified);
|
||||
|
@ -164,11 +164,11 @@ describe("Zotero.Item", function () {
|
|||
describe("#parentID", function () {
|
||||
it("should create a child note", function* () {
|
||||
var item = new Zotero.Item('book');
|
||||
var parentItemID = yield item.save();
|
||||
var parentItemID = yield item.saveTx();
|
||||
|
||||
item = new Zotero.Item('note');
|
||||
item.parentID = parentItemID;
|
||||
var childItemID = yield item.save();
|
||||
var childItemID = yield item.saveTx();
|
||||
|
||||
item = yield Zotero.Items.getAsync(childItemID);
|
||||
assert.ok(item.parentID);
|
||||
|
@ -198,7 +198,7 @@ describe("Zotero.Item", function () {
|
|||
var item = new Zotero.Item('attachment');
|
||||
item.attachmentLinkMode = 'linked_url';
|
||||
item.url = "https://www.zotero.org/";
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
|
||||
item.parentKey = false;
|
||||
|
@ -207,15 +207,15 @@ describe("Zotero.Item", function () {
|
|||
|
||||
it("should move a top-level note under another item", function* () {
|
||||
var noteItem = new Zotero.Item('note');
|
||||
var id = yield noteItem.save()
|
||||
var id = yield noteItem.saveTx()
|
||||
noteItem = yield Zotero.Items.getAsync(id);
|
||||
|
||||
var item = new Zotero.Item('book');
|
||||
id = yield item.save();
|
||||
id = yield item.saveTx();
|
||||
var { libraryID, key } = Zotero.Items.getLibraryAndKeyFromID(id);
|
||||
|
||||
noteItem.parentKey = key;
|
||||
yield noteItem.save();
|
||||
yield noteItem.saveTx();
|
||||
|
||||
assert.isFalse(noteItem.isTopLevelItem());
|
||||
})
|
||||
|
@ -224,19 +224,19 @@ describe("Zotero.Item", function () {
|
|||
// Create a collection
|
||||
var collection = new Zotero.Collection;
|
||||
collection.name = "Test";
|
||||
var collectionID = yield collection.save();
|
||||
var collectionID = yield collection.saveTx();
|
||||
|
||||
// Create a top-level note and add it to a collection
|
||||
var noteItem = new Zotero.Item('note');
|
||||
noteItem.addToCollection(collectionID);
|
||||
var id = yield noteItem.save()
|
||||
var id = yield noteItem.saveTx()
|
||||
noteItem = yield Zotero.Items.getAsync(id);
|
||||
|
||||
var item = new Zotero.Item('book');
|
||||
id = yield item.save();
|
||||
id = yield item.saveTx();
|
||||
var { libraryID, key } = Zotero.Items.getLibraryAndKeyFromID(id);
|
||||
noteItem.parentKey = key;
|
||||
yield noteItem.save();
|
||||
yield noteItem.saveTx();
|
||||
|
||||
assert.isFalse(noteItem.isTopLevelItem());
|
||||
})
|
||||
|
@ -258,7 +258,7 @@ describe("Zotero.Item", function () {
|
|||
|
||||
var item = new Zotero.Item("journalArticle");
|
||||
item.setCreators(creators);
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
yield item.loadCreators();
|
||||
assert.sameDeepMembers(item.getCreatorsJSON(), creators);
|
||||
|
@ -282,7 +282,7 @@ describe("Zotero.Item", function () {
|
|||
|
||||
var item = new Zotero.Item("journalArticle");
|
||||
item.setCreators(creators);
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
yield item.loadCreators();
|
||||
assert.sameDeepMembers(item.getCreators(), creators);
|
||||
|
@ -295,7 +295,7 @@ describe("Zotero.Item", function () {
|
|||
var item = new Zotero.Item("attachment");
|
||||
item.attachmentLinkMode = Zotero.Attachments.LINK_MODE_IMPORTED_FILE;
|
||||
item.attachmentCharset = charset;
|
||||
var itemID = yield item.save();
|
||||
var itemID = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(itemID);
|
||||
assert.equal(item.attachmentCharset, charset);
|
||||
})
|
||||
|
@ -305,7 +305,7 @@ describe("Zotero.Item", function () {
|
|||
var item = new Zotero.Item("attachment");
|
||||
item.attachmentLinkMode = Zotero.Attachments.LINK_MODE_IMPORTED_FILE;
|
||||
item.attachmentCharset = charset;
|
||||
var itemID = yield item.save();
|
||||
var itemID = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(itemID);
|
||||
|
||||
// Set charset to same value
|
||||
|
@ -320,20 +320,20 @@ describe("Zotero.Item", function () {
|
|||
|
||||
// Create parent item
|
||||
var item = new Zotero.Item("book");
|
||||
var parentItemID = yield item.save();
|
||||
var parentItemID = yield item.saveTx();
|
||||
|
||||
// Create attachment item
|
||||
var item = new Zotero.Item("attachment");
|
||||
item.attachmentLinkMode = Zotero.Attachments.LINK_MODE_IMPORTED_FILE;
|
||||
item.parentID = parentItemID;
|
||||
var itemID = yield item.save();
|
||||
var itemID = yield item.saveTx();
|
||||
|
||||
// Should be empty when unset
|
||||
assert.equal(item.attachmentFilename, '');
|
||||
|
||||
// Set filename
|
||||
item.attachmentFilename = filename;
|
||||
yield item.save();
|
||||
yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(itemID);
|
||||
|
||||
// Check filename
|
||||
|
@ -358,7 +358,7 @@ describe("Zotero.Item", function () {
|
|||
];
|
||||
var item = new Zotero.Item('journalArticle');
|
||||
item.setTags(tags);
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
yield item.loadTags();
|
||||
assert.sameDeepMembers(item.getTags(tags), tags);
|
||||
|
@ -375,7 +375,7 @@ describe("Zotero.Item", function () {
|
|||
];
|
||||
var item = new Zotero.Item('journalArticle');
|
||||
item.setTags(tags);
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
yield item.loadTags();
|
||||
item.setTags(tags);
|
||||
|
@ -393,11 +393,11 @@ describe("Zotero.Item", function () {
|
|||
];
|
||||
var item = new Zotero.Item('journalArticle');
|
||||
item.setTags(tags);
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
yield item.loadTags();
|
||||
item.setTags(tags.slice(0));
|
||||
yield item.save();
|
||||
yield item.saveTx();
|
||||
assert.sameDeepMembers(item.getTags(tags), tags.slice(0));
|
||||
})
|
||||
})
|
||||
|
|
|
@ -7,7 +7,7 @@ describe("Zotero.ItemTreeView", function() {
|
|||
itemsView = win.ZoteroPane.itemsView;
|
||||
|
||||
var item = new Zotero.Item('book');
|
||||
existingItemID = yield item.save();
|
||||
existingItemID = yield item.saveTx();
|
||||
});
|
||||
after(function () {
|
||||
win.close();
|
||||
|
@ -47,7 +47,7 @@ describe("Zotero.ItemTreeView", function() {
|
|||
|
||||
// Create item
|
||||
var item = new Zotero.Item('book');
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
|
||||
// New item should be selected
|
||||
var selected = itemsView.getSelectedItems();
|
||||
|
@ -71,7 +71,7 @@ describe("Zotero.ItemTreeView", function() {
|
|||
|
||||
// Create item with skipNotifier flag
|
||||
var item = new Zotero.Item('book');
|
||||
var id = yield item.save({
|
||||
var id = yield item.saveTx({
|
||||
skipNotifier: true
|
||||
});
|
||||
|
||||
|
@ -96,7 +96,7 @@ describe("Zotero.ItemTreeView", function() {
|
|||
|
||||
// Create item with skipSelect flag
|
||||
var item = new Zotero.Item('book');
|
||||
var id = yield item.save({
|
||||
var id = yield item.saveTx({
|
||||
skipSelect: true
|
||||
});
|
||||
|
||||
|
@ -114,7 +114,7 @@ describe("Zotero.ItemTreeView", function() {
|
|||
it("shouldn't select a modified item", function* () {
|
||||
// Create item
|
||||
var item = new Zotero.Item('book');
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
|
||||
itemsView.selection.clearSelection();
|
||||
|
@ -124,7 +124,7 @@ describe("Zotero.ItemTreeView", function() {
|
|||
|
||||
// Modify item
|
||||
item.setField('title', 'no select on modify');
|
||||
yield item.save();
|
||||
yield item.saveTx();
|
||||
|
||||
// itemSelected should have been called once (from 'selectEventsSuppressed = false'
|
||||
// in notify()) as a no-op
|
||||
|
@ -138,7 +138,7 @@ describe("Zotero.ItemTreeView", function() {
|
|||
it("should maintain selection on a selected modified item", function* () {
|
||||
// Create item
|
||||
var item = new Zotero.Item('book');
|
||||
var id = yield item.save();
|
||||
var id = yield item.saveTx();
|
||||
item = yield Zotero.Items.getAsync(id);
|
||||
|
||||
yield itemsView.selectItem(id);
|
||||
|
@ -151,7 +151,7 @@ describe("Zotero.ItemTreeView", function() {
|
|||
|
||||
// Modify item
|
||||
item.setField('title', 'maintain selection on modify');
|
||||
yield item.save();
|
||||
yield item.saveTx();
|
||||
|
||||
// itemSelected should have been called once (from 'selectEventsSuppressed = false'
|
||||
// in notify()) as a no-op
|
||||
|
|
|
@ -3,7 +3,7 @@ describe("Zotero.Search", function() {
|
|||
it("should fail without a name", function* () {
|
||||
var s = new Zotero.Search;
|
||||
s.addCondition('title', 'is', 'test');
|
||||
var e = yield getPromiseError(s.save());
|
||||
var e = yield getPromiseError(s.saveTx());
|
||||
assert.ok(e);
|
||||
assert.equal(e.constructor.name, Error.prototype.constructor.name); // TEMP: Error mismatch
|
||||
assert.equal(e.message, "Name not provided for saved search");
|
||||
|
@ -14,7 +14,7 @@ describe("Zotero.Search", function() {
|
|||
var s = new Zotero.Search;
|
||||
s.name = "Test";
|
||||
s.addCondition('title', 'is', 'test');
|
||||
var id = yield s.save();
|
||||
var id = yield s.saveTx();
|
||||
assert.typeOf(id, 'number');
|
||||
|
||||
// Check saved search
|
||||
|
@ -40,14 +40,14 @@ describe("Zotero.Search", function() {
|
|||
s.libraryID = Zotero.Libraries.userLibraryID;
|
||||
s.name = "Test";
|
||||
s.addCondition('title', 'is', 'test');
|
||||
var id = yield s.save();
|
||||
var id = yield s.saveTx();
|
||||
assert.typeOf(id, 'number');
|
||||
|
||||
// Add condition
|
||||
s = yield Zotero.Searches.getAsync(id);
|
||||
yield s.loadConditions();
|
||||
s.addCondition('title', 'contains', 'foo');
|
||||
var saved = yield s.save();
|
||||
var saved = yield s.saveTx();
|
||||
assert.isTrue(saved);
|
||||
|
||||
// Check saved search
|
||||
|
@ -64,14 +64,14 @@ describe("Zotero.Search", function() {
|
|||
s.name = "Test";
|
||||
s.addCondition('title', 'is', 'test');
|
||||
s.addCondition('title', 'contains', 'foo');
|
||||
var id = yield s.save();
|
||||
var id = yield s.saveTx();
|
||||
assert.typeOf(id, 'number');
|
||||
|
||||
// Remove condition
|
||||
s = yield Zotero.Searches.getAsync(id);
|
||||
yield s.loadConditions();
|
||||
s.removeCondition(0);
|
||||
var saved = yield s.save();
|
||||
var saved = yield s.saveTx();
|
||||
assert.isTrue(saved);
|
||||
|
||||
// Check saved search
|
||||
|
|
|
@ -39,7 +39,7 @@ describe("ZoteroPane", function() {
|
|||
it("should update the item count", function* () {
|
||||
var collection = new Zotero.Collection;
|
||||
collection.name = "Count Test";
|
||||
var id = yield collection.save();
|
||||
var id = yield collection.saveTx();
|
||||
yield waitForItemsLoad(win);
|
||||
|
||||
// Unselected, with no items in view
|
||||
|
@ -51,7 +51,7 @@ describe("ZoteroPane", function() {
|
|||
// Unselected, with one item in view
|
||||
var item = new Zotero.Item('newspaperArticle');
|
||||
item.setCollections([id]);
|
||||
var itemID1 = yield item.save({
|
||||
var itemID1 = yield item.saveTx({
|
||||
skipSelect: true
|
||||
});
|
||||
assert.equal(
|
||||
|
@ -62,7 +62,7 @@ describe("ZoteroPane", function() {
|
|||
// Unselected, with multiple items in view
|
||||
var item = new Zotero.Item('audioRecording');
|
||||
item.setCollections([id]);
|
||||
var itemID2 = yield item.save({
|
||||
var itemID2 = yield item.saveTx({
|
||||
skipSelect: true
|
||||
});
|
||||
assert.equal(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue