diff --git a/chrome/content/zotero/xpcom/commons.js b/chrome/content/zotero/xpcom/commons.js index f7b4d0418b..3d29cc8e4a 100644 --- a/chrome/content/zotero/xpcom/commons.js +++ b/chrome/content/zotero/xpcom/commons.js @@ -1,7 +1,7 @@ /* ***** BEGIN LICENSE BLOCK ***** - Copyright © 2009 Center for History and New Media + Copyright © 2009 Center for History and New Media George Mason University, Fairfax, Virginia, USA http://zotero.org @@ -25,12 +25,6 @@ //TODO localize Zotero.Commons = new function() { - this.createBucket = createBucket; - this.syncBucketList = syncBucketList; - this.removeBucket = removeBucket; - this._createAuthenticatedRequest = _createAuthenticatedRequest; - this._createUnauthenticatedRequest = _createUnauthenticatedRequest; - this.uri = 'http://www.archive.org/'; this.apiUrl = 'http://s3.us.archive.org'; @@ -51,7 +45,7 @@ Zotero.Commons = new function() { return buckets; }); - function createBucket(bucketName) { + this.createBucket = function (bucketName) { var accessKey = Zotero.Prefs.get("commons.accessKey"); var secretKey = Zotero.Prefs.get("commons.secretKey"); @@ -64,29 +58,28 @@ Zotero.Commons = new function() { "x-archive-meta01-language":"eng" }; - var req = this._createAuthenticatedRequest( - "PUT", "/" + bucketName, headers, accessKey, secretKey - ); - - req.onreadystatechange = function() { - if(req.readyState == 4) { - if(req.status == 201) { - // add bucketName to preference if isn't already there - var prefBucketNames = Zotero.Prefs.get("commons.buckets").split(','); - if(!Zotero.inArray(bucketName, prefBucketNames)) { - prefBucketNames.push(bucketName); - prefBucketNames.sort(); - Zotero.Prefs.set("commons.buckets", prefBucketNames.join(',')); - Zotero.Notifier.trigger('add', 'bucket', true); - } + var callback = function (req) { + if (req.status == 201) { + // add bucketName to preference if isn't already there + var prefBucketNames = Zotero.Prefs.get("commons.buckets").split(','); + if(!Zotero.inArray(bucketName, prefBucketNames)) { + prefBucketNames.push(bucketName); + prefBucketNames.sort(); + Zotero.Prefs.set("commons.buckets", prefBucketNames.join(',')); + Zotero.Notifier.trigger('add', 'bucket', true); } - else if(req.status == 403) { + } + else { + Zotero.debug(req.status); + Zotero.debug(req.responseText); + + if (req.status == 403) { alert("Bucket creation failed: authentication failed."); } - else if(req.status == 409) { + else if (req.status == 409) { alert("Bucket creation failed: bucket name already taken."); } - else if(req.status == 503) { + else if (req.status == 503) { alert("Bucket creation failed: server unavailable."); } else { @@ -94,98 +87,119 @@ Zotero.Commons = new function() { } } } - - req.send(null); + + var req = this.createAuthenticatedRequest( + "PUT", "/" + bucketName, headers, accessKey, secretKey, callback + ); } - function syncBucketList() { + this.syncBucketList = function () { var accessKey = Zotero.Prefs.get("commons.accessKey"); var secretKey = Zotero.Prefs.get("commons.secretKey"); // get list of buckets from IA - var req = this._createAuthenticatedRequest( - "GET", "/", {}, accessKey, secretKey - ); - - req.onreadystatechange = function() { - if (req.readyState == 4) { - if(req.status < 400) { - var zu = new Zotero.Utilities; - var prompt = Components.classes["@mozilla.org/network/default-prompt;1"] - .getService(Components.interfaces.nsIPrompt); - - var prefChanged = false; - var prefBuckets = Zotero.Prefs.get("commons.buckets"); - var prefBucketNames = (prefBuckets) ? prefBuckets.split(',').sort() : []; - var newPrefBucketNames = []; - var iaBucketNames = []; - var buckets = req.responseXML.getElementsByTagName("Bucket"); - for(var i = 0, len = buckets.length; i < len; i++) { - var bucketName = buckets[i].getElementsByTagName('Name')[0].textContent; - iaBucketNames.push(bucketName); - if(Zotero.inArray(bucketName, prefBucketNames)) { - newPrefBucketNames.push(bucketName); - } - } - iaBucketNames.sort(); - - // newPrefBucketNames currently contains intersection - // of prefBucketNames and iaBucketNames - var askToAddBuckets = zu.arrayDiff(newPrefBucketNames, iaBucketNames); - var askToRemoveBuckets = zu.arrayDiff(newPrefBucketNames, prefBucketNames); - - // prompt user about adding buckets - for(var i = 0, len = askToAddBuckets.length; i < len; i++) { - var result = prompt.confirm("", "'" + askToAddBuckets[i] + "' is associated with " - + "your IA account, but is not in the Zotero list of buckets\n\n" - + "Add bucket '" + askToAddBuckets[i] + "'?"); - if (result) { - newPrefBucketNames.push(askToAddBuckets[i]); - prefChanged = true; - } - } - - // prompt user about removing buckets - for(var i = 0, len = askToRemoveBuckets.length; i < len; i++) { - var result = prompt.confirm("", "'" + askToRemoveBuckets[i] + "' is in your " - + "Zotero list of buckets, but is not associated with your IA account\n\n" - + "Remove bucket '" + askToRemoveBuckets[i] + "'?"); - if (result) { - prefChanged = true; - } - else { - newPrefBucketNames.push(askToRemoveBuckets[i]); - } - } - - newPrefBucketNames.sort(); - Zotero.Prefs.set("commons.buckets", newPrefBucketNames.join(',')); - - // refresh left pane if local bucket list changed - if(prefChanged) { - Zotero.Notifier.trigger('add', 'bucket', true); - } - - // give user feedback if no difference between lists - // (don't leave user wondering if nothing happened) - if(askToAddBuckets.length == 0 && askToRemoveBuckets.length == 0) { - alert("No differences between local bucket list and IA bucket list found."); - } - } - else if(req.status == 503) { + var callback = function (req) { + // Error + if (req.status != 200) { + Zotero.debug(req.status); + Zotero.debug(req.responseText); + + if (req.status == 503) { alert("Bucket list sync failed: server unavailable."); } else { alert("Bucket list sync failed: server error " + req.status); } + + return; + } + + Zotero.debug(req.responseText); + + var zu = new Zotero.Utilities; + var prompt = Components.classes["@mozilla.org/network/default-prompt;1"] + .getService(Components.interfaces.nsIPrompt); + + var prefChanged = false; + var prefBuckets = Zotero.Prefs.get("commons.buckets"); + var prefBucketNames = prefBuckets ? prefBuckets.split(',').sort() : []; + + // Remove any duplicate buckets that got into the pref somehow + var len = prefBucketNames.length; + var hash = {}; + for each(var val in prefBucketNames) { + hash[val] = true; + } + for (var key in hash) { + prefBucketNames.push(key); + } + if (prefBucketNames.length != len) { + prefChanged = true; + } + + var newPrefBucketNames = []; + var iaBucketNames = []; + var buckets = req.responseXML.getElementsByTagName("Bucket"); + for(var i = 0, len = buckets.length; i < len; i++) { + var bucketName = buckets[i].getElementsByTagName('Name')[0].textContent; + iaBucketNames.push(bucketName); + if (prefBucketNames.indexOf(bucketName) != -1) { + newPrefBucketNames.push(bucketName); + } + } + iaBucketNames.sort(); + + // newPrefBucketNames currently contains intersection + // of prefBucketNames and iaBucketNames + var askToAddBuckets = zu.arrayDiff(prefBucketNames, iaBucketNames); + var askToRemoveBuckets = zu.arrayDiff(iaBucketNames, prefBucketNames); + + // prompt user about adding buckets + for(var i = 0, len = askToAddBuckets.length; i < len; i++) { + var result = prompt.confirm("", "'" + askToAddBuckets[i] + "' is associated with " + + "your IA account but is not in your list of buckets in Zotero.\n\n" + + "Add bucket '" + askToAddBuckets[i] + "'?"); + if (result) { + newPrefBucketNames.push(askToAddBuckets[i]); + prefChanged = true; + } + } + + // prompt user about removing buckets + for(var i = 0, len = askToRemoveBuckets.length; i < len; i++) { + var result = prompt.confirm("", "'" + askToRemoveBuckets[i] + "' is in your " + + "list of buckets in Zotero but is not associated with your IA account.\n\n" + + "Remove bucket '" + askToRemoveBuckets[i] + "' from Zotero?"); + if (result) { + prefChanged = true; + } + else { + newPrefBucketNames.push(askToRemoveBuckets[i]); + } + } + newPrefBucketNames.sort(); + Zotero.Prefs.set("commons.buckets", newPrefBucketNames.join(',')); + + // refresh left pane if local bucket list changed + if(prefChanged) { + Zotero.Notifier.trigger('add', 'bucket', true); + } + + // give user feedback if no difference between lists + // (don't leave user wondering if nothing happened) + if(askToAddBuckets.length == 0 && askToRemoveBuckets.length == 0) { + alert("Your list of buckets in Zotero is up-to-date."); } } - - req.send(null); + + var req = this.createAuthenticatedRequest( + "GET", "/", {}, accessKey, secretKey, callback + ); } - + + // remove bucketName from preference, and refresh left pane in Zotero - function removeBucket(bucketName) { + this.removeBucket = function (bucketName) { var prefBucketNames = Zotero.Prefs.get("commons.buckets").split(','); var newPrefBucketNames = []; for(var i = 0, len = prefBucketNames.length; i < len; i++) { @@ -197,8 +211,9 @@ Zotero.Commons = new function() { Zotero.Prefs.set("commons.buckets", newPrefBucketNames.join(',')); Zotero.Notifier.trigger('add', 'bucket', true); } - - function _createAuthenticatedRequest(method, resource, headers, accessKey, secretKey) { + + + this.createAuthenticatedRequest = function (method, resource, headers, accessKey, secretKey, callback, data, sendAsBinary) { var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] .createInstance(Components.interfaces.nsIXMLHttpRequest); req.open(method, Zotero.Commons.apiUrl + resource, true); @@ -228,11 +243,31 @@ Zotero.Commons = new function() { for(var header in headers) { req.setRequestHeader(header, headers[header]); } - - return req; + + if (data) { + if (sendAsBinary) { + req.sendAsBinary(data); + } + else { + req.send(data); + } + } + else { + req.send(null); + } + + + if (callback) { + req.onreadystatechange = function() { + if (req.readyState == 4) { + callback(req); + } + }; + } } - - function _createUnauthenticatedRequest(method, resource, headers) { + + + this.createUnauthenticatedRequest = function (method, resource, headers) { var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] .createInstance(Components.interfaces.nsIXMLHttpRequest); req.open(method, Zotero.Commons.apiUrl + resource, false); @@ -250,8 +285,8 @@ Zotero.Commons = new function() { // of using multiple accounts Zotero.Commons.Bucket = function(name, accessKey, secretKey) { this.name = name; - this._accessKey = accessKey; - this._secretKey = secretKey; + this.accessKey = accessKey; + this.secretKey = secretKey; this._items = null; this._requestingItems = false; this._needRefresh = false; @@ -273,11 +308,13 @@ Zotero.Commons.Bucket.prototype.__defineGetter__('uri', function () { return 'http://www.archive.org/details/' + this.name; }); + Zotero.Commons.Bucket.prototype.getKeyUrl = function(name, key) { return 'http://' + name + '.s3.us.archive.org/' + key; } + Zotero.Commons.Bucket.prototype.relationPredicate = "owl:sameAs"; @@ -303,26 +340,26 @@ Zotero.Commons.Bucket.prototype.deleteItems = function(ids) { // since cascade delete is enabled, delete the ZIP and derived files should follow. // this does not, however, delete the RDF file. var resource = '/' + self.name + '/' + zipName; - Zotero.debug("Commons: about to delete: " + resource); - - var req = Zotero.Commons._createAuthenticatedRequest( - method, resource, headers, self._accessKey, self._secretKey); - - req.onreadystatechange = function() { - if (req.readyState == 4) { - if(req.status == 204) { - Zotero.debug("Commons: " + resource + " was deleted successfully."); - this._needRefresh = true; - Zotero.Notifier.trigger('refresh', 'bucket', ids); - - //respecify metadata - self.updateMetadata(item.key,"delete",null); - - } - else if(req.status == 403) { + Zotero.debug("Commons: Deleting: " + resource); + + // Delete IA items + var callback = function (req) { + if (req.status == 204) { + Zotero.debug("Commons: " + resource + " was deleted successfully."); + this._needRefresh = true; + Zotero.Notifier.trigger('refresh', 'bucket', ids); + + //respecify metadata + self.updateMetadata(item.key,"delete",null); + } + else { + Zotero.debug(req.status); + Zotero.debug(req.responseText); + + if (req.status == 403) { alert("Failed to delete " + resource + " at IA: authentication failed."); } - else if(req.status == 503) { + else if (req.status == 503) { alert("Failed to delete " + resource + " at IA: server unavailable."); } else { @@ -331,18 +368,19 @@ Zotero.Commons.Bucket.prototype.deleteItems = function(ids) { } } }; - - req.send(null); //request to delete IA items - + + Zotero.Commons.createAuthenticatedRequest( + method, resource, headers, self.accessKey, self.secretKey, callback + ); + + // Delete Zotero RDF file zipName = item.key + ".rdf"; resource = '/' + self.name + '/' + zipName; - Zotero.debug("Commons: about to delete: " + resource); - - var req2 = Zotero.Commons._createAuthenticatedRequest( - method, resource, headers, self._accessKey, self._secretKey); - - req2.send(null); //request to delete zotero RDF file + Zotero.debug("Commons: Deleting: " + resource); + Zotero.Commons.createAuthenticatedRequest( + method, resource, headers, self.accessKey, self.secretKey + ); } } @@ -413,33 +451,31 @@ Zotero.Commons.Bucket.prototype.updateMetadata = function(key, action, data) { Zotero.debug(headers2); resource2 = '/' + self.name; - var req3 = Zotero.Commons._createAuthenticatedRequest( - method, resource2, headers2, self._accessKey, self._secretKey - ); - - req3.onreadystatechange = function() { - if (req3.readyState == 4) { - if(req3.status < 202) { - Zotero.debug("Commons: " + resource2 + " metadata updated successfully."); - // if adding item, upload file - data.bucket._putKeyCallback(data); - } - else if(req3.status == 403) { + var callback = function (req) { + if(req.status < 202) { + Zotero.debug("Commons: " + resource2 + " metadata updated successfully."); + // if adding item, upload file + data.bucket.putKeyCallback(data); + } + else { + Zotero.debug(req.status); + Zotero.debug(req.responseText); + + if (req.status == 403) { alert("Failed to change " + key + " metadata: authentication failed."); } - else if(req3.status == 503) { + else if (req.status == 503) { alert("Failed to change " + key + " metadata: server unavailable."); } else { alert("Failed to change " + key + " metadata. Status code: " + req.status); - Zotero.debug("Commons: request to change metadata failed with code: " + req.status); } } }; - req3.send(null); - Zotero.debug("Commons: metadata request sent."); - - }, function() { + + Zotero.Commons.createAuthenticatedRequest( + method, resource2, headers2, self.accessKey, self.secretKey, callback + ); }); } @@ -466,13 +502,21 @@ Zotero.Commons.Bucket.prototype.getItems = function() { this._requestingItems = true; var self = this; self._items = []; - var itemIDs = []; - Zotero.debug("Commons: getting items for: " + Zotero.Commons.apiUrl + resource); - // get a list of keys (files) associated with this bucket - Zotero.Utilities.HTTP.doGet(Zotero.Commons.apiUrl + resource, function(xmlhttp) { - + var req = Zotero.Utilities.HTTP.doGet(Zotero.Commons.apiUrl + resource, function(xmlhttp) { + if (xmlhttp.status != 200) { + Zotero.debug(xmlhttp.status); + Zotero.debug(xmlhttp.responseText); + alert("Error loading data from the Internet Archive"); + self._requestingItems = false; + return; + } + + Zotero.debug(xmlhttp.responseText); + + var itemIDs = []; + // While looking for Zotero exported items in the bucket, // check for a full-text ("derived" in IA terms) OCR'd version of the PDF. // If so, get it and add it as an attachment to the corresponding Zotero client item. @@ -551,18 +595,24 @@ Zotero.Commons.Bucket.prototype.getItems = function() { } } } - self._items.push(item); - } // end is item - } // end RDF - } // for each Content section - + + self._items.push(item); + } + } + } + Zotero.Notifier.trigger('refresh', 'bucket', itemIDs); + + self._requestingItems = false; + }); - },null); // end callBack function for doGet + // Browser offline + if (!req) { + self._requestingItems = false; + } - - self._requestingItems = false; - return self._items; + // List isn't yet available + return []; } @@ -572,11 +622,27 @@ Zotero.Commons.Bucket.prototype.uploadItems = function(ids) { var items = Zotero.Items.get(ids); if (!items) { + Zotero.debug("No items to upload"); return; } - + + var itemsToUpload = false; + for (var i=0, len=items.length; i