Fix a few small data layer bugs, and tidy up a little
I don't think these were triggered by any client code, but I found them while porting code to the server.
This commit is contained in:
parent
30a329d2e8
commit
70d9b9870c
6 changed files with 229 additions and 119 deletions
|
@ -99,24 +99,57 @@ Zotero.Collection.prototype.getName = function() {
|
|||
* Populate collection data from a database row
|
||||
*/
|
||||
Zotero.Collection.prototype.loadFromRow = function(row) {
|
||||
for each(let col in this.ObjectsClass.primaryFields) {
|
||||
if (row[col] === undefined) {
|
||||
Zotero.debug('Skipping missing collection field ' + col);
|
||||
var primaryFields = this._ObjectsClass.primaryFields;
|
||||
for (let i=0; i<primaryFields.length; i++) {
|
||||
let col = primaryFields[i];
|
||||
try {
|
||||
var val = row[col];
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.debug('Skipping missing ' + this._objectType + ' field ' + col);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (col) {
|
||||
case this._ObjectsClass.idColumn:
|
||||
col = 'id';
|
||||
break;
|
||||
|
||||
// Integer
|
||||
case 'libraryID':
|
||||
val = parseInt(val);
|
||||
break;
|
||||
|
||||
// Integer or 0
|
||||
case 'version':
|
||||
val = val ? parseInt(val) : 0;
|
||||
break;
|
||||
|
||||
// Value or false
|
||||
case 'parentKey':
|
||||
val = val || false;
|
||||
break;
|
||||
|
||||
// Integer or false if falsy
|
||||
case 'parentID':
|
||||
val = val ? parseInt(val) : false;
|
||||
break;
|
||||
|
||||
// Boolean
|
||||
case 'synced':
|
||||
case 'hasChildCollections':
|
||||
case 'hasChildItems':
|
||||
val = !!val;
|
||||
break;
|
||||
|
||||
default:
|
||||
val = val || '';
|
||||
}
|
||||
|
||||
this['_' + col] = val;
|
||||
}
|
||||
|
||||
this._id = row.collectionID;
|
||||
this._libraryID = parseInt(row.libraryID);
|
||||
this._key = row.key;
|
||||
this._name = row.name;
|
||||
this._parentID = row.parentID || false;
|
||||
this._parentKey = row.parentKey || false;
|
||||
this._version = parseInt(row.version);
|
||||
this._synced = !!row.synced;
|
||||
|
||||
this._hasChildCollections = !!row.hasChildCollections;
|
||||
this._childCollectionsLoaded = false;
|
||||
this._hasChildItems = !!row.hasChildItems;
|
||||
this._childItemsLoaded = false;
|
||||
|
||||
this._loaded.primaryData = true;
|
||||
|
@ -288,7 +321,7 @@ Zotero.Collection.prototype._saveData = Zotero.Promise.coroutine(function* (env)
|
|||
this.libraryID, this.parentKey
|
||||
);
|
||||
Zotero.DB.addCurrentCallback("commit", function () {
|
||||
this.ObjectsClass.registerChildCollection(parentCollectionID, this.id);
|
||||
this.ObjectsClass.registerChildCollection(parentCollectionID, collectionID);
|
||||
}.bind(this));
|
||||
}
|
||||
// Remove this from the previous parent's cached collection lists after commit,
|
||||
|
@ -298,12 +331,12 @@ Zotero.Collection.prototype._saveData = Zotero.Promise.coroutine(function* (env)
|
|||
this.libraryID, this._previousData.parentKey
|
||||
);
|
||||
Zotero.DB.addCurrentCallback("commit", function () {
|
||||
this.ObjectsClass.unregisterChildCollection(parentCollectionID, this.id);
|
||||
this.ObjectsClass.unregisterChildCollection(parentCollectionID, collectionID);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
if (!isNew) {
|
||||
Zotero.Notifier.queue('move', 'collection', this.id);
|
||||
Zotero.Notifier.queue('move', 'collection', collectionID);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -100,7 +100,9 @@ Zotero.DataObject.prototype._get = function (field) {
|
|||
if (this['_' + field] !== null) {
|
||||
return this['_' + field];
|
||||
}
|
||||
this._requireData('primaryData');
|
||||
if (field != 'libraryID' && field != 'key' && field != 'id') {
|
||||
this._requireData('primaryData');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -769,6 +771,19 @@ Zotero.DataObject.prototype._markFieldChange = function (field, oldValue) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
Zotero.DataObject.prototype.hasChanged = function() {
|
||||
var changed = Object.keys(this._changed).filter(dataType => this._changed[dataType]);
|
||||
if (changed.length == 1
|
||||
&& changed[0] == 'primaryData'
|
||||
&& this._changed.primaryData.synced
|
||||
&& this._previousData.synced == this._synced) {
|
||||
return false;
|
||||
}
|
||||
return !!changed.length;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clears log of changed values
|
||||
* @param {String} [dataType] data type/field to clear. Defaults to clearing everything
|
||||
|
@ -904,17 +919,6 @@ Zotero.DataObject.prototype.saveTx = function (options) {
|
|||
}
|
||||
|
||||
|
||||
Zotero.DataObject.prototype.hasChanged = function() {
|
||||
var changed = Object.keys(this._changed).filter(dataType => this._changed[dataType]);
|
||||
if (changed.length == 1
|
||||
&& changed[0] == 'primaryData'
|
||||
&& this._changed.primaryData.synced
|
||||
&& this._previousData.synced == this._synced) {
|
||||
return false;
|
||||
}
|
||||
return !!changed.length;
|
||||
}
|
||||
|
||||
Zotero.DataObject.prototype._initSave = Zotero.Promise.coroutine(function* (env) {
|
||||
// Default to user library if not specified
|
||||
if (this.libraryID === null) {
|
||||
|
|
|
@ -277,8 +277,8 @@ Zotero.DataObjects.prototype.getByLibraryAndKeyAsync = Zotero.Promise.coroutine(
|
|||
});
|
||||
|
||||
|
||||
Zotero.DataObjects.prototype.exists = function (itemID) {
|
||||
return !!this.getLibraryAndKeyFromID(itemID);
|
||||
Zotero.DataObjects.prototype.exists = function (id) {
|
||||
return !!this.getLibraryAndKeyFromID(id);
|
||||
}
|
||||
|
||||
|
||||
|
@ -383,7 +383,8 @@ Zotero.DataObjects.prototype.registerObject = function (obj) {
|
|||
var libraryID = obj.libraryID;
|
||||
var key = obj.key;
|
||||
|
||||
Zotero.debug("Registering " + this._ZDO_object + " " + id + " as " + libraryID + "/" + key);
|
||||
Zotero.debug("Registering " + this._ZDO_object + " " + id
|
||||
+ " as " + libraryID + "/" + key);
|
||||
if (!this._objectIDs[libraryID]) {
|
||||
this._objectIDs[libraryID] = {};
|
||||
}
|
||||
|
@ -568,8 +569,7 @@ Zotero.DataObjects.prototype._load = Zotero.Promise.coroutine(function* (library
|
|||
obj = new Zotero[this._ZDO_Object];
|
||||
obj.loadFromRow(rowObj, true);
|
||||
if (!options || !options.noCache) {
|
||||
this._objectCache[id] = obj;
|
||||
obj._inCache = true;
|
||||
this.registerObject(obj);
|
||||
}
|
||||
}
|
||||
loaded[id] = obj;
|
||||
|
|
|
@ -316,23 +316,26 @@ Zotero.Item.prototype._parseRowData = function(row) {
|
|||
for (let i=0; i<primaryFields.length; i++) {
|
||||
let col = primaryFields[i];
|
||||
|
||||
if (row[col] === undefined) {
|
||||
try {
|
||||
var val = row[col];
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.debug('Skipping missing field ' + col);
|
||||
continue;
|
||||
}
|
||||
|
||||
let val = row[col];
|
||||
|
||||
//Zotero.debug("Setting field '" + col + "' to '" + val + "' for item " + this.id);
|
||||
|
||||
switch (col) {
|
||||
// Skip
|
||||
case 'libraryID':
|
||||
case 'itemTypeID':
|
||||
break;
|
||||
|
||||
// Unchanged
|
||||
case 'itemID':
|
||||
col = 'id';
|
||||
break;
|
||||
|
||||
case 'libraryID':
|
||||
break;
|
||||
|
||||
// Integer or 0
|
||||
case 'version':
|
||||
|
@ -665,15 +668,15 @@ Zotero.Item.prototype.setField = function(field, value, loadIn) {
|
|||
|
||||
value = Zotero.Date.dateToSQL(date, true);
|
||||
break;
|
||||
|
||||
|
||||
case 'version':
|
||||
value = parseInt(value);
|
||||
break;
|
||||
|
||||
|
||||
case 'synced':
|
||||
value = !!value;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
throw new Error('Primary field ' + field + ' cannot be changed in Zotero.Item.setField()');
|
||||
|
||||
|
@ -686,27 +689,27 @@ Zotero.Item.prototype.setField = function(field, value, loadIn) {
|
|||
*/
|
||||
|
||||
// If field value has changed
|
||||
if (this['_' + field] != value || field == 'synced') {
|
||||
Zotero.debug("Field '" + field + "' has changed from '" + this['_' + field] + "' to '" + value + "'", 4);
|
||||
|
||||
// Save a copy of the field before modifying
|
||||
this._markFieldChange(field, this['_' + field]);
|
||||
|
||||
if (field == 'itemTypeID') {
|
||||
this.setType(value, loadIn);
|
||||
}
|
||||
else {
|
||||
|
||||
this['_' + field] = value;
|
||||
|
||||
if (!this._changed.primaryData) {
|
||||
this._changed.primaryData = {};
|
||||
}
|
||||
this._changed.primaryData[field] = true;
|
||||
}
|
||||
if (this['_' + field] === value && field != 'synced') {
|
||||
Zotero.debug("Field '" + field + "' has not changed", 4);
|
||||
return false;
|
||||
}
|
||||
|
||||
Zotero.debug("Field '" + field + "' has changed from '" + this['_' + field] + "' to '" + value + "'", 4);
|
||||
|
||||
// Save a copy of the field before modifying
|
||||
this._markFieldChange(field, this['_' + field]);
|
||||
|
||||
if (field == 'itemTypeID') {
|
||||
this.setType(value, loadIn);
|
||||
}
|
||||
else {
|
||||
Zotero.debug("Field '" + field + "' has not changed", 4);
|
||||
|
||||
this['_' + field] = value;
|
||||
|
||||
if (!this._changed.primaryData) {
|
||||
this._changed.primaryData = {};
|
||||
}
|
||||
this._changed.primaryData[field] = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -89,12 +89,43 @@ Zotero.defineProperty(Zotero.Search.prototype, 'conditions', {
|
|||
|
||||
|
||||
Zotero.Search.prototype.loadFromRow = function (row) {
|
||||
this._id = row.savedSearchID;
|
||||
this._libraryID = parseInt(row.libraryID);
|
||||
this._key = row.key;
|
||||
this._name = row.name;
|
||||
this._version = parseInt(row.version);
|
||||
this._synced = !!row.synced;
|
||||
var primaryFields = this._ObjectsClass.primaryFields;
|
||||
for (let i=0; i<primaryFields.length; i++) {
|
||||
let col = primaryFields[i];
|
||||
try {
|
||||
var val = row[col];
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.debug('Skipping missing ' + this._objectType + ' field ' + col);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (col) {
|
||||
case this._ObjectsClass.idColumn:
|
||||
col = 'id';
|
||||
break;
|
||||
|
||||
// Integer
|
||||
case 'libraryID':
|
||||
val = parseInt(val);
|
||||
break;
|
||||
|
||||
// Integer or 0
|
||||
case 'version':
|
||||
val = val ? parseInt(val) : 0;
|
||||
break;
|
||||
|
||||
// Boolean
|
||||
case 'synced':
|
||||
val = !!val;
|
||||
break;
|
||||
|
||||
default:
|
||||
val = val || '';
|
||||
}
|
||||
|
||||
this['_' + col] = val;
|
||||
}
|
||||
|
||||
this._loaded.primaryData = true;
|
||||
this._clearChanged('primaryData');
|
||||
|
@ -140,33 +171,35 @@ Zotero.Search.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
|
|||
yield Zotero.DB.queryAsync(sql, env.sqlValues);
|
||||
}
|
||||
|
||||
if (!isNew) {
|
||||
var sql = "DELETE FROM savedSearchConditions WHERE savedSearchID=?";
|
||||
yield Zotero.DB.queryAsync(sql, this.id);
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var sql = "INSERT INTO savedSearchConditions "
|
||||
+ "(savedSearchID, searchConditionID, condition, operator, value, required) "
|
||||
+ "VALUES (?,?,?,?,?,?)";
|
||||
for (let id in this._conditions) {
|
||||
let condition = this._conditions[id];
|
||||
if (this._changed.conditions) {
|
||||
if (!isNew) {
|
||||
var sql = "DELETE FROM savedSearchConditions WHERE savedSearchID=?";
|
||||
yield Zotero.DB.queryAsync(sql, this.id);
|
||||
}
|
||||
|
||||
// Convert condition and mode to "condition[/mode]"
|
||||
let conditionString = condition.mode ?
|
||||
condition.condition + '/' + condition.mode :
|
||||
condition.condition
|
||||
|
||||
var sqlParams = [
|
||||
searchID,
|
||||
i,
|
||||
conditionString,
|
||||
condition.operator ? condition.operator : null,
|
||||
condition.value ? condition.value : null,
|
||||
condition.required ? 1 : null
|
||||
];
|
||||
yield Zotero.DB.queryAsync(sql, sqlParams);
|
||||
i++;
|
||||
var i = 0;
|
||||
var sql = "INSERT INTO savedSearchConditions "
|
||||
+ "(savedSearchID, searchConditionID, condition, operator, value, required) "
|
||||
+ "VALUES (?,?,?,?,?,?)";
|
||||
for (let id in this._conditions) {
|
||||
let condition = this._conditions[id];
|
||||
|
||||
// Convert condition and mode to "condition[/mode]"
|
||||
let conditionString = condition.mode ?
|
||||
condition.condition + '/' + condition.mode :
|
||||
condition.condition
|
||||
|
||||
var sqlParams = [
|
||||
searchID,
|
||||
i,
|
||||
conditionString,
|
||||
condition.operator ? condition.operator : null,
|
||||
condition.value ? condition.value : null,
|
||||
condition.required ? 1 : null
|
||||
];
|
||||
yield Zotero.DB.queryAsync(sql, sqlParams);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -185,7 +218,6 @@ Zotero.Search.prototype._finalizeSave = Zotero.Promise.coroutine(function* (env)
|
|||
}
|
||||
|
||||
if (!env.skipCache) {
|
||||
yield this.loadPrimaryData(true);
|
||||
yield this.reload();
|
||||
// If new, there's no other data we don't have, so we can mark everything as loaded
|
||||
if (env.isNew) {
|
||||
|
@ -824,11 +856,9 @@ Zotero.Search.prototype.getSQLParams = Zotero.Promise.coroutine(function* () {
|
|||
|
||||
|
||||
Zotero.Search.prototype.loadConditions = Zotero.Promise.coroutine(function* (reload) {
|
||||
Zotero.debug("Loading conditions for search " + this.libraryKey);
|
||||
if (this._loaded.conditions && !reload) return;
|
||||
|
||||
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');
|
||||
|
@ -876,6 +906,7 @@ Zotero.Search.prototype.loadConditions = Zotero.Promise.coroutine(function* (rel
|
|||
}
|
||||
|
||||
this._loaded.conditions = true;
|
||||
this._clearChanged('conditions');
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -3,26 +3,18 @@
|
|||
describe("Zotero.DataObject", function() {
|
||||
var types = ['collection', 'item', 'search'];
|
||||
|
||||
describe("#loadAllData()", function () {
|
||||
it("should load data on a regular item", function* () {
|
||||
var item = new Zotero.Item('book');
|
||||
var id = yield item.saveTx();
|
||||
yield item.loadAllData();
|
||||
assert.throws(item.getNote.bind(item), 'getNote() can only be called on notes and attachments');
|
||||
})
|
||||
|
||||
it("should load data on an attachment item", function* () {
|
||||
var item = new Zotero.Item('attachment');
|
||||
var id = yield item.saveTx();
|
||||
yield item.loadAllData();
|
||||
assert.equal(item.getNote(), '');
|
||||
})
|
||||
|
||||
it("should load data on a note item", function* () {
|
||||
var item = new Zotero.Item('note');
|
||||
var id = yield item.saveTx();
|
||||
yield item.loadAllData();
|
||||
assert.equal(item.getNote(), '');
|
||||
describe("#key", function () {
|
||||
it("shouldn't update .loaded on get if unset", function* () {
|
||||
for (let type of types) {
|
||||
if (type == 'item') {
|
||||
var param = 'book';
|
||||
}
|
||||
let obj = new Zotero[Zotero.Utilities.capitalize(type)](param);
|
||||
obj.libraryID = Zotero.Libraries.userLibraryID;
|
||||
assert.isNull(obj.key);
|
||||
assert.isFalse(obj._loaded.primaryData);
|
||||
obj.key = Zotero.DataObjectUtilities.generateKey();
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -173,6 +165,53 @@ describe("Zotero.DataObject", function() {
|
|||
});
|
||||
})
|
||||
|
||||
describe("#loadPrimaryData()", function () {
|
||||
it("should load unloaded primary data if partially set", function* () {
|
||||
var objs = {};
|
||||
for (let type of types) {
|
||||
let obj = createUnsavedDataObject(type);
|
||||
yield obj.save({
|
||||
skipCache: true
|
||||
});
|
||||
objs[type] = {
|
||||
key: obj.key,
|
||||
version: obj.version
|
||||
};
|
||||
}
|
||||
|
||||
for (let type of types) {
|
||||
let obj = new Zotero[Zotero.Utilities.capitalize(type)];
|
||||
obj.libraryID = Zotero.Libraries.userLibraryID;
|
||||
obj.key = objs[type].key;
|
||||
yield obj.loadPrimaryData();
|
||||
assert.equal(obj.version, objs[type].version);
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("#loadAllData()", function () {
|
||||
it("should load data on a regular item", function* () {
|
||||
var item = new Zotero.Item('book');
|
||||
var id = yield item.saveTx();
|
||||
yield item.loadAllData();
|
||||
assert.throws(item.getNote.bind(item), 'getNote() can only be called on notes and attachments');
|
||||
})
|
||||
|
||||
it("should load data on an attachment item", function* () {
|
||||
var item = new Zotero.Item('attachment');
|
||||
var id = yield item.saveTx();
|
||||
yield item.loadAllData();
|
||||
assert.equal(item.getNote(), '');
|
||||
})
|
||||
|
||||
it("should load data on a note item", function* () {
|
||||
var item = new Zotero.Item('note');
|
||||
var id = yield item.saveTx();
|
||||
yield item.loadAllData();
|
||||
assert.equal(item.getNote(), '');
|
||||
})
|
||||
})
|
||||
|
||||
describe("#save()", function () {
|
||||
it("should add new identifiers to cache", function* () {
|
||||
// Collection
|
||||
|
|
Loading…
Add table
Reference in a new issue