- 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) { function _importDone(obj, worked) {
// add items to import collection // add items to import collection
_importCollection.addItems(obj.newItems); _importCollection.addItems([item.id for each(item in obj.newItems)]);
Zotero.DB.commitTransaction(); Zotero.DB.commitTransaction();

View file

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

View file

@ -465,11 +465,21 @@ Zotero.Server.Connector.SaveItem.prototype = {
// save items // save items
var itemSaver = new Zotero.Translate.ItemSaver(libraryID, var itemSaver = new Zotero.Translate.ItemSaver(libraryID,
Zotero.Translate.ItemSaver.ATTACHMENT_MODE_DOWNLOAD, 1); Zotero.Translate.ItemSaver.ATTACHMENT_MODE_DOWNLOAD, 1);
for each(var item in data.items) { itemSaver.saveItems(data.items, function(returnValue, data) {
var savedItem = itemSaver.saveItem(item); if(returnValue) {
if(collection) collection.addItem(savedItem.id); try {
} for each(var item in data) {
sendResponseCallback(201); 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; 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();
// Allow progress meter to update if(translate instanceof Zotero.Translate.Web) {
// // For web translators, we queue saves
// This can probably be re-enabled for web translators once badly asynced ones are fixed translate.saveQueue.push(item);
if(!translate.noWait && translate instanceof Zotero.Translate.Import) { translate._runHandler("itemSaving", item);
Zotero.wait(); } 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
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);
} }
// 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(); 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 * 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). * 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 === undefined) returnValue = true;
if(returnValue) { if(returnValue) {
this._debug("Translation successful"); 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 { } else {
if(error) { if(error) {
// report error to console // report error to console
@ -1082,6 +1128,7 @@ Zotero.Translate.Base.prototype = {
this._runningAsyncProcesses = 0; this._runningAsyncProcesses = 0;
this._returnValue = undefined; this._returnValue = undefined;
this._aborted = false; this._aborted = false;
this.saveQueue = [];
Zotero.debug("Translate: Parsing code for "+translator.label, 4); 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 () {};"+ src += "Zotero.Collection = function () {};"+
"Zotero.Collection.prototype.complete = function() { Zotero._collectionDone(this); };"; "Zotero.Collection.prototype.complete = function() { Zotero._collectionDone(this); };";
} else if (this instanceof Zotero.Translate.Import) { } 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() { Zotero.Translate.Web.prototype._prepareTranslation = function() {
this._itemSaver = new Zotero.Translate.ItemSaver(this._libraryID, this._itemSaver = new Zotero.Translate.ItemSaver(this._libraryID,
Zotero.Translate.ItemSaver[(this._saveAttachments ? "ATTACHMENT_MODE_DOWNLOAD" : "ATTACHMENT_MODE_IGNORE")], 1); 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._progress = undefined;
this._itemSaver = new Zotero.Translate.ItemSaver(this._libraryID, this._itemSaver = new Zotero.Translate.ItemSaver(this._libraryID,
Zotero.Translate.ItemSaver[(this._saveAttachments ? "ATTACHMENT_MODE_FILE" : "ATTACHMENT_MODE_IGNORE")]); Zotero.Translate.ItemSaver[(this._saveAttachments ? "ATTACHMENT_MODE_FILE" : "ATTACHMENT_MODE_IGNORE")]);
this.newItems = this._itemSaver.newItems; this.newItems = [];
this.newCollections = this._itemSaver.newCollections; this.newCollections = this._itemSaver.newCollections;
} }

View file

@ -72,73 +72,79 @@ Zotero.Translate.ItemSaver.ATTACHMENT_MODE_DOWNLOAD = 1;
Zotero.Translate.ItemSaver.ATTACHMENT_MODE_FILE = 2; Zotero.Translate.ItemSaver.ATTACHMENT_MODE_FILE = 2;
Zotero.Translate.ItemSaver.prototype = { 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 // if no open transaction, open a transaction and add a timer call to close it
var openedTransaction = false;
if(!Zotero.DB.transactionInProgress()) { if(!Zotero.DB.transactionInProgress()) {
Zotero.debug("Translate: Beginning transaction");
Zotero.DB.beginTransaction(); Zotero.DB.beginTransaction();
openedTransaction = true;
var me = this;
Zotero.setTimeout(function() { me._closeTransaction() }, 0);
Zotero.showZoteroPaneProgressMeter(Zotero.getString("ingester.scraping"), false);
} }
// Get typeID, defaulting to "webpage" try {
var newItem; var newItems = [];
var type = (item.itemType ? item.itemType : "webpage"); for each(var item in items) {
// Get typeID, defaulting to "webpage"
if(type == "note") { // handle notes differently var newItem;
newItem = new Zotero.Item('note'); var type = (item.itemType ? item.itemType : "webpage");
newItem.libraryID = this._libraryID;
newItem.setNote(item.note);
var myID = newItem.save();
newItem = Zotero.Items.get(myID);
} else {
if(type == "attachment") { // handle attachments differently
newItem = this._saveAttachment(item);
if(!newItem) return;
var myID = newItem.id;
} else {
var typeID = Zotero.ItemTypes.getID(type);
newItem = new Zotero.Item(typeID);
newItem._libraryID = this._libraryID;
this._saveFields(item, newItem);
// handle creators if(type == "note") { // handle notes differently
if(item.creators) { newItem = new Zotero.Item('note');
this._saveCreators(item, newItem); newItem.libraryID = this._libraryID;
} newItem.setNote(item.note);
var myID = newItem.save();
// save item newItem = Zotero.Items.get(myID);
var myID = newItem.save(); } else {
newItem = Zotero.Items.get(myID); if(type == "attachment") { // handle attachments differently
newItem = this._saveAttachment(item);
// handle notes if(!newItem) return;
if(item.notes) { var myID = newItem.id;
this._saveNotes(item, myID); } else {
} var typeID = Zotero.ItemTypes.getID(type);
newItem = new Zotero.Item(typeID);
// handle attachments newItem._libraryID = this._libraryID;
if(item.attachments) {
for(var i=0; i<item.attachments.length; i++) { this._saveFields(item, newItem);
var newAttachment = this._saveAttachment(item.attachments[i], myID);
if(newAttachment) this._saveTags(item.attachments[i], newAttachment); // handle creators
if(item.creators) {
this._saveCreators(item, newItem);
}
// save item
var myID = newItem.save();
newItem = Zotero.Items.get(myID);
// handle notes
if(item.notes) {
this._saveNotes(item, myID);
}
// handle attachments
if(item.attachments) {
for(var i=0; i<item.attachments.length; i++) {
var newAttachment = this._saveAttachment(item.attachments[i], myID);
if(newAttachment) this._saveTags(item.attachments[i], newAttachment);
}
}
} }
} }
if(item.itemID) this._IDMap[item.itemID] = myID;
// handle see also
this._saveTags(item, newItem);
// add to new item list
newItem = Zotero.Items.get(myID);
newItems.push(newItem);
} }
if(openedTransaction) Zotero.DB.commitTransaction();
callback(true, newItems);
} catch(e) {
if(openedTransaction) Zotero.DB.rollbackTransaction();
callback(false, e);
} }
if(item.itemID) this._IDMap[item.itemID] = myID;
// handle see also
this._saveTags(item, newItem);
// add to new item list
this.newItems.push(myID);
newItem = Zotero.Items.get(myID);
return newItem;
}, },
"saveCollection":function(collection) { "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 : ", "); 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) * Generate a random string of length 'len' (defaults to 8)
**/ **/
function randomString(len, chars) { function randomString(len, chars) {
if (!chars) { return Zotero.Utilities.randomString(len, 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;
} }