Merge pull request #576 from aurimasv/async_db-av2
[Async DB] Modularize Zotero.DataObject.save()
This commit is contained in:
commit
66a04a39db
16 changed files with 2430 additions and 2341 deletions
|
@ -232,7 +232,7 @@
|
||||||
<body>
|
<body>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
this.updateSearch();
|
this.updateSearch();
|
||||||
return this.search.save(true);
|
return this.search.save({fixGaps: true});
|
||||||
]]>
|
]]>
|
||||||
</body>
|
</body>
|
||||||
</method>
|
</method>
|
||||||
|
|
|
@ -201,7 +201,7 @@ Zotero.API.Data = {
|
||||||
var params = this.parsePath(path);
|
var params = this.parsePath(path);
|
||||||
//Zotero.debug(params);
|
//Zotero.debug(params);
|
||||||
|
|
||||||
return Zotero.DataObjectUtilities.getClassForObjectType(params.objectType)
|
return Zotero.DataObjectUtilities.getObjectsClassForObjectType(params.objectType)
|
||||||
.apiDataGenerator(params);
|
.apiDataGenerator(params);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,38 +37,51 @@ Zotero.Collection = function() {
|
||||||
this._childItems = [];
|
this._childItems = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Collection._super = Zotero.DataObject;
|
Zotero.extendClass(Zotero.DataObject, Zotero.Collection);
|
||||||
Zotero.Collection.prototype = Object.create(Zotero.Collection._super.prototype);
|
|
||||||
Zotero.Collection.constructor = Zotero.Collection;
|
|
||||||
|
|
||||||
Zotero.Collection.prototype._objectType = 'collection';
|
Zotero.Collection.prototype._objectType = 'collection';
|
||||||
Zotero.Collection.prototype._dataTypes = Zotero.Collection._super.prototype._dataTypes.concat([
|
Zotero.Collection.prototype._dataTypes = Zotero.Collection._super.prototype._dataTypes.concat([
|
||||||
'primaryData',
|
|
||||||
'childCollections',
|
'childCollections',
|
||||||
'childItems'
|
'childItems'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Zotero.Collection.prototype.__defineGetter__('id', function () { return this._get('id'); });
|
Zotero.defineProperty(Zotero.Collection.prototype, 'ChildObjects', {
|
||||||
Zotero.Collection.prototype.__defineSetter__('id', function (val) { this._set('id', val); });
|
get: function() Zotero.Items
|
||||||
Zotero.Collection.prototype.__defineGetter__('libraryID', function () { return this._get('libraryID'); });
|
|
||||||
Zotero.Collection.prototype.__defineSetter__('libraryID', function (val) { return this._set('libraryID', val); });
|
|
||||||
Zotero.Collection.prototype.__defineGetter__('key', function () { return this._get('key'); });
|
|
||||||
Zotero.Collection.prototype.__defineSetter__('key', function (val) { this._set('key', val) });
|
|
||||||
Zotero.Collection.prototype.__defineGetter__('name', function () { return this._get('name'); });
|
|
||||||
Zotero.Collection.prototype.__defineSetter__('name', function (val) { this._set('name', val); });
|
|
||||||
// .parentKey and .parentID defined in dataObject.js
|
|
||||||
Zotero.Collection.prototype.__defineGetter__('version', function () { return this._get('version'); });
|
|
||||||
Zotero.Collection.prototype.__defineSetter__('version', function (val) { this._set('version', val); });
|
|
||||||
Zotero.Collection.prototype.__defineGetter__('synced', function () { return this._get('synced'); });
|
|
||||||
Zotero.Collection.prototype.__defineSetter__('synced', function (val) { this._set('synced', val); });
|
|
||||||
|
|
||||||
Zotero.Collection.prototype.__defineGetter__('parent', function (val) {
|
|
||||||
Zotero.debug("WARNING: Zotero.Collection.prototype.parent has been deprecated -- use .parentID or .parentKey", 2);
|
|
||||||
return this.parentID;
|
|
||||||
});
|
});
|
||||||
Zotero.Collection.prototype.__defineSetter__('parent', function (val) {
|
|
||||||
Zotero.debug("WARNING: Zotero.Collection.prototype.parent has been deprecated -- use .parentID or .parentKey", 2);
|
Zotero.defineProperty(Zotero.Collection.prototype, 'id', {
|
||||||
this.parentID = val;
|
get: function() this._get('id'),
|
||||||
|
set: function(val) this._set('id', val)
|
||||||
|
});
|
||||||
|
Zotero.defineProperty(Zotero.Collection.prototype, 'libraryID', {
|
||||||
|
get: function() this._get('libraryID'),
|
||||||
|
set: function(val) this._set('libraryID', val)
|
||||||
|
});
|
||||||
|
Zotero.defineProperty(Zotero.Collection.prototype, 'key', {
|
||||||
|
get: function() this._get('key'),
|
||||||
|
set: function(val) this._set('key', val)
|
||||||
|
});
|
||||||
|
Zotero.defineProperty(Zotero.Collection.prototype, 'name', {
|
||||||
|
get: function() this._get('name'),
|
||||||
|
set: function(val) this._set('name', val)
|
||||||
|
});
|
||||||
|
Zotero.defineProperty(Zotero.Collection.prototype, 'version', {
|
||||||
|
get: function() this._get('version'),
|
||||||
|
set: function(val) this._set('version', val)
|
||||||
|
});
|
||||||
|
Zotero.defineProperty(Zotero.Collection.prototype, 'synced', {
|
||||||
|
get: function() this._get('synced'),
|
||||||
|
set: function(val) this._set('synced', val)
|
||||||
|
});
|
||||||
|
Zotero.defineProperty(Zotero.Collection.prototype, 'parent', {
|
||||||
|
get: function() {
|
||||||
|
Zotero.debug("WARNING: Zotero.Collection.prototype.parent has been deprecated -- use .parentID or .parentKey", 2);
|
||||||
|
return this.parentID;
|
||||||
|
},
|
||||||
|
set: function(val) {
|
||||||
|
Zotero.debug("WARNING: Zotero.Collection.prototype.parent has been deprecated -- use .parentID or .parentKey", 2);
|
||||||
|
this.parentID = val;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Zotero.Collection.prototype._set = function (field, value) {
|
Zotero.Collection.prototype._set = function (field, value) {
|
||||||
|
@ -114,45 +127,13 @@ Zotero.Collection.prototype.getName = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Build collection from database
|
|
||||||
*/
|
|
||||||
Zotero.Collection.prototype.loadPrimaryData = Zotero.Promise.coroutine(function* (reload) {
|
|
||||||
if (this._loaded.primaryData && !reload) return;
|
|
||||||
|
|
||||||
var id = this._id;
|
|
||||||
var key = this._key;
|
|
||||||
var libraryID = this._libraryID;
|
|
||||||
|
|
||||||
var sql = Zotero.Collections.getPrimaryDataSQL();
|
|
||||||
if (id) {
|
|
||||||
sql += " AND O.collectionID=?";
|
|
||||||
var params = id;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sql += " AND O.libraryID=? AND O.key=?";
|
|
||||||
var params = [libraryID, key];
|
|
||||||
}
|
|
||||||
var data = yield Zotero.DB.rowQueryAsync(sql, params);
|
|
||||||
|
|
||||||
this._loaded.primaryData = true;
|
|
||||||
this._clearChanged('primaryData');
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadFromRow(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Populate collection data from a database row
|
* Populate collection data from a database row
|
||||||
*/
|
*/
|
||||||
Zotero.Collection.prototype.loadFromRow = function(row) {
|
Zotero.Collection.prototype.loadFromRow = function(row) {
|
||||||
Zotero.debug("Loading collection from row");
|
Zotero.debug("Loading collection from row");
|
||||||
|
|
||||||
for each(let col in Zotero.Collections.primaryFields) {
|
for each(let col in this.ObjectsClass.primaryFields) {
|
||||||
if (row[col] === undefined) {
|
if (row[col] === undefined) {
|
||||||
Zotero.debug('Skipping missing collection field ' + col);
|
Zotero.debug('Skipping missing collection field ' + col);
|
||||||
}
|
}
|
||||||
|
@ -267,168 +248,139 @@ Zotero.Collection.prototype.getChildItems = function (asIDs, includeDeleted) {
|
||||||
return objs;
|
return objs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Zotero.Collection.prototype._initSave = Zotero.Promise.coroutine(function* (env) {
|
||||||
|
if (!this.name) {
|
||||||
|
throw new Error('Collection name is empty');
|
||||||
|
}
|
||||||
|
|
||||||
|
var proceed = yield Zotero.Collection._super.prototype._initSave.apply(this, arguments);
|
||||||
|
if (!proceed) return false;
|
||||||
|
|
||||||
|
// Verify parent
|
||||||
|
if (this._parentKey) {
|
||||||
|
let newParent = this.ObjectsClass.getByLibraryAndKey(
|
||||||
|
this.libraryID, this._parentKey
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!newParent) {
|
||||||
|
throw new Error("Cannot set parent to invalid collection " + this._parentKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newParent.id == this.id) {
|
||||||
|
throw new Error('Cannot move collection into itself!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.id && (yield this.hasDescendent('collection', newParent.id))) {
|
||||||
|
throw ('Cannot move collection "' + this.name + '" into one of its own descendents');
|
||||||
|
}
|
||||||
|
|
||||||
|
env.parent = newParent.id;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
env.parent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
Zotero.Collection.prototype.save = Zotero.Promise.coroutine(function* () {
|
Zotero.Collection.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
|
||||||
try {
|
var isNew = env.isNew;
|
||||||
Zotero.Collections.editCheck(this);
|
|
||||||
|
var collectionID = env.id = this._id = this.id ? this.id : yield Zotero.ID.get('collections');
|
||||||
|
var libraryID = env.libraryID = this.libraryID;
|
||||||
|
var key = env.key = this._key = this.key ? this.key : this._generateKey();
|
||||||
|
|
||||||
|
Zotero.debug("Saving collection " + this.id);
|
||||||
|
|
||||||
|
var columns = [
|
||||||
|
'collectionID',
|
||||||
|
'collectionName',
|
||||||
|
'parentCollectionID',
|
||||||
|
'clientDateModified',
|
||||||
|
'libraryID',
|
||||||
|
'key',
|
||||||
|
'version',
|
||||||
|
'synced'
|
||||||
|
];
|
||||||
|
var sqlValues = [
|
||||||
|
collectionID ? { int: collectionID } : null,
|
||||||
|
{ string: this.name },
|
||||||
|
env.parent ? env.parent : null,
|
||||||
|
Zotero.DB.transactionDateTime,
|
||||||
|
this.libraryID ? this.libraryID : 0,
|
||||||
|
key,
|
||||||
|
this.version ? this.version : 0,
|
||||||
|
this.synced ? 1 : 0
|
||||||
|
];
|
||||||
|
if (isNew) {
|
||||||
|
var placeholders = columns.map(function () '?').join();
|
||||||
|
|
||||||
if (!this.name) {
|
var sql = "REPLACE INTO collections (" + columns.join(', ') + ") "
|
||||||
throw new Error('Collection name is empty');
|
+ "VALUES (" + placeholders + ")";
|
||||||
|
var insertID = yield Zotero.DB.queryAsync(sql, sqlValues);
|
||||||
|
if (!collectionID) {
|
||||||
|
collectionID = env.id = insertID;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Zotero.Utilities.isEmpty(this._changed)) {
|
|
||||||
Zotero.debug("Collection " + this.id + " has not changed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var isNew = !this.id;
|
|
||||||
|
|
||||||
// Register this item's identifiers in Zotero.DataObjects on transaction commit,
|
|
||||||
// before other callbacks run
|
|
||||||
var collectionID, libraryID, key;
|
|
||||||
if (isNew) {
|
|
||||||
var transactionOptions = {
|
|
||||||
onCommit: function () {
|
|
||||||
Zotero.Collections.registerIdentifiers(collectionID, libraryID, key);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var transactionOptions = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Zotero.DB.executeTransaction(function* () {
|
|
||||||
// how to know if date modified changed (in server code too?)
|
|
||||||
|
|
||||||
collectionID = this._id = this.id ? this.id : yield Zotero.ID.get('collections');
|
|
||||||
libraryID = this.libraryID;
|
|
||||||
key = this._key = this.key ? this.key : this._generateKey();
|
|
||||||
|
|
||||||
Zotero.debug("Saving collection " + this.id);
|
|
||||||
|
|
||||||
// Verify parent
|
|
||||||
if (this._parentKey) {
|
|
||||||
let newParent = Zotero.Collections.getByLibraryAndKey(
|
|
||||||
this.libraryID, this._parentKey
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!newParent) {
|
|
||||||
throw new Error("Cannot set parent to invalid collection " + this._parentKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newParent.id == this.id) {
|
|
||||||
throw new Error('Cannot move collection into itself!');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.id && (yield this.hasDescendent('collection', newParent.id))) {
|
|
||||||
throw ('Cannot move collection "' + this.name + '" into one of its own descendents');
|
|
||||||
}
|
|
||||||
|
|
||||||
var parent = newParent.id;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var parent = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var columns = [
|
|
||||||
'collectionID',
|
|
||||||
'collectionName',
|
|
||||||
'parentCollectionID',
|
|
||||||
'clientDateModified',
|
|
||||||
'libraryID',
|
|
||||||
'key',
|
|
||||||
'version',
|
|
||||||
'synced'
|
|
||||||
];
|
|
||||||
var sqlValues = [
|
|
||||||
collectionID ? { int: collectionID } : null,
|
|
||||||
{ string: this.name },
|
|
||||||
parent ? parent : null,
|
|
||||||
Zotero.DB.transactionDateTime,
|
|
||||||
this.libraryID ? this.libraryID : 0,
|
|
||||||
key,
|
|
||||||
this.version ? this.version : 0,
|
|
||||||
this.synced ? 1 : 0
|
|
||||||
];
|
|
||||||
if (isNew) {
|
|
||||||
var placeholders = columns.map(function () '?').join();
|
|
||||||
|
|
||||||
var sql = "REPLACE INTO collections (" + columns.join(', ') + ") "
|
|
||||||
+ "VALUES (" + placeholders + ")";
|
|
||||||
var insertID = yield Zotero.DB.queryAsync(sql, sqlValues);
|
|
||||||
if (!collectionID) {
|
|
||||||
collectionID = insertID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
columns.shift();
|
|
||||||
sqlValues.push(sqlValues.shift());
|
|
||||||
let sql = 'UPDATE collections SET '
|
|
||||||
+ columns.map(function (x) x + '=?').join(', ')
|
|
||||||
+ ' WHERE collectionID=?';
|
|
||||||
yield Zotero.DB.queryAsync(sql, sqlValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._changed.parentKey) {
|
|
||||||
var parentIDs = [];
|
|
||||||
if (this.id && this._previousData.parentKey) {
|
|
||||||
parentIDs.push(Zotero.Collections.getIDFromLibraryAndKey(
|
|
||||||
this.libraryID, this._previousData.parentKey
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if (this.parentKey) {
|
|
||||||
parentIDs.push(Zotero.Collections.getIDFromLibraryAndKey(
|
|
||||||
this.libraryID, this.parentKey
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if (this.id) {
|
|
||||||
Zotero.Notifier.trigger('move', 'collection', this.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNew && this.libraryID) {
|
|
||||||
var groupID = Zotero.Groups.getGroupIDFromLibraryID(this.libraryID);
|
|
||||||
var group = Zotero.Groups.get(groupID);
|
|
||||||
group.clearCollectionCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNew) {
|
|
||||||
Zotero.Notifier.trigger('add', 'collection', this.id);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Zotero.Notifier.trigger('modify', 'collection', this.id, this._previousData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invalidate cached child collections
|
|
||||||
if (parentIDs) {
|
|
||||||
Zotero.Collections.refreshChildCollections(parentIDs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// New collections have to be reloaded via Zotero.Collections.get(), so mark them as disabled
|
|
||||||
if (isNew) {
|
|
||||||
var id = this.id;
|
|
||||||
this._disabled = true;
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
yield this.reload();
|
|
||||||
this._clearChanged();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}.bind(this), transactionOptions);
|
|
||||||
}
|
}
|
||||||
catch (e) {
|
else {
|
||||||
try {
|
columns.shift();
|
||||||
yield this.reload();
|
sqlValues.push(sqlValues.shift());
|
||||||
this._clearChanged();
|
let sql = 'UPDATE collections SET '
|
||||||
}
|
+ columns.map(function (x) x + '=?').join(', ')
|
||||||
catch (e2) {
|
+ ' WHERE collectionID=?';
|
||||||
Zotero.debug(e2, 1);
|
yield Zotero.DB.queryAsync(sql, sqlValues);
|
||||||
}
|
|
||||||
|
|
||||||
Zotero.debug(e, 1);
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._changed.parentKey) {
|
||||||
|
var parentIDs = [];
|
||||||
|
if (this.id && this._previousData.parentKey) {
|
||||||
|
parentIDs.push(this.ObjectsClass.getIDFromLibraryAndKey(
|
||||||
|
this.libraryID, this._previousData.parentKey
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (this.parentKey) {
|
||||||
|
parentIDs.push(this.ObjectsClass.getIDFromLibraryAndKey(
|
||||||
|
this.libraryID, this.parentKey
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (this.id) {
|
||||||
|
Zotero.Notifier.trigger('move', 'collection', this.id);
|
||||||
|
}
|
||||||
|
env.parentIDs = parentIDs;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Zotero.Collection.prototype._finalizeSave = Zotero.Promise.coroutine(function* (env) {
|
||||||
|
var isNew = env.isNew;
|
||||||
|
if (isNew && Zotero.Libraries.isGroupLibrary(this.libraryID)) {
|
||||||
|
var groupID = Zotero.Groups.getGroupIDFromLibraryID(this.libraryID);
|
||||||
|
var group = Zotero.Groups.get(groupID);
|
||||||
|
group.clearCollectionCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNew) {
|
||||||
|
Zotero.Notifier.trigger('add', 'collection', this.id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Zotero.Notifier.trigger('modify', 'collection', this.id, this._previousData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalidate cached child collections
|
||||||
|
if (env.parentIDs) {
|
||||||
|
this.ObjectsClass.refreshChildCollections(env.parentIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// New collections have to be reloaded via Zotero.Collections.get(), so mark them as disabled
|
||||||
|
if (isNew) {
|
||||||
|
var id = this.id;
|
||||||
|
this._disabled = true;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield this.reload();
|
||||||
|
this._clearChanged();
|
||||||
|
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -466,7 +418,7 @@ Zotero.Collection.prototype.addItems = Zotero.Promise.coroutine(function* (itemI
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let item = yield Zotero.Items.getAsync(itemID);
|
let item = yield this.ChildObjects.getAsync(itemID);
|
||||||
yield item.loadCollections();
|
yield item.loadCollections();
|
||||||
item.addToCollection(this.id);
|
item.addToCollection(this.id);
|
||||||
yield item.save({
|
yield item.save({
|
||||||
|
@ -513,7 +465,7 @@ Zotero.Collection.prototype.removeItems = Zotero.Promise.coroutine(function* (it
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let item = yield Zotero.Items.getAsync(itemID);
|
let item = yield this.ChildObjects.getAsync(itemID);
|
||||||
yield item.loadCollections();
|
yield item.loadCollections();
|
||||||
item.removeFromCollection(this.id);
|
item.removeFromCollection(this.id);
|
||||||
yield item.save({
|
yield item.save({
|
||||||
|
@ -565,7 +517,7 @@ Zotero.Collection.prototype.diff = function (collection, includeMatches) {
|
||||||
var diff = [];
|
var diff = [];
|
||||||
var thisData = this.serialize();
|
var thisData = this.serialize();
|
||||||
var otherData = collection.serialize();
|
var otherData = collection.serialize();
|
||||||
var numDiffs = Zotero.Collections.diff(thisData, otherData, diff, includeMatches);
|
var numDiffs = this.ObjectsClass.diff(thisData, otherData, diff, includeMatches);
|
||||||
|
|
||||||
// For the moment, just compare children and increase numDiffs if any differences
|
// For the moment, just compare children and increase numDiffs if any differences
|
||||||
var d1 = Zotero.Utilities.arrayDiff(
|
var d1 = Zotero.Utilities.arrayDiff(
|
||||||
|
@ -625,7 +577,7 @@ Zotero.Collection.prototype.clone = function (includePrimary, newCollection) {
|
||||||
var sameLibrary = newCollection.libraryID == this.libraryID;
|
var sameLibrary = newCollection.libraryID == this.libraryID;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var newCollection = new Zotero.Collection;
|
var newCollection = new this.constructor;
|
||||||
var sameLibrary = true;
|
var sameLibrary = true;
|
||||||
|
|
||||||
if (includePrimary) {
|
if (includePrimary) {
|
||||||
|
@ -661,7 +613,7 @@ Zotero.Collection.prototype.erase = function(deleteItems) {
|
||||||
// Descendent collections
|
// Descendent collections
|
||||||
if (descendents[i].type == 'collection') {
|
if (descendents[i].type == 'collection') {
|
||||||
collections.push(descendents[i].id);
|
collections.push(descendents[i].id);
|
||||||
var c = yield Zotero.Collections.getAsync(descendents[i].id);
|
var c = yield this.ObjectsClass.getAsync(descendents[i].id);
|
||||||
if (c) {
|
if (c) {
|
||||||
notifierData[c.id] = { old: c.toJSON() };
|
notifierData[c.id] = { old: c.toJSON() };
|
||||||
}
|
}
|
||||||
|
@ -675,7 +627,7 @@ Zotero.Collection.prototype.erase = function(deleteItems) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (del.length) {
|
if (del.length) {
|
||||||
yield Zotero.Items.trash(del);
|
yield this.ChildObjects.trash(del);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove relations
|
// Remove relations
|
||||||
|
@ -698,9 +650,9 @@ Zotero.Collection.prototype.erase = function(deleteItems) {
|
||||||
|
|
||||||
// TODO: Update member items
|
// TODO: Update member items
|
||||||
}.bind(this))
|
}.bind(this))
|
||||||
.then(function () {
|
.then(() => {
|
||||||
// Clear deleted collection from internal memory
|
// Clear deleted collection from internal memory
|
||||||
Zotero.Collections.unload(collections);
|
this.ObjectsClass.unload(collections);
|
||||||
//return Zotero.Collections.reloadAll();
|
//return Zotero.Collections.reloadAll();
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
|
@ -815,7 +767,7 @@ Zotero.Collection.prototype.getChildren = Zotero.Promise.coroutine(function* (re
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recursive) {
|
if (recursive) {
|
||||||
let child = yield Zotero.Collections.getAsync(children[i].id);
|
let child = yield this.ObjectsClass.getAsync(children[i].id);
|
||||||
let descendents = yield child.getChildren(
|
let descendents = yield child.getChildren(
|
||||||
true, nested, type, includeDeletedItems, level+1
|
true, nested, type, includeDeletedItems, level+1
|
||||||
);
|
);
|
||||||
|
@ -871,7 +823,7 @@ Zotero.Collection.prototype.addLinkedCollection = Zotero.Promise.coroutine(funct
|
||||||
var predicate = Zotero.Relations.linkedObjectPredicate;
|
var predicate = Zotero.Relations.linkedObjectPredicate;
|
||||||
if ((yield Zotero.Relations.getByURIs(url1, predicate, url2)).length
|
if ((yield Zotero.Relations.getByURIs(url1, predicate, url2)).length
|
||||||
|| (yield Zotero.Relations.getByURIs(url2, predicate, url1)).length) {
|
|| (yield Zotero.Relations.getByURIs(url2, predicate, url1)).length) {
|
||||||
Zotero.debug("Collections " + this.key + " and " + collection.key + " are already linked");
|
Zotero.debug(this._ObjectTypePlural + " " + this.key + " and " + collection.key + " are already linked");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -901,9 +853,9 @@ Zotero.Collection.prototype.loadChildCollections = Zotero.Promise.coroutine(func
|
||||||
|
|
||||||
this._childCollections = [];
|
this._childCollections = [];
|
||||||
|
|
||||||
if (ids) {
|
if (ids.length) {
|
||||||
for each(var id in ids) {
|
for each(var id in ids) {
|
||||||
var col = yield Zotero.Collections.getAsync(id);
|
var col = yield this.ObjectsClass.getAsync(id);
|
||||||
if (!col) {
|
if (!col) {
|
||||||
throw new Error('Collection ' + id + ' not found');
|
throw new Error('Collection ' + id + ' not found');
|
||||||
}
|
}
|
||||||
|
@ -943,7 +895,7 @@ Zotero.Collection.prototype.loadChildItems = Zotero.Promise.coroutine(function*
|
||||||
this._childItems = [];
|
this._childItems = [];
|
||||||
|
|
||||||
if (ids) {
|
if (ids) {
|
||||||
var items = yield Zotero.Items.getAsync(ids)
|
var items = yield this.ChildObjects.getAsync(ids)
|
||||||
if (items) {
|
if (items) {
|
||||||
this._childItems = items;
|
this._childItems = items;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,10 @@
|
||||||
/*
|
/*
|
||||||
* Primary interface for accessing Zotero collection
|
* Primary interface for accessing Zotero collection
|
||||||
*/
|
*/
|
||||||
Zotero.Collections = new function() {
|
Zotero.Collections = function() {
|
||||||
Zotero.DataObjects.apply(this, ['collection']);
|
this.constructor = null;
|
||||||
this.constructor.prototype = new Zotero.DataObjects();
|
|
||||||
|
this._ZDO_object = 'collection';
|
||||||
|
|
||||||
this._primaryDataSQLParts = {
|
this._primaryDataSQLParts = {
|
||||||
collectionID: "O.collectionID",
|
collectionID: "O.collectionID",
|
||||||
|
@ -45,9 +46,13 @@ Zotero.Collections = new function() {
|
||||||
hasChildCollections: "(SELECT COUNT(*) FROM collections WHERE "
|
hasChildCollections: "(SELECT COUNT(*) FROM collections WHERE "
|
||||||
+ "parentCollectionID=O.collectionID) != 0 AS hasChildCollections",
|
+ "parentCollectionID=O.collectionID) != 0 AS hasChildCollections",
|
||||||
hasChildItems: "(SELECT COUNT(*) FROM collectionItems WHERE "
|
hasChildItems: "(SELECT COUNT(*) FROM collectionItems WHERE "
|
||||||
+ "collectionID=O.collectionID) != 0 AS hasChildItems "
|
+ "collectionID=O.collectionID) != 0 AS hasChildItems"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this._primaryDataSQLFrom = "FROM collections O "
|
||||||
|
+ "LEFT JOIN collections CP ON (O.parentCollectionID=CP.collectionID)";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add new collection to DB and return Collection object
|
* Add new collection to DB and return Collection object
|
||||||
*
|
*
|
||||||
|
@ -74,55 +79,51 @@ Zotero.Collections = new function() {
|
||||||
* Takes parent collectionID as optional parameter;
|
* Takes parent collectionID as optional parameter;
|
||||||
* by default, returns root collections
|
* by default, returns root collections
|
||||||
*/
|
*/
|
||||||
this.getByParent = Zotero.Promise.coroutine(function* (libraryID, parent, recursive) {
|
this.getByParent = Zotero.Promise.coroutine(function* (libraryID, parentID, recursive) {
|
||||||
var toReturn = [];
|
let children;
|
||||||
|
|
||||||
if (!parent) {
|
if (parentID) {
|
||||||
parent = null;
|
let parent = yield this.getAsync(parentID);
|
||||||
|
yield parent.loadChildCollections();
|
||||||
|
children = parent.getChildCollections();
|
||||||
|
if (!children.length) Zotero.debug('No child collections in collection ' + parentID, 5);
|
||||||
|
} else if (libraryID || libraryID === 0) {
|
||||||
|
children = this.getCollectionsInLibrary(libraryID);
|
||||||
|
if (!children.length) Zotero.debug('No child collections in library ' + libraryID, 5);
|
||||||
|
} else {
|
||||||
|
throw new Error("Either library ID or parent collection ID must be provided to getNumCollectionsByParent");
|
||||||
}
|
}
|
||||||
|
|
||||||
var sql = "SELECT collectionID AS id, collectionName AS name FROM collections C "
|
if (!children.length) {
|
||||||
+ "WHERE libraryID=? AND parentCollectionID " + (parent ? '= ' + parent : 'IS NULL');
|
return children;
|
||||||
var children = yield Zotero.DB.queryAsync(sql, [libraryID]);
|
|
||||||
|
|
||||||
if (!children) {
|
|
||||||
Zotero.debug('No child collections of collection ' + parent, 5);
|
|
||||||
return toReturn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do proper collation sort
|
// Do proper collation sort
|
||||||
var collation = Zotero.getLocaleCollation();
|
children.sort(function (a, b) Zotero.localeCompare(a.name, b.name));
|
||||||
children.sort(function (a, b) {
|
|
||||||
return collation.compareString(1, a.name, b.name);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
if (!recursive) return children;
|
||||||
|
|
||||||
|
let toReturn = [];
|
||||||
for (var i=0, len=children.length; i<len; i++) {
|
for (var i=0, len=children.length; i<len; i++) {
|
||||||
var obj = yield this.getAsync(children[i].id);
|
var obj = children[i];
|
||||||
if (!obj) {
|
|
||||||
throw ('Collection ' + children[i].id + ' not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
toReturn.push(obj);
|
toReturn.push(obj);
|
||||||
|
|
||||||
// If recursive, get descendents
|
var desc = obj.getDescendents(false, 'collection');
|
||||||
if (recursive) {
|
for (var j in desc) {
|
||||||
var desc = obj.getDescendents(false, 'collection');
|
var obj2 = yield this.getAsync(desc[j]['id']);
|
||||||
for (var j in desc) {
|
if (!obj2) {
|
||||||
var obj2 = yield this.getAsync(desc[j]['id']);
|
throw new Error('Collection ' + desc[j] + ' not found');
|
||||||
if (!obj2) {
|
|
||||||
throw new Error('Collection ' + desc[j] + ' not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: This is a quick hack so that we can indent subcollections
|
|
||||||
// in the search dialog -- ideally collections would have a
|
|
||||||
// getLevel() method, but there's no particularly quick way
|
|
||||||
// of calculating that without either storing it in the DB or
|
|
||||||
// changing the schema to Modified Preorder Tree Traversal,
|
|
||||||
// and I don't know if we'll actually need it anywhere else.
|
|
||||||
obj2.level = desc[j].level;
|
|
||||||
|
|
||||||
toReturn.push(obj2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: This is a quick hack so that we can indent subcollections
|
||||||
|
// in the search dialog -- ideally collections would have a
|
||||||
|
// getLevel() method, but there's no particularly quick way
|
||||||
|
// of calculating that without either storing it in the DB or
|
||||||
|
// changing the schema to Modified Preorder Tree Traversal,
|
||||||
|
// and I don't know if we'll actually need it anywhere else.
|
||||||
|
obj2.level = desc[j].level;
|
||||||
|
|
||||||
|
toReturn.push(obj2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,6 +131,17 @@ Zotero.Collections = new function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
this.getCollectionsInLibrary = Zotero.Promise.coroutine(function* (libraryID) {
|
||||||
|
let sql = "SELECT collectionID AS id FROM collections C "
|
||||||
|
+ "WHERE libraryID=? AND parentCollectionId IS NULL";
|
||||||
|
let ids = yield Zotero.DB.queryAsync(sql, [libraryID]);
|
||||||
|
let collections = yield this.getAsync(ids.map(function(row) row.id));
|
||||||
|
if (!collections.length) return collections;
|
||||||
|
|
||||||
|
return collections.sort(function (a, b) Zotero.localeCompare(a.name, b.name));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
this.getCollectionsContainingItems = function (itemIDs, asIDs) {
|
this.getCollectionsContainingItems = function (itemIDs, asIDs) {
|
||||||
// If an unreasonable number of items, don't try
|
// If an unreasonable number of items, don't try
|
||||||
if (itemIDs.length > 100) {
|
if (itemIDs.length > 100) {
|
||||||
|
@ -145,8 +157,8 @@ Zotero.Collections = new function() {
|
||||||
}
|
}
|
||||||
sql = sql.substring(0, sql.length - 5);
|
sql = sql.substring(0, sql.length - 5);
|
||||||
return Zotero.DB.columnQueryAsync(sql, sqlParams)
|
return Zotero.DB.columnQueryAsync(sql, sqlParams)
|
||||||
.then(function (collectionIDs) {
|
.then(collectionIDs => {
|
||||||
return asIDs ? collectionIDs : Zotero.Collections.get(collectionIDs);
|
return asIDs ? collectionIDs : this.get(collectionIDs);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -186,32 +198,23 @@ Zotero.Collections = new function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
this.erase = function (ids) {
|
this.erase = function(ids) {
|
||||||
ids = Zotero.flattenArguments(ids);
|
ids = Zotero.flattenArguments(ids);
|
||||||
|
|
||||||
Zotero.DB.beginTransaction();
|
return Zotero.DB.executeTransaction(function* () {
|
||||||
for each(var id in ids) {
|
for each(var id in ids) {
|
||||||
var collection = this.getAsync(id);
|
var collection = yield this.getAsync(id);
|
||||||
if (collection) {
|
if (collection) {
|
||||||
collection.erase();
|
yield collection.erase();
|
||||||
|
}
|
||||||
|
collection = undefined;
|
||||||
}
|
}
|
||||||
collection = undefined;
|
|
||||||
}
|
this.unload(ids);
|
||||||
|
});
|
||||||
this.unload(ids);
|
};
|
||||||
|
|
||||||
Zotero.DB.commitTransaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Zotero.DataObjects.call(this);
|
||||||
|
|
||||||
this.getPrimaryDataSQL = function () {
|
return this;
|
||||||
// This should be the same as the query in Zotero.Collection.load(),
|
}.bind(Object.create(Zotero.DataObjects.prototype))();
|
||||||
// just without a specific collectionID
|
|
||||||
return "SELECT "
|
|
||||||
+ Object.keys(this._primaryDataSQLParts).map(key => this._primaryDataSQLParts[key]).join(", ") + " "
|
|
||||||
+ "FROM collections O "
|
|
||||||
+ "LEFT JOIN collections CP ON (O.parentCollectionID=CP.collectionID) "
|
|
||||||
+ "WHERE 1";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@ Zotero.DataObject = function () {
|
||||||
let objectType = this._objectType;
|
let objectType = this._objectType;
|
||||||
this._ObjectType = objectType[0].toUpperCase() + objectType.substr(1);
|
this._ObjectType = objectType[0].toUpperCase() + objectType.substr(1);
|
||||||
this._objectTypePlural = Zotero.DataObjectUtilities.getObjectTypePlural(objectType);
|
this._objectTypePlural = Zotero.DataObjectUtilities.getObjectTypePlural(objectType);
|
||||||
|
this._ObjectTypePlural = this._objectTypePlural[0].toUpperCase() + this._objectTypePlural.substr(1);
|
||||||
|
this._ObjectsClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType(objectType);
|
||||||
|
|
||||||
this._id = null;
|
this._id = null;
|
||||||
this._libraryID = null;
|
this._libraryID = null;
|
||||||
|
@ -53,23 +55,36 @@ Zotero.DataObject = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
Zotero.DataObject.prototype._objectType = 'dataObject';
|
Zotero.DataObject.prototype._objectType = 'dataObject';
|
||||||
Zotero.DataObject.prototype._dataTypes = [];
|
Zotero.DataObject.prototype._dataTypes = ['primaryData'];
|
||||||
|
|
||||||
Zotero.Utilities.Internal.defineProperty(Zotero.DataObject.prototype, 'objectType', {
|
Zotero.defineProperty(Zotero.DataObject.prototype, 'objectType', {
|
||||||
get: function() this._objectType
|
get: function() this._objectType
|
||||||
});
|
});
|
||||||
Zotero.Utilities.Internal.defineProperty(Zotero.DataObject.prototype, 'libraryKey', {
|
Zotero.defineProperty(Zotero.DataObject.prototype, 'id', {
|
||||||
|
get: function() this._id
|
||||||
|
});
|
||||||
|
Zotero.defineProperty(Zotero.DataObject.prototype, 'libraryID', {
|
||||||
|
get: function() this._libraryID
|
||||||
|
});
|
||||||
|
Zotero.defineProperty(Zotero.DataObject.prototype, 'key', {
|
||||||
|
get: function() this._key
|
||||||
|
});
|
||||||
|
Zotero.defineProperty(Zotero.DataObject.prototype, 'libraryKey', {
|
||||||
get: function() this._libraryID + "/" + this._key
|
get: function() this._libraryID + "/" + this._key
|
||||||
});
|
});
|
||||||
Zotero.Utilities.Internal.defineProperty(Zotero.DataObject.prototype, 'parentKey', {
|
Zotero.defineProperty(Zotero.DataObject.prototype, 'parentKey', {
|
||||||
get: function() this._parentKey,
|
get: function() this._parentKey,
|
||||||
set: function(v) this._setParentKey(v)
|
set: function(v) this._setParentKey(v)
|
||||||
});
|
});
|
||||||
Zotero.Utilities.Internal.defineProperty(Zotero.DataObject.prototype, 'parentID', {
|
Zotero.defineProperty(Zotero.DataObject.prototype, 'parentID', {
|
||||||
get: function() this._getParentID(),
|
get: function() this._getParentID(),
|
||||||
set: function(v) this._setParentID(v)
|
set: function(v) this._setParentID(v)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Zotero.defineProperty(Zotero.DataObject.prototype, 'ObjectsClass', {
|
||||||
|
get: function() this._ObjectsClass
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
Zotero.DataObject.prototype._get = function (field) {
|
Zotero.DataObject.prototype._get = function (field) {
|
||||||
if (this['_' + field] !== null) {
|
if (this['_' + field] !== null) {
|
||||||
|
@ -135,7 +150,7 @@ Zotero.DataObject.prototype._getParentID = function () {
|
||||||
if (!this._parentKey) {
|
if (!this._parentKey) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return this._parentID = this._getClass().getIDFromLibraryAndKey(this._libraryID, this._parentKey);
|
return this._parentID = this.ObjectsClass.getIDFromLibraryAndKey(this._libraryID, this._parentKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -148,7 +163,7 @@ Zotero.DataObject.prototype._getParentID = function () {
|
||||||
Zotero.DataObject.prototype._setParentID = function (id) {
|
Zotero.DataObject.prototype._setParentID = function (id) {
|
||||||
return this._setParentKey(
|
return this._setParentKey(
|
||||||
id
|
id
|
||||||
? this._getClass().getLibraryAndKeyFromID(Zotero.DataObjectUtilities.checkDataID(id))[1]
|
? this.ObjectsClass.getLibraryAndKeyFromID(Zotero.DataObjectUtilities.checkDataID(id))[1]
|
||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -309,6 +324,60 @@ Zotero.DataObject.prototype._getLinkedObject = Zotero.Promise.coroutine(function
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build object from database
|
||||||
|
*/
|
||||||
|
Zotero.DataObject.prototype.loadPrimaryData = Zotero.Promise.coroutine(function* (reload, failOnMissing) {
|
||||||
|
if (this._loaded.primaryData && !reload) return;
|
||||||
|
|
||||||
|
var id = this.id;
|
||||||
|
var key = this.key;
|
||||||
|
var libraryID = this.libraryID;
|
||||||
|
|
||||||
|
if (!id && !key) {
|
||||||
|
throw new Error('ID or key not set in Zotero.' + this._ObjectType + '.loadPrimaryData()');
|
||||||
|
}
|
||||||
|
|
||||||
|
var columns = [], join = [], where = [];
|
||||||
|
var primaryFields = this.ObjectsClass.primaryFields;
|
||||||
|
var idField = this.ObjectsClass.idColumn;
|
||||||
|
for (let i=0; i<primaryFields.length; i++) {
|
||||||
|
let field = primaryFields[i];
|
||||||
|
// If field not already set
|
||||||
|
if (field == idField || this['_' + field] === null || reload) {
|
||||||
|
columns.push(this.ObjectsClass.getPrimaryDataSQLPart(field));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!columns.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should match Zotero.*.primaryDataSQL, but without
|
||||||
|
// necessarily including all columns
|
||||||
|
var sql = "SELECT " + columns.join(", ") + this.ObjectsClass.primaryDataSQLFrom;
|
||||||
|
if (id) {
|
||||||
|
sql += " AND O." + idField + "=? ";
|
||||||
|
var params = id;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sql += " AND O.key=? AND O.libraryID=? ";
|
||||||
|
var params = [key, libraryID];
|
||||||
|
}
|
||||||
|
sql += (where.length ? ' AND ' + where.join(' AND ') : '');
|
||||||
|
var row = yield Zotero.DB.rowQueryAsync(sql, params);
|
||||||
|
|
||||||
|
if (!row) {
|
||||||
|
if (failOnMissing) {
|
||||||
|
throw new Error(this._ObjectType + " " + (id ? id : libraryID + "/" + key)
|
||||||
|
+ " not found in Zotero." + this._ObjectType + ".loadPrimaryData()");
|
||||||
|
}
|
||||||
|
this._loaded.primaryData = true;
|
||||||
|
this._clearChanged('primaryData');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadFromRow(row, reload);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reloads loaded, changed data
|
* Reloads loaded, changed data
|
||||||
|
@ -368,13 +437,6 @@ Zotero.DataObject.prototype._requireData = function (dataType) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a global Zotero class object given a data object. (e.g. Zotero.Items)
|
|
||||||
* @return {obj} One of Zotero data classes
|
|
||||||
*/
|
|
||||||
Zotero.DataObject.prototype._getClass = function () {
|
|
||||||
return Zotero.DataObjectUtilities.getClassForObjectType(this._objectType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads data for a given data type
|
* Loads data for a given data type
|
||||||
|
@ -385,6 +447,14 @@ Zotero.DataObject.prototype._loadDataType = function (dataType, reload) {
|
||||||
return this["load" + dataType[0].toUpperCase() + dataType.substr(1)](reload);
|
return this["load" + dataType[0].toUpperCase() + dataType.substr(1)](reload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Zotero.DataObject.prototype.loadAllData = function (reload) {
|
||||||
|
let loadPromises = new Array(this._dataTypes.length);
|
||||||
|
for (let i=0; i<this._dataTypes.length; i++) {
|
||||||
|
loadPromises[i] = this._loadDataType(this._dataTypes[i], reload);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Zotero.Promise.all(loadPromises);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save old version of data that's being changed, to pass to the notifier
|
* Save old version of data that's being changed, to pass to the notifier
|
||||||
|
@ -422,6 +492,141 @@ Zotero.DataObject.prototype._clearFieldChange = function (field) {
|
||||||
delete this._previousData[field];
|
delete this._previousData[field];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Zotero.DataObject.prototype.isEditable = function () {
|
||||||
|
return Zotero.Libraries.isEditable(this.libraryID);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Zotero.DataObject.prototype.editCheck = function () {
|
||||||
|
if (!Zotero.Sync.Server.updatesInProgress && !Zotero.Sync.Storage.updatesInProgress && !this.isEditable()) {
|
||||||
|
throw ("Cannot edit " + this._objectType + " in read-only Zotero library");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save changes to database
|
||||||
|
*
|
||||||
|
* @return {Promise<Integer|Boolean>} Promise for itemID of new item,
|
||||||
|
* TRUE on item update, or FALSE if item was unchanged
|
||||||
|
*/
|
||||||
|
Zotero.DataObject.prototype.save = Zotero.Promise.coroutine(function* (options) {
|
||||||
|
var env = {
|
||||||
|
transactionOptions: null,
|
||||||
|
options: options || {}
|
||||||
|
};
|
||||||
|
|
||||||
|
var proceed = yield this._initSave(env);
|
||||||
|
if (!proceed) return false;
|
||||||
|
|
||||||
|
if (env.isNew) {
|
||||||
|
Zotero.debug('Saving data for new ' + this._objectType + ' to database', 4);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Zotero.debug('Updating database with new ' + this._objectType + ' data', 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Zotero.DB.executeTransaction(function* () {
|
||||||
|
yield this._saveData(env);
|
||||||
|
return yield this._finalizeSave(env);
|
||||||
|
}.bind(this), env.transactionOptions)
|
||||||
|
.catch(e => {
|
||||||
|
return this._recoverFromSaveError(env, e)
|
||||||
|
.catch(function(e2) {
|
||||||
|
Zotero.debug(e2, 1);
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
Zotero.debug(e, 1);
|
||||||
|
throw e;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Zotero.DataObject.prototype.hasChanged = function() {
|
||||||
|
Zotero.debug(this._changed);
|
||||||
|
return !!Object.keys(this._changed).filter(dataType => this._changed[dataType]).length
|
||||||
|
}
|
||||||
|
|
||||||
|
Zotero.DataObject.prototype._saveData = function() {
|
||||||
|
throw new Error("Zotero.DataObject.prototype._saveData is an abstract method");
|
||||||
|
}
|
||||||
|
|
||||||
|
Zotero.DataObject.prototype._finalizeSave = function() {
|
||||||
|
throw new Error("Zotero.DataObject.prototype._finalizeSave is an abstract method");
|
||||||
|
}
|
||||||
|
|
||||||
|
Zotero.DataObject.prototype._recoverFromSaveError = Zotero.Promise.coroutine(function* () {
|
||||||
|
yield this.reload(null, true);
|
||||||
|
this._clearChanged();
|
||||||
|
});
|
||||||
|
|
||||||
|
Zotero.DataObject.prototype._initSave = Zotero.Promise.coroutine(function* (env) {
|
||||||
|
env.isNew = !this.id;
|
||||||
|
|
||||||
|
if (!env.options.skipEditCheck) this.editCheck();
|
||||||
|
|
||||||
|
if (!this.hasChanged()) {
|
||||||
|
Zotero.debug(this._ObjectType + ' ' + this.id + ' has not changed', 4);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register this object's identifiers in Zotero.DataObjects on transaction commit,
|
||||||
|
// before other callbacks run
|
||||||
|
if (env.isNew) {
|
||||||
|
env.transactionOptions = {
|
||||||
|
onCommit: () => {
|
||||||
|
this.ObjectsClass.registerIdentifiers(env.id, env.libraryID, env.key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete object from database
|
||||||
|
*/
|
||||||
|
Zotero.DataObject.prototype.erase = Zotero.Promise.coroutine(function* () {
|
||||||
|
var env = {};
|
||||||
|
|
||||||
|
var proceed = yield this._eraseInit(env);
|
||||||
|
if (!proceed) return false;
|
||||||
|
|
||||||
|
Zotero.debug('Deleting ' + this.objectType + ' ' + this.id);
|
||||||
|
|
||||||
|
yield Zotero.DB.executeTransaction(function* () {
|
||||||
|
yield this._eraseData(env);
|
||||||
|
yield this._erasePreCommit(env);
|
||||||
|
}.bind(this))
|
||||||
|
.catch(e => {
|
||||||
|
return this._eraseRecoverFromFailure(env);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this._erasePostCommit(env);
|
||||||
|
});
|
||||||
|
|
||||||
|
Zotero.DataObject.prototype._eraseInit = function(env) {
|
||||||
|
if (!this.id) return Zotero.Promise.resolve(false);
|
||||||
|
|
||||||
|
return Zotero.Promise.resolve(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
Zotero.DataObject.prototype._eraseData = function(env) {
|
||||||
|
throw new Error("Zotero.DataObject.prototype._eraseData is an abstract method");
|
||||||
|
};
|
||||||
|
|
||||||
|
Zotero.DataObject.prototype._erasePreCommit = function(env) {
|
||||||
|
return Zotero.Promise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
Zotero.DataObject.prototype._erasePostCommit = function(env) {
|
||||||
|
return Zotero.Promise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
Zotero.DataObject.prototype._eraseRecoverFromFailure = function(env) {
|
||||||
|
throw new Error("Zotero.DataObject.prototype._eraseRecoverFromFailure is an abstract method");
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates data object key
|
* Generates data object key
|
||||||
* @return {String} key
|
* @return {String} key
|
||||||
|
|
|
@ -59,12 +59,12 @@ Zotero.DataObjectUtilities = {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
"getObjectTypePlural": function getObjectTypePlural(objectType) {
|
"getObjectTypePlural": function(objectType) {
|
||||||
return objectType == 'search' ? 'searches' : objectType + 's';
|
return objectType == 'search' ? 'searches' : objectType + 's';
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
"getClassForObjectType": function getClassForObjectType(objectType) {
|
"getObjectsClassForObjectType": function(objectType) {
|
||||||
var objectTypePlural = this.getObjectTypePlural(objectType);
|
var objectTypePlural = this.getObjectTypePlural(objectType);
|
||||||
var className = objectTypePlural[0].toUpperCase() + objectTypePlural.substr(1);
|
var className = objectTypePlural[0].toUpperCase() + objectTypePlural.substr(1);
|
||||||
return Zotero[className]
|
return Zotero[className]
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -103,7 +103,7 @@ Zotero.ItemFields = new function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof field == 'number') {
|
if (typeof field == 'number') {
|
||||||
return field;
|
return _fields[field] ? field : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _fields[field] ? _fields[field]['id'] : false;
|
return _fields[field] ? _fields[field]['id'] : false;
|
||||||
|
|
|
@ -27,17 +27,16 @@
|
||||||
/*
|
/*
|
||||||
* Primary interface for accessing Zotero items
|
* Primary interface for accessing Zotero items
|
||||||
*/
|
*/
|
||||||
Zotero.Items = new function() {
|
Zotero.Items = function() {
|
||||||
Zotero.DataObjects.apply(this, ['item']);
|
this.constructor = null;
|
||||||
this.constructor.prototype = new Zotero.DataObjects();
|
|
||||||
|
|
||||||
// Privileged methods
|
this._ZDO_object = 'item';
|
||||||
this.add = add;
|
|
||||||
this.getSortTitle = getSortTitle;
|
|
||||||
|
|
||||||
Object.defineProperty(this, "_primaryDataSQLParts", {
|
// This needs to wait until all Zotero components are loaded to initialize,
|
||||||
|
// but otherwise it can be just a simple property
|
||||||
|
Zotero.defineProperty(this, "_primaryDataSQLParts", {
|
||||||
get: function () {
|
get: function () {
|
||||||
return _primaryDataSQLParts ? _primaryDataSQLParts : (_primaryDataSQLParts = {
|
return {
|
||||||
itemID: "O.itemID",
|
itemID: "O.itemID",
|
||||||
itemTypeID: "O.itemTypeID",
|
itemTypeID: "O.itemTypeID",
|
||||||
dateAdded: "O.dateAdded",
|
dateAdded: "O.dateAdded",
|
||||||
|
@ -88,18 +87,17 @@ Zotero.Items = new function() {
|
||||||
attachmentContentType: "IA.contentType AS attachmentContentType",
|
attachmentContentType: "IA.contentType AS attachmentContentType",
|
||||||
attachmentPath: "IA.path AS attachmentPath",
|
attachmentPath: "IA.path AS attachmentPath",
|
||||||
attachmentSyncState: "IA.syncState AS attachmentSyncState"
|
attachmentSyncState: "IA.syncState AS attachmentSyncState"
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
});
|
}, {lazy: true});
|
||||||
|
|
||||||
// Private members
|
|
||||||
var _primaryDataSQLParts;
|
|
||||||
var _cachedFields = {};
|
|
||||||
var _firstCreatorSQL = '';
|
|
||||||
var _sortCreatorSQL = '';
|
|
||||||
var _emptyTrashIdleObserver = null;
|
|
||||||
var _emptyTrashTimer = null;
|
|
||||||
|
|
||||||
|
this._primaryDataSQLFrom = "FROM items O "
|
||||||
|
+ "LEFT JOIN itemAttachments IA USING (itemID) "
|
||||||
|
+ "LEFT JOIN items IAP ON (IA.parentItemID=IAP.itemID) "
|
||||||
|
+ "LEFT JOIN itemNotes INo ON (O.itemID=INo.itemID) "
|
||||||
|
+ "LEFT JOIN items INoP ON (INo.parentItemID=INoP.itemID) "
|
||||||
|
+ "LEFT JOIN deletedItems DI ON (O.itemID=DI.itemID)";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return items marked as deleted
|
* Return items marked as deleted
|
||||||
|
@ -215,77 +213,7 @@ Zotero.Items = new function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
this._cachedFields = {};
|
||||||
* Create a new item with optional metadata and pass back the primary reference
|
|
||||||
*
|
|
||||||
* Using "var item = new Zotero.Item()" and "item.save()" directly results
|
|
||||||
* in an orphaned reference to the created item. If other code retrieves the
|
|
||||||
* new item with Zotero.Items.get() and modifies it, the original reference
|
|
||||||
* will not reflect the changes.
|
|
||||||
*
|
|
||||||
* Using this method avoids the need to call Zotero.Items.get() after save()
|
|
||||||
* in order to get the primary item reference. Since it accepts metadata
|
|
||||||
* as a JavaScript object, it also offers a simpler syntax than
|
|
||||||
* item.setField() and item.setCreator().
|
|
||||||
*
|
|
||||||
* Callers with no need for an up-to-date reference after save() (or who
|
|
||||||
* don't mind doing an extra Zotero.Items.get()) can use Zotero.Item
|
|
||||||
* directly if they prefer.
|
|
||||||
*
|
|
||||||
* Sample usage:
|
|
||||||
*
|
|
||||||
* var data = {
|
|
||||||
* title: "Shakespeare: The Invention of the Human",
|
|
||||||
* publisher: "Riverhead Hardcover",
|
|
||||||
* date: '1998-10-26',
|
|
||||||
* ISBN: 1573221201,
|
|
||||||
* pages: 745,
|
|
||||||
* creators: [
|
|
||||||
* ['Harold', 'Bloom', 'author']
|
|
||||||
* ]
|
|
||||||
* };
|
|
||||||
* var item = Zotero.Items.add('book', data);
|
|
||||||
*/
|
|
||||||
function add(itemTypeOrID, data) {
|
|
||||||
var item = new Zotero.Item(itemTypeOrID);
|
|
||||||
for (var field in data) {
|
|
||||||
if (field == 'creators') {
|
|
||||||
var i = 0;
|
|
||||||
for each(var creator in data.creators) {
|
|
||||||
// TODO: accept format from toArray()
|
|
||||||
|
|
||||||
var fields = {
|
|
||||||
firstName: creator[0],
|
|
||||||
lastName: creator[1],
|
|
||||||
fieldMode: creator[3] ? creator[3] : 0
|
|
||||||
};
|
|
||||||
|
|
||||||
var creatorDataID = Zotero.Creators.getDataID(fields);
|
|
||||||
if (creatorDataID) {
|
|
||||||
var linkedCreators = Zotero.Creators.getCreatorsWithData(creatorDataID);
|
|
||||||
// TODO: identical creators?
|
|
||||||
var creatorID = linkedCreators[0];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var creatorObj = new Zotero.Creator;
|
|
||||||
creatorObj.setFields(fields);
|
|
||||||
var creatorID = creatorObj.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
item.setCreator(i, Zotero.Creators.get(creatorID), creator[2]);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
item.setField(field, data[field]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var id = item.save();
|
|
||||||
|
|
||||||
return this.getAsync(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.cacheFields = Zotero.Promise.coroutine(function* (libraryID, fields, items) {
|
this.cacheFields = Zotero.Promise.coroutine(function* (libraryID, fields, items) {
|
||||||
if (items && items.length == 0) {
|
if (items && items.length == 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -315,14 +243,14 @@ Zotero.Items = new function() {
|
||||||
var fieldIDs = [];
|
var fieldIDs = [];
|
||||||
for each(var field in fields) {
|
for each(var field in fields) {
|
||||||
// Check if field already cached
|
// Check if field already cached
|
||||||
if (_cachedFields[libraryID] && _cachedFields[libraryID].indexOf(field) != -1) {
|
if (this._cachedFields[libraryID] && this._cachedFields[libraryID].indexOf(field) != -1) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_cachedFields[libraryID]) {
|
if (!this._cachedFields[libraryID]) {
|
||||||
_cachedFields[libraryID] = [];
|
this._cachedFields[libraryID] = [];
|
||||||
}
|
}
|
||||||
_cachedFields[libraryID].push(field);
|
this._cachedFields[libraryID].push(field);
|
||||||
|
|
||||||
if (this.isPrimaryField(field)) {
|
if (this.isPrimaryField(field)) {
|
||||||
primaryFields.push(field);
|
primaryFields.push(field);
|
||||||
|
@ -472,7 +400,7 @@ Zotero.Items = new function() {
|
||||||
for (let i=0; i<allItemIDs.length; i++) {
|
for (let i=0; i<allItemIDs.length; i++) {
|
||||||
let itemID = allItemIDs[i];
|
let itemID = allItemIDs[i];
|
||||||
let item = this._objectCache[itemID];
|
let item = this._objectCache[itemID];
|
||||||
yield this._objectCache[itemID].loadDisplayTitle()
|
yield item.loadDisplayTitle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,7 +425,7 @@ Zotero.Items = new function() {
|
||||||
// Move child items to master
|
// Move child items to master
|
||||||
var ids = otherItem.getAttachments(true).concat(otherItem.getNotes(true));
|
var ids = otherItem.getAttachments(true).concat(otherItem.getNotes(true));
|
||||||
for each(var id in ids) {
|
for each(var id in ids) {
|
||||||
var attachment = yield Zotero.Items.getAsync(id);
|
var attachment = yield this.getAsync(id);
|
||||||
|
|
||||||
// TODO: Skip identical children?
|
// TODO: Skip identical children?
|
||||||
|
|
||||||
|
@ -549,7 +477,7 @@ Zotero.Items = new function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
yield item.save();
|
yield item.save();
|
||||||
});
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -604,9 +532,11 @@ Zotero.Items = new function() {
|
||||||
/**
|
/**
|
||||||
* Start idle observer to delete trashed items older than a certain number of days
|
* Start idle observer to delete trashed items older than a certain number of days
|
||||||
*/
|
*/
|
||||||
|
this._emptyTrashIdleObserver = null;
|
||||||
|
this._emptyTrashTimer = null;
|
||||||
this.startEmptyTrashTimer = function () {
|
this.startEmptyTrashTimer = function () {
|
||||||
_emptyTrashIdleObserver = {
|
this._emptyTrashIdleObserver = {
|
||||||
observe: function (subject, topic, data) {
|
observe: (subject, topic, data) => {
|
||||||
if (topic == 'idle' || topic == 'timer-callback') {
|
if (topic == 'idle' || topic == 'timer-callback') {
|
||||||
var days = Zotero.Prefs.get('trashAutoEmptyDays');
|
var days = Zotero.Prefs.get('trashAutoEmptyDays');
|
||||||
if (!days) {
|
if (!days) {
|
||||||
|
@ -620,20 +550,20 @@ Zotero.Items = new function() {
|
||||||
// TODO: increase number after dealing with slow
|
// TODO: increase number after dealing with slow
|
||||||
// tag.getLinkedItems() call during deletes
|
// tag.getLinkedItems() call during deletes
|
||||||
var num = 10;
|
var num = 10;
|
||||||
Zotero.Items.emptyTrash(null, days, num)
|
this.emptyTrash(null, days, num)
|
||||||
.then(function (deleted) {
|
.then(deleted => {
|
||||||
if (!deleted) {
|
if (!deleted) {
|
||||||
_emptyTrashTimer = null;
|
this._emptyTrashTimer = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a timer to do more every few seconds
|
// Set a timer to do more every few seconds
|
||||||
if (!_emptyTrashTimer) {
|
if (!this._emptyTrashTimer) {
|
||||||
_emptyTrashTimer = Components.classes["@mozilla.org/timer;1"]
|
this._emptyTrashTimer = Components.classes["@mozilla.org/timer;1"]
|
||||||
.createInstance(Components.interfaces.nsITimer);
|
.createInstance(Components.interfaces.nsITimer);
|
||||||
}
|
}
|
||||||
_emptyTrashTimer.init(
|
this._emptyTrashTimer.init(
|
||||||
_emptyTrashIdleObserver.observe,
|
this._emptyTrashIdleObserver.observe,
|
||||||
5 * 1000,
|
5 * 1000,
|
||||||
Components.interfaces.nsITimer.TYPE_ONE_SHOT
|
Components.interfaces.nsITimer.TYPE_ONE_SHOT
|
||||||
);
|
);
|
||||||
|
@ -641,8 +571,8 @@ Zotero.Items = new function() {
|
||||||
}
|
}
|
||||||
// When no longer idle, cancel timer
|
// When no longer idle, cancel timer
|
||||||
else if (topic == 'back') {
|
else if (topic == 'back') {
|
||||||
if (_emptyTrashTimer) {
|
if (this._emptyTrashTimer) {
|
||||||
_emptyTrashTimer.cancel();
|
this._emptyTrashTimer.cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -650,7 +580,7 @@ Zotero.Items = new function() {
|
||||||
|
|
||||||
var idleService = Components.classes["@mozilla.org/widget/idleservice;1"].
|
var idleService = Components.classes["@mozilla.org/widget/idleservice;1"].
|
||||||
getService(Components.interfaces.nsIIdleService);
|
getService(Components.interfaces.nsIIdleService);
|
||||||
idleService.addIdleObserver(_emptyTrashIdleObserver, 305);
|
idleService.addIdleObserver(this._emptyTrashIdleObserver, 305);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -693,28 +623,12 @@ Zotero.Items = new function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
this.getPrimaryDataSQL = function () {
|
|
||||||
return "SELECT "
|
|
||||||
+ Object.keys(this._primaryDataSQLParts).map((val) => this._primaryDataSQLParts[val]).join(', ')
|
|
||||||
+ this.primaryDataSQLFrom;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
this.primaryDataSQLFrom = " FROM items O "
|
|
||||||
+ "LEFT JOIN itemAttachments IA USING (itemID) "
|
|
||||||
+ "LEFT JOIN items IAP ON (IA.parentItemID=IAP.itemID) "
|
|
||||||
+ "LEFT JOIN itemNotes INo ON (O.itemID=INo.itemID) "
|
|
||||||
+ "LEFT JOIN items INoP ON (INo.parentItemID=INoP.itemID) "
|
|
||||||
+ "LEFT JOIN deletedItems DI ON (O.itemID=DI.itemID) "
|
|
||||||
+ "WHERE 1";
|
|
||||||
|
|
||||||
|
|
||||||
this._postLoad = function (libraryID, ids) {
|
this._postLoad = function (libraryID, ids) {
|
||||||
if (!ids) {
|
if (!ids) {
|
||||||
if (!_cachedFields[libraryID]) {
|
if (!this._cachedFields[libraryID]) {
|
||||||
_cachedFields[libraryID] = [];
|
this._cachedFields[libraryID] = [];
|
||||||
}
|
}
|
||||||
_cachedFields[libraryID] = this.primaryFields.concat();
|
this._cachedFields[libraryID] = this.primaryFields.concat();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -724,6 +638,7 @@ Zotero.Items = new function() {
|
||||||
*
|
*
|
||||||
* Why do we do this entirely in SQL? Because we're crazy. Crazy like foxes.
|
* Why do we do this entirely in SQL? Because we're crazy. Crazy like foxes.
|
||||||
*/
|
*/
|
||||||
|
var _firstCreatorSQL = '';
|
||||||
function _getFirstCreatorSQL() {
|
function _getFirstCreatorSQL() {
|
||||||
if (_firstCreatorSQL) {
|
if (_firstCreatorSQL) {
|
||||||
return _firstCreatorSQL;
|
return _firstCreatorSQL;
|
||||||
|
@ -828,6 +743,7 @@ Zotero.Items = new function() {
|
||||||
/*
|
/*
|
||||||
* Generate SQL to retrieve sortCreator field
|
* Generate SQL to retrieve sortCreator field
|
||||||
*/
|
*/
|
||||||
|
var _sortCreatorSQL = '';
|
||||||
function _getSortCreatorSQL() {
|
function _getSortCreatorSQL() {
|
||||||
if (_sortCreatorSQL) {
|
if (_sortCreatorSQL) {
|
||||||
return _sortCreatorSQL;
|
return _sortCreatorSQL;
|
||||||
|
@ -947,7 +863,7 @@ Zotero.Items = new function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getSortTitle(title) {
|
this.getSortTitle = function(title) {
|
||||||
if (title === false || title === undefined) {
|
if (title === false || title === undefined) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -956,4 +872,8 @@ Zotero.Items = new function() {
|
||||||
}
|
}
|
||||||
return title.replace(/^[\[\'\"](.*)[\'\"\]]?$/, '$1')
|
return title.replace(/^[\[\'\"](.*)[\'\"\]]?$/, '$1')
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
Zotero.DataObjects.call(this);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}.bind(Object.create(Zotero.DataObjects.prototype))();
|
||||||
|
|
|
@ -28,7 +28,7 @@ Zotero.Libraries = new function () {
|
||||||
_userLibraryID,
|
_userLibraryID,
|
||||||
_libraryDataLoaded = false;
|
_libraryDataLoaded = false;
|
||||||
|
|
||||||
Zotero.Utilities.Internal.defineProperty(this, 'userLibraryID', {
|
Zotero.defineProperty(this, 'userLibraryID', {
|
||||||
get: function() {
|
get: function() {
|
||||||
if (!_libraryDataLoaded) {
|
if (!_libraryDataLoaded) {
|
||||||
throw new Error("Library data not yet loaded");
|
throw new Error("Library data not yet loaded");
|
||||||
|
@ -177,4 +177,12 @@ Zotero.Libraries = new function () {
|
||||||
throw new Error("Unsupported library type '" + type + "' in Zotero.Libraries.getName()");
|
throw new Error("Unsupported library type '" + type + "' in Zotero.Libraries.getName()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
this.isGroupLibrary = function (libraryID) {
|
||||||
|
if (!_libraryDataLoaded) {
|
||||||
|
throw new Error("Library data not yet loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getType(libraryID) == 'group';
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,18 +23,15 @@
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Zotero.Relations = new function () {
|
Zotero.Relations = function () {
|
||||||
Zotero.DataObjects.apply(this, ['relation']);
|
this.constructor = null;
|
||||||
this.constructor.prototype = new Zotero.DataObjects();
|
|
||||||
|
|
||||||
this.__defineGetter__('relatedItemPredicate', function () "dc:relation");
|
this._ZDO_object = 'relation';
|
||||||
this.__defineGetter__('linkedObjectPredicate', function () "owl:sameAs");
|
this._ZDO_idOnly = true;
|
||||||
this.__defineGetter__('deletedItemPredicate', function () 'dc:isReplacedBy');
|
|
||||||
|
|
||||||
var _namespaces = {
|
Zotero.defineProperty(this, 'relatedItemPredicate', {value: 'dc:relation'});
|
||||||
dc: 'http://purl.org/dc/elements/1.1/',
|
Zotero.defineProperty(this, 'linkedObjectPredicate', {value: 'owl:sameAs'});
|
||||||
owl: 'http://www.w3.org/2002/07/owl#'
|
Zotero.defineProperty(this, 'deletedItemPredicate', {value: 'dc:isReplacedBy'});
|
||||||
};
|
|
||||||
|
|
||||||
this.get = function (id) {
|
this.get = function (id) {
|
||||||
if (typeof id != 'number') {
|
if (typeof id != 'number') {
|
||||||
|
@ -52,7 +49,7 @@ Zotero.Relations = new function () {
|
||||||
*/
|
*/
|
||||||
this.getByURIs = Zotero.Promise.coroutine(function* (subject, predicate, object) {
|
this.getByURIs = Zotero.Promise.coroutine(function* (subject, predicate, object) {
|
||||||
if (predicate) {
|
if (predicate) {
|
||||||
predicate = _getPrefixAndValue(predicate).join(':');
|
predicate = this._getPrefixAndValue(predicate).join(':');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!subject && !predicate && !object) {
|
if (!subject && !predicate && !object) {
|
||||||
|
@ -141,7 +138,7 @@ Zotero.Relations = new function () {
|
||||||
|
|
||||||
|
|
||||||
this.add = Zotero.Promise.coroutine(function* (libraryID, subject, predicate, object) {
|
this.add = Zotero.Promise.coroutine(function* (libraryID, subject, predicate, object) {
|
||||||
predicate = _getPrefixAndValue(predicate).join(':');
|
predicate = this._getPrefixAndValue(predicate).join(':');
|
||||||
|
|
||||||
var relation = new Zotero.Relation;
|
var relation = new Zotero.Relation;
|
||||||
if (!libraryID) {
|
if (!libraryID) {
|
||||||
|
@ -272,11 +269,15 @@ Zotero.Relations = new function () {
|
||||||
return relation;
|
return relation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._namespaces = {
|
||||||
|
dc: 'http://purl.org/dc/elements/1.1/',
|
||||||
|
owl: 'http://www.w3.org/2002/07/owl#'
|
||||||
|
};
|
||||||
|
|
||||||
function _getPrefixAndValue(uri) {
|
this._getPrefixAndValue = function(uri) {
|
||||||
var [prefix, value] = uri.split(':');
|
var [prefix, value] = uri.split(':');
|
||||||
if (prefix && value) {
|
if (prefix && value) {
|
||||||
if (!_namespaces[prefix]) {
|
if (!this._namespaces[prefix]) {
|
||||||
throw ("Invalid prefix '" + prefix + "' in Zotero.Relations._getPrefixAndValue()");
|
throw ("Invalid prefix '" + prefix + "' in Zotero.Relations._getPrefixAndValue()");
|
||||||
}
|
}
|
||||||
return [prefix, value];
|
return [prefix, value];
|
||||||
|
@ -290,4 +291,8 @@ Zotero.Relations = new function () {
|
||||||
}
|
}
|
||||||
throw ("Invalid namespace in URI '" + uri + "' in Zotero.Relations._getPrefixAndValue()");
|
throw ("Invalid namespace in URI '" + uri + "' in Zotero.Relations._getPrefixAndValue()");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
Zotero.DataObjects.call(this);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}.bind(Object.create(Zotero.DataObjects.prototype))();
|
|
@ -37,13 +37,10 @@ Zotero.Search = function() {
|
||||||
this._hasPrimaryConditions = false;
|
this._hasPrimaryConditions = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Search._super = Zotero.DataObject;
|
Zotero.extendClass(Zotero.DataObject, Zotero.Search);
|
||||||
Zotero.Search.prototype = Object.create(Zotero.Search._super.prototype);
|
|
||||||
Zotero.Search.constructor = Zotero.Search;
|
|
||||||
|
|
||||||
Zotero.Search.prototype._objectType = 'search';
|
Zotero.Search.prototype._objectType = 'search';
|
||||||
Zotero.Search.prototype._dataTypes = Zotero.Search._super.prototype._dataTypes.concat([
|
Zotero.Search.prototype._dataTypes = Zotero.Search._super.prototype._dataTypes.concat([
|
||||||
'primaryData',
|
|
||||||
'conditions'
|
'conditions'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -62,21 +59,33 @@ Zotero.Search.prototype.setName = function(val) {
|
||||||
this.name = val;
|
this.name = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Zotero.defineProperty(Zotero.Search.prototype, 'id', {
|
||||||
Zotero.Search.prototype.__defineGetter__('id', function () { return this._get('id'); });
|
get: function() this._get('id'),
|
||||||
Zotero.Search.prototype.__defineSetter__('id', function (val) { this._set('id', val); });
|
set: function(val) this._set('id', val)
|
||||||
Zotero.Search.prototype.__defineGetter__('libraryID', function () { return this._get('libraryID'); });
|
});
|
||||||
Zotero.Search.prototype.__defineSetter__('libraryID', function (val) { return this._set('libraryID', val); });
|
Zotero.defineProperty(Zotero.Search.prototype, 'libraryID', {
|
||||||
Zotero.Search.prototype.__defineGetter__('key', function () { return this._get('key'); });
|
get: function() this._get('libraryID'),
|
||||||
Zotero.Search.prototype.__defineSetter__('key', function (val) { this._set('key', val) });
|
set: function(val) this._set('libraryID', val)
|
||||||
Zotero.Search.prototype.__defineGetter__('name', function () { return this._get('name'); });
|
});
|
||||||
Zotero.Search.prototype.__defineSetter__('name', function (val) { this._set('name', val); });
|
Zotero.defineProperty(Zotero.Search.prototype, 'key', {
|
||||||
Zotero.Search.prototype.__defineGetter__('version', function () { return this._get('version'); });
|
get: function() this._get('key'),
|
||||||
Zotero.Search.prototype.__defineSetter__('version', function (val) { this._set('version', val); });
|
set: function(val) this._set('key', val)
|
||||||
Zotero.Search.prototype.__defineGetter__('synced', function () { return this._get('synced'); });
|
});
|
||||||
Zotero.Search.prototype.__defineSetter__('synced', function (val) { this._set('synced', val); });
|
Zotero.defineProperty(Zotero.Search.prototype, 'name', {
|
||||||
|
get: function() this._get('name'),
|
||||||
Zotero.Search.prototype.__defineGetter__('conditions', function (arr) { this.getSearchConditions(); });
|
set: function(val) this._set('name', val)
|
||||||
|
});
|
||||||
|
Zotero.defineProperty(Zotero.Search.prototype, 'version', {
|
||||||
|
get: function() this._get('version'),
|
||||||
|
set: function(val) this._set('version', val)
|
||||||
|
});
|
||||||
|
Zotero.defineProperty(Zotero.Search.prototype, 'synced', {
|
||||||
|
get: function() this._get('synced'),
|
||||||
|
set: function(val) this._set('synced', val)
|
||||||
|
});
|
||||||
|
Zotero.defineProperty(Zotero.Search.prototype, 'conditions', {
|
||||||
|
get: function() this.getSearchConditions()
|
||||||
|
});
|
||||||
|
|
||||||
Zotero.Search.prototype._set = function (field, value) {
|
Zotero.Search.prototype._set = function (field, value) {
|
||||||
if (field == 'id' || field == 'libraryID' || field == 'key') {
|
if (field == 'id' || field == 'libraryID' || field == 'key') {
|
||||||
|
@ -161,152 +170,115 @@ Zotero.Search.prototype.loadFromRow = function (row) {
|
||||||
this._identified = true;
|
this._identified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Zotero.Search.prototype._initSave = Zotero.Promise.coroutine(function* (env) {
|
||||||
|
if (!this.name) {
|
||||||
|
throw('Name not provided for saved search');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Zotero.Search._super.prototype._initSave.apply(this, arguments);
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
Zotero.Search.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
|
||||||
* Save the search to the DB and return a savedSearchID
|
var fixGaps = env.options.fixGaps;
|
||||||
*
|
var isNew = env.isNew;
|
||||||
* If there are gaps in the searchConditionIDs, |fixGaps| must be true
|
|
||||||
* and the caller must dispose of the search or reload the condition ids,
|
var searchID = env.id = this._id = this.id ? this.id : yield Zotero.ID.get('savedSearches');
|
||||||
* which may change after the save.
|
var libraryID = env.libraryID = this.libraryID;
|
||||||
*
|
var key = env.key = this._key = this.key ? this.key : this._generateKey();
|
||||||
* For new searches, name must be set called before saving
|
|
||||||
*/
|
var columns = [
|
||||||
Zotero.Search.prototype.save = Zotero.Promise.coroutine(function* (fixGaps) {
|
'savedSearchID',
|
||||||
try {
|
'savedSearchName',
|
||||||
Zotero.Searches.editCheck(this);
|
'clientDateModified',
|
||||||
|
'libraryID',
|
||||||
if (!this.name) {
|
'key',
|
||||||
throw('Name not provided for saved search');
|
'version',
|
||||||
}
|
'synced'
|
||||||
|
];
|
||||||
var isNew = !this.id;
|
var placeholders = columns.map(function () '?').join();
|
||||||
|
var sqlValues = [
|
||||||
// Register this item's identifiers in Zotero.DataObjects on transaction commit,
|
searchID ? { int: searchID } : null,
|
||||||
// before other callbacks run
|
{ string: this.name },
|
||||||
var searchID, libraryID, key;
|
Zotero.DB.transactionDateTime,
|
||||||
if (isNew) {
|
this.libraryID ? this.libraryID : 0,
|
||||||
var transactionOptions = {
|
key,
|
||||||
onCommit: function () {
|
this.version ? this.version : 0,
|
||||||
Zotero.Searches.registerIdentifiers(searchID, libraryID, key);
|
this.synced ? 1 : 0
|
||||||
}
|
];
|
||||||
};
|
|
||||||
}
|
var sql = "REPLACE INTO savedSearches (" + columns.join(', ') + ") "
|
||||||
else {
|
+ "VALUES (" + placeholders + ")";
|
||||||
var transactionOptions = null;
|
var insertID = yield Zotero.DB.queryAsync(sql, sqlValues);
|
||||||
}
|
if (!searchID) {
|
||||||
|
searchID = env.id = insertID;
|
||||||
return Zotero.DB.executeTransaction(function* () {
|
|
||||||
searchID = this._id = this.id ? this.id : yield Zotero.ID.get('savedSearches');
|
|
||||||
libraryID = this.libraryID;
|
|
||||||
key = this._key = this.key ? this.key : this._generateKey();
|
|
||||||
|
|
||||||
Zotero.debug("Saving " + (isNew ? 'new ' : '') + "search " + this.id);
|
|
||||||
|
|
||||||
var columns = [
|
|
||||||
'savedSearchID',
|
|
||||||
'savedSearchName',
|
|
||||||
'clientDateModified',
|
|
||||||
'libraryID',
|
|
||||||
'key',
|
|
||||||
'version',
|
|
||||||
'synced'
|
|
||||||
];
|
|
||||||
var placeholders = columns.map(function () '?').join();
|
|
||||||
var sqlValues = [
|
|
||||||
searchID ? { int: searchID } : null,
|
|
||||||
{ string: this.name },
|
|
||||||
Zotero.DB.transactionDateTime,
|
|
||||||
this.libraryID ? this.libraryID : 0,
|
|
||||||
key,
|
|
||||||
this.version ? this.version : 0,
|
|
||||||
this.synced ? 1 : 0
|
|
||||||
];
|
|
||||||
|
|
||||||
var sql = "REPLACE INTO savedSearches (" + columns.join(', ') + ") "
|
|
||||||
+ "VALUES (" + placeholders + ")";
|
|
||||||
var insertID = yield Zotero.DB.queryAsync(sql, sqlValues);
|
|
||||||
if (!searchID) {
|
|
||||||
searchID = insertID;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isNew) {
|
|
||||||
var sql = "DELETE FROM savedSearchConditions WHERE savedSearchID=?";
|
|
||||||
yield Zotero.DB.queryAsync(sql, this.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close gaps in savedSearchIDs
|
|
||||||
var saveConditions = {};
|
|
||||||
var i = 1;
|
|
||||||
for (var id in this._conditions) {
|
|
||||||
if (!fixGaps && id != i) {
|
|
||||||
Zotero.DB.rollbackTransaction();
|
|
||||||
throw ('searchConditionIDs not contiguous and |fixGaps| not set in save() of saved search ' + this._id);
|
|
||||||
}
|
|
||||||
saveConditions[i] = this._conditions[id];
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._conditions = saveConditions;
|
|
||||||
|
|
||||||
for (var i in this._conditions){
|
|
||||||
var sql = "INSERT INTO savedSearchConditions (savedSearchID, "
|
|
||||||
+ "searchConditionID, condition, operator, value, required) "
|
|
||||||
+ "VALUES (?,?,?,?,?,?)";
|
|
||||||
|
|
||||||
// Convert condition and mode to "condition[/mode]"
|
|
||||||
var condition = this._conditions[i].mode ?
|
|
||||||
this._conditions[i].condition + '/' + this._conditions[i].mode :
|
|
||||||
this._conditions[i].condition
|
|
||||||
|
|
||||||
var sqlParams = [
|
|
||||||
searchID,
|
|
||||||
i,
|
|
||||||
condition,
|
|
||||||
this._conditions[i].operator ? this._conditions[i].operator : null,
|
|
||||||
this._conditions[i].value ? this._conditions[i].value : null,
|
|
||||||
this._conditions[i].required ? 1 : null
|
|
||||||
];
|
|
||||||
yield Zotero.DB.queryAsync(sql, sqlParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (isNew) {
|
|
||||||
Zotero.Notifier.trigger('add', 'search', this.id);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Zotero.Notifier.trigger('modify', 'search', this.id, this._previousData);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNew && this.libraryID) {
|
|
||||||
var groupID = Zotero.Groups.getGroupIDFromLibraryID(this.libraryID);
|
|
||||||
var group = yield Zotero.Groups.get(groupID);
|
|
||||||
group.clearSearchCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNew) {
|
|
||||||
var id = this.id;
|
|
||||||
this._disabled = true;
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
yield this.reload();
|
|
||||||
this._clearChanged();
|
|
||||||
|
|
||||||
return isNew ? this.id : true;
|
|
||||||
}.bind(this), transactionOptions);
|
|
||||||
}
|
}
|
||||||
catch (e) {
|
|
||||||
try {
|
if (!isNew) {
|
||||||
yield this.reload();
|
var sql = "DELETE FROM savedSearchConditions WHERE savedSearchID=?";
|
||||||
this._clearChanged();
|
yield Zotero.DB.queryAsync(sql, this.id);
|
||||||
}
|
|
||||||
catch (e2) {
|
|
||||||
Zotero.debug(e2, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Zotero.debug(e, 1);
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close gaps in savedSearchIDs
|
||||||
|
var saveConditions = {};
|
||||||
|
var i = 1;
|
||||||
|
for (var id in this._conditions) {
|
||||||
|
if (!fixGaps && id != i) {
|
||||||
|
Zotero.DB.rollbackTransaction();
|
||||||
|
throw ('searchConditionIDs not contiguous and |fixGaps| not set in save() of saved search ' + this._id);
|
||||||
|
}
|
||||||
|
saveConditions[i] = this._conditions[id];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._conditions = saveConditions;
|
||||||
|
|
||||||
|
for (var i in this._conditions){
|
||||||
|
var sql = "INSERT INTO savedSearchConditions (savedSearchID, "
|
||||||
|
+ "searchConditionID, condition, operator, value, required) "
|
||||||
|
+ "VALUES (?,?,?,?,?,?)";
|
||||||
|
|
||||||
|
// Convert condition and mode to "condition[/mode]"
|
||||||
|
var condition = this._conditions[i].mode ?
|
||||||
|
this._conditions[i].condition + '/' + this._conditions[i].mode :
|
||||||
|
this._conditions[i].condition
|
||||||
|
|
||||||
|
var sqlParams = [
|
||||||
|
searchID,
|
||||||
|
i,
|
||||||
|
condition,
|
||||||
|
this._conditions[i].operator ? this._conditions[i].operator : null,
|
||||||
|
this._conditions[i].value ? this._conditions[i].value : null,
|
||||||
|
this._conditions[i].required ? 1 : null
|
||||||
|
];
|
||||||
|
yield Zotero.DB.queryAsync(sql, sqlParams);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Zotero.Search.prototype._finalizeSave = Zotero.Promise.coroutine(function* (env) {
|
||||||
|
var isNew = env.isNew;
|
||||||
|
if (isNew) {
|
||||||
|
Zotero.Notifier.trigger('add', 'search', this.id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Zotero.Notifier.trigger('modify', 'search', this.id, this._previousData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNew && Zotero.Libraries.isGroupLibrary(this.libraryID)) {
|
||||||
|
var groupID = Zotero.Groups.getGroupIDFromLibraryID(this.libraryID);
|
||||||
|
var group = yield Zotero.Groups.get(groupID);
|
||||||
|
group.clearSearchCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNew) {
|
||||||
|
var id = this.id;
|
||||||
|
this._disabled = true;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield this.reload();
|
||||||
|
this._clearChanged();
|
||||||
|
|
||||||
|
return isNew ? this.id : true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -1189,7 +1161,7 @@ Zotero.Search.prototype._buildQuery = Zotero.Promise.coroutine(function* () {
|
||||||
let objLibraryID;
|
let objLibraryID;
|
||||||
let objKey = condition.value;
|
let objKey = condition.value;
|
||||||
let objectType = condition.name == 'collection' ? 'collection' : 'search';
|
let objectType = condition.name == 'collection' ? 'collection' : 'search';
|
||||||
let objectTypeClass = Zotero.DataObjectUtilities.getClassForObjectType(objectType);
|
let objectTypeClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType(objectType);
|
||||||
|
|
||||||
// Old-style library-key hash
|
// Old-style library-key hash
|
||||||
if (objKey.contains('_')) {
|
if (objKey.contains('_')) {
|
||||||
|
@ -1665,29 +1637,25 @@ Zotero.Search.prototype._buildQuery = Zotero.Promise.coroutine(function* () {
|
||||||
this._sqlParams = sqlParams.length ? sqlParams : false;
|
this._sqlParams = sqlParams.length ? sqlParams : false;
|
||||||
});
|
});
|
||||||
|
|
||||||
Zotero.Searches = new function(){
|
Zotero.Searches = function() {
|
||||||
Zotero.DataObjects.apply(this, ['search', 'searches', 'savedSearch', 'savedSearches']);
|
this.constructor = null;
|
||||||
this.constructor.prototype = new Zotero.DataObjects();
|
|
||||||
|
|
||||||
Object.defineProperty(this, "_primaryDataSQLParts", {
|
this._ZDO_object = 'search';
|
||||||
get: function () {
|
this._ZDO_id = 'savedSearch';
|
||||||
return _primaryDataSQLParts ? _primaryDataSQLParts : (_primaryDataSQLParts = {
|
this._ZDO_table = 'savedSearches';
|
||||||
savedSearchID: "O.savedSearchID",
|
|
||||||
name: "O.savedSearchName",
|
|
||||||
libraryID: "O.libraryID",
|
|
||||||
key: "O.key",
|
|
||||||
version: "O.version",
|
|
||||||
synced: "O.synced"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
this._primaryDataSQLParts = {
|
||||||
var _primaryDataSQLParts;
|
savedSearchID: "O.savedSearchID",
|
||||||
|
name: "O.savedSearchName",
|
||||||
|
libraryID: "O.libraryID",
|
||||||
|
key: "O.key",
|
||||||
|
version: "O.version",
|
||||||
|
synced: "O.synced"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
this.init = Zotero.Promise.coroutine(function* () {
|
this.init = Zotero.Promise.coroutine(function* () {
|
||||||
yield this.constructor.prototype.init.apply(this);
|
yield Zotero.DataObjects.prototype.init.apply(this);
|
||||||
yield Zotero.SearchConditions.init();
|
yield Zotero.SearchConditions.init();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1735,6 +1703,8 @@ Zotero.Searches = new function(){
|
||||||
let id = ids[i];
|
let id = ids[i];
|
||||||
var search = new Zotero.Search;
|
var search = new Zotero.Search;
|
||||||
search.id = id;
|
search.id = id;
|
||||||
|
yield search.loadPrimaryData();
|
||||||
|
yield search.loadConditions();
|
||||||
notifierData[id] = { old: search.serialize() };
|
notifierData[id] = { old: search.serialize() };
|
||||||
|
|
||||||
var sql = "DELETE FROM savedSearchConditions WHERE savedSearchID=?";
|
var sql = "DELETE FROM savedSearchConditions WHERE savedSearchID=?";
|
||||||
|
@ -1756,7 +1726,11 @@ Zotero.Searches = new function(){
|
||||||
+ Object.keys(this._primaryDataSQLParts).map(key => this._primaryDataSQLParts[key]).join(", ") + " "
|
+ Object.keys(this._primaryDataSQLParts).map(key => this._primaryDataSQLParts[key]).join(", ") + " "
|
||||||
+ "FROM savedSearches O WHERE 1";
|
+ "FROM savedSearches O WHERE 1";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
Zotero.DataObjects.call(this);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}.bind(Object.create(Zotero.DataObjects.prototype))();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -493,24 +493,6 @@ Zotero.Utilities.Internal = {
|
||||||
}, 0, 0, null);
|
}, 0, 0, null);
|
||||||
|
|
||||||
return pipe.inputStream;
|
return pipe.inputStream;
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines property on the object
|
|
||||||
* More compact way to do Object.defineProperty
|
|
||||||
*
|
|
||||||
* @param {Object} obj Target object
|
|
||||||
* @param {String} prop Property to be defined
|
|
||||||
* @param {Object} desc Propery descriptor. If not overriden, "enumerable" is true
|
|
||||||
*/
|
|
||||||
"defineProperty": function(obj, prop, desc) {
|
|
||||||
if (typeof prop != 'string') throw new Error("Property must be a string");
|
|
||||||
var d = { __proto__: null, enumerable: true }; // Enumerable by default
|
|
||||||
for (let p in desc) {
|
|
||||||
if (!desc.hasOwnProperty(p)) continue;
|
|
||||||
d[p] = desc[p];
|
|
||||||
}
|
|
||||||
Object.defineProperty(obj, prop, d);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1400,6 +1400,40 @@ Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines property on the object
|
||||||
|
* More compact way to do Object.defineProperty
|
||||||
|
*
|
||||||
|
* @param {Object} obj Target object
|
||||||
|
* @param {String} prop Property to be defined
|
||||||
|
* @param {Object} desc Propery descriptor. If not overriden, "enumerable" is true
|
||||||
|
* @param {Object} opts Options:
|
||||||
|
* lazy {Boolean} If true, the _getter_ is intended for late
|
||||||
|
* initialization of the property. The getter is replaced with a simple
|
||||||
|
* property once initialized.
|
||||||
|
*/
|
||||||
|
this.defineProperty = function(obj, prop, desc, opts) {
|
||||||
|
if (typeof prop != 'string') throw new Error("Property must be a string");
|
||||||
|
var d = { __proto__: null, enumerable: true, configurable: true }; // Enumerable by default
|
||||||
|
for (let p in desc) {
|
||||||
|
if (!desc.hasOwnProperty(p)) continue;
|
||||||
|
d[p] = desc[p];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts) {
|
||||||
|
if (opts.lazy && d.get) {
|
||||||
|
let getter = d.get;
|
||||||
|
d.get = function() {
|
||||||
|
var val = getter.call(this);
|
||||||
|
this[prop] = val; // Replace getter with value
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(obj, prop, d);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function should be removed
|
* This function should be removed
|
||||||
*
|
*
|
||||||
|
@ -1497,6 +1531,12 @@ Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.defineProperty(this, "localeCompare", {
|
||||||
|
get: function() {
|
||||||
|
var collation = this.getLocaleCollation();
|
||||||
|
return collation.compareString.bind(collation, 1);
|
||||||
|
}
|
||||||
|
}, {lazy: true});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sets font size based on prefs -- intended for use on root element
|
* Sets font size based on prefs -- intended for use on root element
|
||||||
|
@ -1579,6 +1619,46 @@ Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines property on the object
|
||||||
|
* More compact way to do Object.defineProperty
|
||||||
|
*
|
||||||
|
* @param {Object} obj Target object
|
||||||
|
* @param {String} prop Property to be defined
|
||||||
|
* @param {Object} desc Propery descriptor. If not overriden, "enumerable" is true
|
||||||
|
* @param {Object} opts Options:
|
||||||
|
* lateInit {Boolean} If true, the _getter_ is intended for late
|
||||||
|
* initialization of the property. The getter is replaced with a simple
|
||||||
|
* property once initialized.
|
||||||
|
*/
|
||||||
|
this.defineProperty = function(obj, prop, desc, opts) {
|
||||||
|
if (typeof prop != 'string') throw new Error("Property must be a string");
|
||||||
|
var d = { __proto__: null, enumerable: true, configurable: true }; // Enumerable by default
|
||||||
|
for (let p in desc) {
|
||||||
|
if (!desc.hasOwnProperty(p)) continue;
|
||||||
|
d[p] = desc[p];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts) {
|
||||||
|
if (opts.lateInit && d.get) {
|
||||||
|
let getter = d.get;
|
||||||
|
d.get = function() {
|
||||||
|
var val = getter.call(this);
|
||||||
|
this[prop] = val; // Replace getter with value
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(obj, prop, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.extendClass = function(superClass, newClass) {
|
||||||
|
newClass._super = superClass;
|
||||||
|
newClass.prototype = Object.create(superClass.prototype);
|
||||||
|
newClass.prototype.constructor = newClass;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allow other events (e.g., UI updates) on main thread to be processed if necessary
|
* Allow other events (e.g., UI updates) on main thread to be processed if necessary
|
||||||
*
|
*
|
||||||
|
|
|
@ -1971,6 +1971,7 @@ var ZoteroPane = new function()
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var deferred = Zotero.Promise.defer();
|
||||||
this.collectionsView.addEventListener('load', function () {
|
this.collectionsView.addEventListener('load', function () {
|
||||||
Zotero.spawn(function* () {
|
Zotero.spawn(function* () {
|
||||||
var currentLibraryID = self.getSelectedLibraryID();
|
var currentLibraryID = self.getSelectedLibraryID();
|
||||||
|
@ -1993,15 +1994,22 @@ var ZoteroPane = new function()
|
||||||
yield self.collectionsView.selectLibrary(item.libraryID);
|
yield self.collectionsView.selectLibrary(item.libraryID);
|
||||||
yield self.itemsView.selectItem(itemID, expand);
|
yield self.itemsView.selectItem(itemID, expand);
|
||||||
}
|
}
|
||||||
|
deferred.resolve(true);
|
||||||
|
})
|
||||||
|
.catch(function(e) {
|
||||||
|
deferred.reject(e);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.catch(function(e) {
|
||||||
|
deferred.reject(e);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// open Zotero pane
|
// open Zotero pane
|
||||||
this.show();
|
this.show();
|
||||||
|
|
||||||
return true;
|
return deferred.promise;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue