- Refactor Zotero.Translate save process. There are now itemSaving and itemDone handlers. The itemSaving handler is called as soon as the translator returns an item, but before it's saved to the database. The itemDone handler is called when the item is saved. This allows us to show a grayed-out item icon in the connector while the item is saving to server, and then un-gray it when it is saved to the server.

- Move Zotero.randomString() to Zotero.Utilities
- Fix import of collections (broken by recent commits)
This commit is contained in:
Simon Kornblith 2011-07-04 09:08:49 +00:00
parent ea5a94e860
commit 1774962de9
7 changed files with 189 additions and 121 deletions

View file

@ -330,7 +330,7 @@ var Zotero_File_Interface = new function() {
*/
function _importDone(obj, worked) {
// add items to import collection
_importCollection.addItems(obj.newItems);
_importCollection.addItems([item.id for each(item in obj.newItems)]);
Zotero.DB.commitTransaction();

View file

@ -26,7 +26,6 @@
Zotero.Translate.ItemSaver = function(libraryID, attachmentMode, forceTagType) {
this.newItems = [];
this._itemsToSaveToServer = [];
this._timeoutID = null;
}
@ -35,37 +34,46 @@ Zotero.Translate.ItemSaver.ATTACHMENT_MODE_DOWNLOAD = 1;
Zotero.Translate.ItemSaver.ATTACHMENT_MODE_FILE = 2;
Zotero.Translate.ItemSaver.prototype = {
"saveItem":function(item) {
/**
* Saves items to Standalone or the server
*/
"saveItems":function(items, callback) {
// don't save documents as documents, since we can't pass them around
for(var i in item.attachments) {
if(item.attachments[i].document) {
item.attachments[i].url = item.attachments[i].document.location.href;
delete item.attachments[i].document;
var nItems = items.length;
for(var i=0; i<nItems.length; i++) {
var attachments = item[i].attachments;
var nAttachments = attachments.length;
for(var j=0; j<nAttachments.length; j++) {
if(attachments[j].document) {
attachments[j].url = attachments[j].document.location.href;
delete attachments[j].document;
}
}
}
// save items
this.newItems.push(item);
var me = this;
Zotero.Connector.callMethod("saveItems", {"items":[item]}, function(success) {
if(success === false && !Zotero.isFx) {
// attempt to save to server on a timer
if(me._timeoutID) clearTimeout(me._timeoutID);
me._itemsToSaveToServer.push(item);
me._timeoutID = setTimeout(function() { me._saveToServer() }, 2000);
// first try to save items via connector
Zotero.Connector.callMethod("saveItems", {"items":items}, function(success, status) {
if(success !== false) {
Zotero.debug("Translate: Save via Standalone succeeded");
callback(true, items);
} else if(Zotero.isFx) {
callback(false, new Error("Save via Standalone failed with "+status));
} else {
me._saveToServer(items, callback);
}
});
},
"_saveToServer":function() {
/**
* Saves items to server
*/
"_saveToServer":function(items, callback) {
const IGNORE_FIELDS = ["seeAlso", "attachments", "complete"];
// clear timeout, since saving has begin
this._timeoutID = null;
var newItems = [];
for(var i in this._itemsToSaveToServer) {
var item = this._itemsToSaveToServer[i];
for(var i in items) {
var item = items[i];
var newItem = {};
newItems.push(newItem);
@ -180,15 +188,14 @@ Zotero.Translate.ItemSaver.prototype = {
var url = 'users/%%USERID%%/items?key=%%APIKEY%%';
var payload = JSON.stringify({"items":newItems}, null, "\t")
this._itemsToSaveToServer = [];
Zotero.OAuth.doAuthenticatedPost(url, payload, function(status, message) {
if(!status) {
Zotero.Messaging.sendMessage("saveDialog_error", status);
Zotero.debug("Translate: Save to server payload:\n\n"+payload);
Zotero.logError(new Error("Translate: Save to server failed: "+message));
Zotero.debug("Translate: Save to server failed with message "+message+"; payload:\n\n"+payload);
callback(false, new Error("Save to server failed with "+message));
} else {
Zotero.debug("Translate: Save to server complete");
callback(true, newItems);
}
}, true);
}

View file

@ -465,11 +465,21 @@ Zotero.Server.Connector.SaveItem.prototype = {
// save items
var itemSaver = new Zotero.Translate.ItemSaver(libraryID,
Zotero.Translate.ItemSaver.ATTACHMENT_MODE_DOWNLOAD, 1);
for each(var item in data.items) {
var savedItem = itemSaver.saveItem(item);
itemSaver.saveItems(data.items, function(returnValue, data) {
if(returnValue) {
try {
for each(var item in data) {
if(collection) collection.addItem(savedItem.id);
}
sendResponseCallback(201);
} catch(e) {
sendResponseCallback(500);
}
} else {
sendResponseCallback(500);
throw data;
}
});
}
}

View file

@ -105,17 +105,32 @@ Zotero.Translate.Sandbox = {
return;
}
var newItem = translate._itemSaver.saveItem(item);
// We use this within the connector to keep track of items as they are saved
if(!item.id) item.id = Zotero.Utilities.randomString();
if(translate instanceof Zotero.Translate.Web) {
// For web translators, we queue saves
translate.saveQueue.push(item);
translate._runHandler("itemSaving", item);
} else {
var newItem;
translate._itemSaver.saveItems([item], function(returnValue, data) {
if(returnValue) {
newItem = data[0];
translate.newItems.push(newItem);
} else {
translate.complete(false, data);
throw data;
}
});
// Allow progress meter to update
//
// This can probably be re-enabled for web translators once badly asynced ones are fixed
if(!translate.noWait && translate instanceof Zotero.Translate.Import) {
Zotero.wait();
}
if(!translate.noWait) Zotero.wait();
translate._runHandler("itemSaving", item);
// pass both the saved item and the original JS array item
translate._runHandler("itemDone", newItem, item);
}
},
/**
@ -948,6 +963,30 @@ Zotero.Translate.Base.prototype = {
this.decrementAsyncProcesses();
},
/**
* Executed when items have been saved (which may happen asynchronously, if in connector)
*
* @param {Boolean} returnValue Whether saving was successful
* @param {Zotero.Item[]|Error} data If returnValue is true, this will be an array of
* Zotero.Item objects. If returnValue is false, this will
* be a string error message.
*/
"itemsSaved":function(returnValue, data) {
if(returnValue) {
// trigger deferred itemDone events
var nItems = data.length;
for(var i=0; i<nItems; i++) {
this._runHandler("itemDone", data[i], this.saveQueue[i]);
}
this.saveQueue = [];
} else {
Zotero.logError(data);
}
this._runHandler("done", returnValue);
},
/**
* Executed on translator completion, either automatically from a synchronous scraper or as
* done() from an asynchronous scraper. Finishes things up and calls callback function(s).
@ -1008,7 +1047,14 @@ Zotero.Translate.Base.prototype = {
if(returnValue === undefined) returnValue = true;
if(returnValue) {
if(this.saveQueue.length) {
var me = this;
this._itemSaver.saveItems(this.saveQueue.slice(),
function(returnValue, data) { me.itemsSaved(returnValue, data) });
return;
} else {
this._debug("Translation successful");
}
} else {
if(error) {
// report error to console
@ -1082,6 +1128,7 @@ Zotero.Translate.Base.prototype = {
this._runningAsyncProcesses = 0;
this._returnValue = undefined;
this._aborted = false;
this.saveQueue = [];
Zotero.debug("Translate: Parsing code for "+translator.label, 4);
@ -1118,7 +1165,7 @@ Zotero.Translate.Base.prototype = {
"}"+
"};";
if(this instanceof Zotero.Translate.Export) {
if(this instanceof Zotero.Translate.Export || this instanceof Zotero.Translate.Import) {
src += "Zotero.Collection = function () {};"+
"Zotero.Collection.prototype.complete = function() { Zotero._collectionDone(this); };";
} else if (this instanceof Zotero.Translate.Import) {
@ -1312,7 +1359,7 @@ Zotero.Translate.Web.prototype._getParameters = function() { return [this.docume
Zotero.Translate.Web.prototype._prepareTranslation = function() {
this._itemSaver = new Zotero.Translate.ItemSaver(this._libraryID,
Zotero.Translate.ItemSaver[(this._saveAttachments ? "ATTACHMENT_MODE_DOWNLOAD" : "ATTACHMENT_MODE_IGNORE")], 1);
this.newItems = this._itemSaver.newItems;
this.newItems = [];
}
/**
@ -1507,7 +1554,7 @@ Zotero.Translate.Import.prototype._prepareTranslation = function() {
this._progress = undefined;
this._itemSaver = new Zotero.Translate.ItemSaver(this._libraryID,
Zotero.Translate.ItemSaver[(this._saveAttachments ? "ATTACHMENT_MODE_FILE" : "ATTACHMENT_MODE_IGNORE")]);
this.newItems = this._itemSaver.newItems;
this.newItems = [];
this.newCollections = this._itemSaver.newCollections;
}

View file

@ -72,17 +72,17 @@ Zotero.Translate.ItemSaver.ATTACHMENT_MODE_DOWNLOAD = 1;
Zotero.Translate.ItemSaver.ATTACHMENT_MODE_FILE = 2;
Zotero.Translate.ItemSaver.prototype = {
"saveItem":function(item) {
"saveItems":function(items, callback) {
// if no open transaction, open a transaction and add a timer call to close it
var openedTransaction = false;
if(!Zotero.DB.transactionInProgress()) {
Zotero.debug("Translate: Beginning transaction");
Zotero.DB.beginTransaction();
var me = this;
Zotero.setTimeout(function() { me._closeTransaction() }, 0);
Zotero.showZoteroPaneProgressMeter(Zotero.getString("ingester.scraping"), false);
openedTransaction = true;
}
try {
var newItems = [];
for each(var item in items) {
// Get typeID, defaulting to "webpage"
var newItem;
var type = (item.itemType ? item.itemType : "webpage");
@ -135,10 +135,16 @@ Zotero.Translate.ItemSaver.prototype = {
this._saveTags(item, newItem);
// add to new item list
this.newItems.push(myID);
newItem = Zotero.Items.get(myID);
return newItem;
newItems.push(newItem);
}
if(openedTransaction) Zotero.DB.commitTransaction();
callback(true, newItems);
} catch(e) {
if(openedTransaction) Zotero.DB.rollbackTransaction();
callback(false, e);
}
},
"saveCollection":function(collection) {
@ -509,15 +515,6 @@ Zotero.Translate.ItemSaver.prototype = {
}
}
}
},
/**
* Closes the transaction when the current code block finishes executing.
*/
"_closeTransaction":function() {
Zotero.debug("Translate: Closing transaction");
Zotero.hideZoteroPaneOverlay();
Zotero.DB.commitTransaction();
}
}

View file

@ -732,6 +732,24 @@ Zotero.Utilities = {
}
return strings.join(delimiter ? delimiter : ", ");
},
/**
* Generate a random string of length 'len' (defaults to 8)
**/
"randomString":function(len, chars) {
if (!chars) {
chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
}
if (!len) {
len = 8;
}
var randomstring = '';
for (var i=0; i<len; i++) {
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum,rnum+1);
}
return randomstring;
}
}

View file

@ -1471,18 +1471,7 @@ if(appInfo.platformVersion[0] >= 2) {
* Generate a random string of length 'len' (defaults to 8)
**/
function randomString(len, chars) {
if (!chars) {
chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
}
if (!len) {
len = 8;
}
var randomstring = '';
for (var i=0; i<len; i++) {
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum,rnum+1);
}
return randomstring;
return Zotero.Utilities.randomString(len, chars);
}