libzotero
Version:
javascript libZotero
266 lines (232 loc) • 7.45 kB
JavaScript
'use strict';
var log = require('./Log.js').Logger('libZotero:Items');
module.exports = function (jsonBody) {
this.instance = 'Zotero.Items';
//represent items as array for ordering purposes
this.itemsVersion = 0;
this.syncState = {
earliestVersion: null,
latestVersion: null
};
this.itemObjects = {};
this.objectMap = this.itemObjects;
this.objectArray = [];
this.unsyncedItemKeys = [];
if (jsonBody) {
this.addItemsFromJson(jsonBody);
}
};
module.exports.prototype = new Zotero.Container();
module.exports.prototype.getItem = function (key) {
return this.getObject(key);
};
module.exports.prototype.getItems = function (keys) {
return this.getObjects(keys);
};
module.exports.prototype.addItem = function (item) {
this.addObject(item);
return this;
};
module.exports.prototype.addItemsFromJson = function (jsonBody) {
log.debug('addItemsFromJson', 3);
var items = this;
var parsedItemJson = jsonBody;
var itemsAdded = [];
parsedItemJson.forEach(function (itemObj) {
var item = new Zotero.Item(itemObj);
items.addItem(item);
itemsAdded.push(item);
});
return itemsAdded;
};
//Remove item from local set if it has been marked as deleted by the server
module.exports.prototype.removeLocalItem = function (key) {
return this.removeObject(key);
};
module.exports.prototype.removeLocalItems = function (keys) {
return this.removeObjects(keys);
};
module.exports.prototype.deleteItem = function (itemKey) {
log.debug('Zotero.Items.deleteItem', 3);
var items = this;
var item;
if (!itemKey) return false;
itemKey = items.extractKey(itemKey);
item = items.getItem(itemKey);
var urlconfig = {
'target': 'item',
'libraryType': items.owningLibrary.libraryType,
'libraryID': items.owningLibrary.libraryID,
'itemKey': item.key
};
var requestConfig = {
url: urlconfig,
type: 'DELETE',
headers: { 'If-Unmodified-Since-Version': item.get('version') }
};
return items.owningLibrary.ajaxRequest(requestConfig);
};
module.exports.prototype.deleteItems = function (deleteItems, version) {
//TODO: split into multiple requests if necessary
log.debug('Zotero.Items.deleteItems', 3);
var items = this;
var deleteKeys = [];
var i;
if (!version && items.itemsVersion !== 0) {
version = items.itemsVersion;
}
//make sure we're working with item keys, not items
var key;
for (i = 0; i < deleteItems.length; i++) {
if (!deleteItems[i]) continue;
key = items.extractKey(deleteItems[i]);
if (key) {
deleteKeys.push(key);
}
}
//split keys into chunks of 50 per request
var deleteChunks = items.chunkObjectsArray(deleteKeys);
/*
var successCallback = function(response){
var deleteProgress = index / deleteChunks.length;
Zotero.trigger("deleteProgress", {'progress': deleteProgress});
return response;
};
*/
var requestObjects = [];
for (i = 0; i < deleteChunks.length; i++) {
var deleteKeysString = deleteChunks[i].join(',');
var urlconfig = {
'target': 'items',
'libraryType': items.owningLibrary.libraryType,
'libraryID': items.owningLibrary.libraryID,
'itemKey': deleteKeysString
};
//headers['If-Unmodified-Since-Version'] = version;
var requestConfig = {
url: urlconfig,
type: 'DELETE'
};
requestObjects.push(requestConfig);
}
return items.owningLibrary.sequentialRequests(requestObjects);
};
module.exports.prototype.trashItems = function (itemsArray) {
var items = this;
var i;
for (i = 0; i < itemsArray.length; i++) {
var item = itemsArray[i];
item.set('deleted', 1);
}
return items.writeItems(itemsArray);
};
module.exports.prototype.untrashItems = function (itemsArray) {
var items = this;
var i;
for (i = 0; i < itemsArray.length; i++) {
var item = itemsArray[i];
item.set('deleted', 0);
}
return items.writeItems(itemsArray);
};
module.exports.prototype.findItems = function (config) {
var items = this;
var matchingItems = [];
Object.keys(items.itemObjects).forEach(function (key) {
var item = item.itemObjects[key];
if (config.collectionKey && item.apiObj.collections.indexOf(config.collectionKey) === -1) {
return;
}
matchingItems.push(items.itemObjects[key]);
});
return matchingItems;
};
//take an array of items and extract children into their own items
//for writing
module.exports.prototype.atomizeItems = function (itemsArray) {
//process the array of items, pulling out child notes/attachments to write
//separately with correct parentItem set and assign generated itemKeys to
//new items
var writeItems = [];
var item;
for (var i = 0; i < itemsArray.length; i++) {
item = itemsArray[i];
//generate an itemKey if the item does not already have one
var itemKey = item.get('key');
if (itemKey === '' || itemKey === null) {
var newItemKey = Zotero.utils.getKey();
item.set('key', newItemKey);
item.set('version', 0);
}
//items that already have item key always in first pass, as are their children
writeItems.push(item);
if (item.hasOwnProperty('notes') && item.notes.length > 0) {
for (var j = 0; j < item.notes.length; j++) {
item.notes[j].set('parentItem', item.get('key'));
}
writeItems = writeItems.concat(item.notes);
}
if (item.hasOwnProperty('attachments') && item.attachments.length > 0) {
for (var k = 0; k < item.attachments.length; k++) {
item.attachments[k].set('parentItem', item.get('key'));
}
writeItems = writeItems.concat(item.attachments);
}
}
return writeItems;
};
//accept an array of 'Zotero.Item's
module.exports.prototype.writeItems = function (itemsArray) {
var items = this;
var library = items.owningLibrary;
var i;
var writeItems = items.atomizeItems(itemsArray);
var config = {
'target': 'items',
'libraryType': items.owningLibrary.libraryType,
'libraryID': items.owningLibrary.libraryID
};
var writeChunks = items.chunkObjectsArray(writeItems);
var rawChunkObjects = items.rawChunks(writeChunks);
//update item with server response if successful
var writeItemsSuccessCallback = function writeItemsSuccessCallback(response) {
var _this = this;
return new Promise(function (resolve) {
var writeItemsCallback = function writeItemsCallback() {
//save updated items to IDB
if (Zotero.config.useIndexedDB) {
_this.library.idbLibrary.updateItems(_this.writeChunk);
}
Zotero.trigger('itemsChanged', { library: _this.library });
response.returnItems = _this.writeChunk;
resolve();
};
log.debug('writeItem successCallback', 3);
//@TODO: It would be nicer if rejections (for partially or entirely
// invalid updates) would propagate all the way to the end-user
// instead of being swalloed here.
items.updateObjectsFromWriteResponse(_this.writeChunk, response).then(writeItemsCallback).catch(writeItemsCallback);
});
};
log.debug('items.itemsVersion: ' + items.itemsVersion, 3);
log.debug('items.libraryVersion: ' + items.libraryVersion, 3);
var requestObjects = [];
for (i = 0; i < writeChunks.length; i++) {
var successContext = {
writeChunk: writeChunks[i],
library: library
};
var requestData = JSON.stringify(rawChunkObjects[i]);
requestObjects.push({
url: config,
type: 'POST',
data: requestData,
processData: false,
success: writeItemsSuccessCallback.bind(successContext)
});
}
return library.sequentialRequests(requestObjects).then(function (responses) {
log.debug('Done with writeItems sequentialRequests promise', 3);
return responses;
});
};