Fixes #1541, shows "Remove Bucket from List" option (can't actually delete a bucket in IA)
Also adds "Create Bucket", and "Sync Bucket List with IA" features, available by right clicking the "Commons" header.
This commit is contained in:
parent
ac79b1d05f
commit
233b51d3e1
4 changed files with 249 additions and 52 deletions
|
@ -1263,6 +1263,33 @@ var ZoteroPane = new function()
|
|||
Zotero.Items.emptyTrash();
|
||||
}
|
||||
}
|
||||
|
||||
this.createBucket = function() {
|
||||
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
||||
.getService(Components.interfaces.nsIPromptService);
|
||||
|
||||
var bucketName = { value: '' };
|
||||
// TODO localize
|
||||
var result = promptService.prompt(window,
|
||||
"New Bucket",
|
||||
"Enter a name for this bucket:", bucketName, "", {});
|
||||
|
||||
if (result && bucketName.value) {
|
||||
Zotero.Commons.createBucket(bucketName.value);
|
||||
}
|
||||
}
|
||||
|
||||
this.removeBucket = function() {
|
||||
if (this.collectionsView
|
||||
&& this.collectionsView.selection
|
||||
&& this.collectionsView.selection.count > 0
|
||||
&& this.collectionsView.selection.currentIndex != -1) {
|
||||
var bucket = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex);
|
||||
if (bucket && bucket.isBucket()) {
|
||||
Zotero.Commons.removeBucket(bucket.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function editSelectedCollection()
|
||||
|
@ -1551,7 +1578,10 @@ var ZoteroPane = new function()
|
|||
createBibCollection: 8,
|
||||
exportFile: 9,
|
||||
loadReport: 10,
|
||||
emptyTrash: 11
|
||||
emptyTrash: 11,
|
||||
createBucket: 12,
|
||||
syncBucketList: 13,
|
||||
removeBucket: 14
|
||||
};
|
||||
|
||||
var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex);
|
||||
|
@ -1621,7 +1651,12 @@ var ZoteroPane = new function()
|
|||
}
|
||||
// Header
|
||||
else if (itemGroup.isHeader()) {
|
||||
|
||||
if (itemGroup.ref.id == 'commons-header') {
|
||||
show = [m.createBucket, m.syncBucketList];
|
||||
}
|
||||
}
|
||||
else if (itemGroup.isBucket()) {
|
||||
show = [m.removeBucket];
|
||||
}
|
||||
// Group
|
||||
else if (itemGroup.isGroup()) {
|
||||
|
|
|
@ -102,6 +102,9 @@
|
|||
<menuitem label="&zotero.toolbar.export.label;" oncommand="Zotero_File_Interface.exportFile()"/>
|
||||
<menuitem oncommand="Zotero_Report_Interface.loadCollectionReport()"/>
|
||||
<menuitem label="&zotero.toolbar.emptyTrash.label;" oncommand="ZoteroPane.emptyTrash();"/>
|
||||
<menuitem label="Create Bucket" oncommand="ZoteroPane.createBucket();"/><!--TODO localize -->
|
||||
<menuitem label="Sync Bucket List with IA" oncommand="Zotero.Commons.syncBucketList();"/><!--TODO localize -->
|
||||
<menuitem label="Remove Bucket from List" oncommand="ZoteroPane.removeBucket();"/><!--TODO localize -->
|
||||
</popup>
|
||||
<popup id="zotero-itemmenu" onpopupshowing="ZoteroPane.buildItemContextMenu();">
|
||||
<menuitem label="&zotero.items.menu.showInLibrary;" oncommand="ZoteroPane.selectItem(this.parentNode.getAttribute('itemID'), true)"/>
|
||||
|
@ -527,4 +530,4 @@
|
|||
oncommand="ZoteroPane.toggleDisplay();"
|
||||
modifiers="accel alt" />
|
||||
</keyset>
|
||||
</overlay>
|
||||
</overlay>
|
||||
|
|
|
@ -36,7 +36,7 @@ Zotero.CollectionTreeView = function()
|
|||
this._treebox = null;
|
||||
this.itemToSelect = null;
|
||||
this._highlightedRows = {};
|
||||
this._unregisterID = Zotero.Notifier.registerObserver(this, ['collection', 'search', 'share', 'group']);
|
||||
this._unregisterID = Zotero.Notifier.registerObserver(this, ['collection', 'search', 'share', 'group', 'bucket']);
|
||||
this.showDuplicates = false;
|
||||
}
|
||||
|
||||
|
@ -226,7 +226,7 @@ Zotero.CollectionTreeView.prototype.refresh = function()
|
|||
}
|
||||
|
||||
var buckets = Zotero.Commons.buckets;
|
||||
if(buckets.length) {
|
||||
if(buckets) {
|
||||
this._showItem(new Zotero.ItemGroup('separator', false));
|
||||
var header = {
|
||||
id: "commons-header",
|
||||
|
@ -410,6 +410,9 @@ Zotero.CollectionTreeView.prototype.notify = function(action, type, ids)
|
|||
// Groups can only be created during sync
|
||||
this.rememberSelection(savedSelection);
|
||||
break;
|
||||
|
||||
case 'bucket':
|
||||
this.reload();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,13 +20,20 @@
|
|||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
//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';
|
||||
|
||||
this.__defineGetter__('buckets', function () {
|
||||
if(!Zotero.Prefs.get("commons.enabled")) {
|
||||
return [];
|
||||
return false;
|
||||
}
|
||||
|
||||
var accessKey = Zotero.Prefs.get("commons.accessKey");
|
||||
|
@ -40,6 +47,190 @@ Zotero.Commons = new function() {
|
|||
}
|
||||
return buckets;
|
||||
});
|
||||
|
||||
function createBucket(bucketName) {
|
||||
var accessKey = Zotero.Prefs.get("commons.accessKey");
|
||||
var secretKey = Zotero.Prefs.get("commons.secretKey");
|
||||
|
||||
var req = this._createAuthenticatedRequest(
|
||||
"PUT", "/" + bucketName, {}, accessKey, secretKey
|
||||
);
|
||||
|
||||
req.onreadystatechange = function() {
|
||||
if(req.readyState == 4) {
|
||||
if(req.status < 400) {
|
||||
// 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) {
|
||||
alert("Bucket creation failed: authentication failed.");
|
||||
}
|
||||
else if(req.status == 409) {
|
||||
alert("Bucket creation failed: bucket name already taken.");
|
||||
}
|
||||
else if(req.status == 503) {
|
||||
alert("Bucket creation failed: server unavailable.");
|
||||
}
|
||||
else {
|
||||
alert("Bucket creation failed: server error " + req.status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
req.send(null);
|
||||
}
|
||||
|
||||
function syncBucketList() {
|
||||
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) {
|
||||
alert("Bucket list sync failed: server unavailable.");
|
||||
}
|
||||
else {
|
||||
alert("Bucket list sync failed: server error " + req.status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
req.send(null);
|
||||
}
|
||||
|
||||
// remove bucketName from preference, and refresh left pane in Zotero
|
||||
function removeBucket(bucketName) {
|
||||
var prefBucketNames = Zotero.Prefs.get("commons.buckets").split(',');
|
||||
var newPrefBucketNames = [];
|
||||
for(var i = 0, len = prefBucketNames.length; i < len; i++) {
|
||||
if(bucketName != prefBucketNames[i]) {
|
||||
newPrefBucketNames.push(prefBucketNames[i]);
|
||||
}
|
||||
}
|
||||
newPrefBucketNames.sort();
|
||||
Zotero.Prefs.set("commons.buckets", newPrefBucketNames.join(','));
|
||||
Zotero.Notifier.trigger('add', 'bucket', true);
|
||||
}
|
||||
|
||||
function _createAuthenticatedRequest(method, resource, headers, accessKey, secretKey) {
|
||||
var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Components.interfaces.nsIXMLHttpRequest);
|
||||
req.open(method, Zotero.Commons.apiUrl + resource, true);
|
||||
|
||||
var d = new Date();
|
||||
headers["Date"] = d.toUTCString();
|
||||
|
||||
var signatureData = method + '\n' +
|
||||
((headers['Content-MD5']) ? headers['Content-MD5'] : '') + '\n' +
|
||||
((headers['Content-Type']) ? headers['Content-Type'] : '') + '\n' +
|
||||
((headers['Date']) ? headers['Date'] : '') + '\n';
|
||||
|
||||
// add x-amz- headers in alphabetic order
|
||||
var amz = [];
|
||||
for(header in headers) {
|
||||
if(header.indexOf("x-amz-") == 0) {
|
||||
amz.push(header + ":" + headers[header] + '\n');
|
||||
}
|
||||
}
|
||||
signatureData += amz.sort().join('');
|
||||
|
||||
signatureData += resource;
|
||||
var signature = Zotero.Commons.SHA1.b64_hmac_sha1(secretKey, signatureData) + '=';
|
||||
headers["Authorization"] = "AWS " + accessKey + ":" + signature;
|
||||
//headers["Authorization"] = "LOW " + accessKey + ":" + secretKey;
|
||||
|
||||
for(var header in headers) {
|
||||
req.setRequestHeader(header, headers[header]);
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
function _createUnauthenticatedRequest(method, resource, headers) {
|
||||
var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Components.interfaces.nsIXMLHttpRequest);
|
||||
req.open(method, Zotero.Commons.apiUrl + resource, true);
|
||||
|
||||
for(var header in headers) {
|
||||
req.setRequestHeader(header, headers[header]);
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -54,8 +245,6 @@ Zotero.Commons.Bucket = function(name, accessKey, secretKey) {
|
|||
}
|
||||
|
||||
|
||||
Zotero.Commons.Bucket.prototype.apiUrl = 'http://s3.us.archive.org';
|
||||
|
||||
Zotero.Commons.Bucket.prototype.RDF_TRANSLATOR = {
|
||||
'label': 'Zotero RDF',
|
||||
'target': 'rdf',
|
||||
|
@ -92,11 +281,7 @@ Zotero.Commons.Bucket.prototype.getItems = function() {
|
|||
this._requestingItems = true;
|
||||
|
||||
// get a list of keys associated with this bucket
|
||||
var method = "GET";
|
||||
var resource = '/' + this.name + '/';
|
||||
var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Components.interfaces.nsIXMLHttpRequest);
|
||||
req.open(method, this.apiUrl + resource, true);
|
||||
var req = Zotero.Commons._createUnauthenticatedRequest("GET", '/' + this.name + '/', {});
|
||||
//req.channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE;
|
||||
|
||||
var self = this;
|
||||
|
@ -193,6 +378,7 @@ Zotero.Commons.Bucket.prototype._translateCallback = function(translation, succe
|
|||
data.bucket._zipDirectory(data.bucket, dir, dir, zw);
|
||||
|
||||
data.uploadFile = zipFile;
|
||||
data.mimetype = "application/zip";
|
||||
// add observer so _putKey is called on zip completion
|
||||
var observer = new Zotero.Commons.ZipWriterObserver(zw, data.bucket._putKey, data);
|
||||
zw.processQueue(observer, null);
|
||||
|
@ -205,30 +391,20 @@ Zotero.Commons.Bucket.prototype._translateCallback = function(translation, succe
|
|||
// Does the put call to IA, puting data.uploadFile into the bucket
|
||||
Zotero.Commons.Bucket.prototype._putKey = function(data) {
|
||||
var self = data.bucket;
|
||||
var method = "PUT";
|
||||
var mimeType = 'application/zip';
|
||||
var key = data.uploadFile.leafName;
|
||||
var method = "PUT";
|
||||
var resource = '/' + self.name + '/' + key;
|
||||
var content = self._readFileContents(data.uploadFile);
|
||||
|
||||
var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Components.interfaces.nsIXMLHttpRequest);
|
||||
req.open(method, self.apiUrl + resource, true);
|
||||
var headers = {
|
||||
"Content-Type": data.mimeType,
|
||||
"Content-Length": content.length,
|
||||
"x-amz-meta-creator": "Zotero Commons"
|
||||
};
|
||||
|
||||
var d = new Date();
|
||||
var headers = {};
|
||||
headers["Content-Type"] = mimeType;
|
||||
headers["Content-Length"] = content.length;
|
||||
headers["Date"] = d.toUTCString();
|
||||
headers["x-amz-meta-creator"] = "Zotero Commons";
|
||||
|
||||
for(var header in headers) {
|
||||
req.setRequestHeader(header, headers[header]);
|
||||
}
|
||||
|
||||
var signature = self._generateSignature(method, resource, headers, self._secretKey);
|
||||
req.setRequestHeader("Authorization", "AWS " + self._accessKey + ":" + signature);
|
||||
//req.setRequestHeader("Authorization", "LOW " + self._accessKey + ":" + self._secretKey);
|
||||
var req = Zotero.Commons._createAuthenticatedRequest(
|
||||
method, resource, headers, self._accessKey, self._secretKey
|
||||
);
|
||||
|
||||
req.onreadystatechange = function() {
|
||||
if (req.readyState == 4) {
|
||||
|
@ -301,26 +477,6 @@ Zotero.Commons.Bucket.prototype._zipDirectory = function(self, rootDir, dir, zip
|
|||
}
|
||||
}
|
||||
|
||||
Zotero.Commons.Bucket.prototype._generateSignature = function(method, resource, headers, secretKey) {
|
||||
var data = method + '\n' +
|
||||
((headers['Content-MD5']) ? headers['Content-MD5'] : '') + '\n' +
|
||||
((headers['Content-Type']) ? headers['Content-Type'] : '') + '\n' +
|
||||
((headers['Date']) ? headers['Date'] : '') + '\n';
|
||||
|
||||
// add x-amz- headers in alphabetic order
|
||||
var amz = [];
|
||||
for(header in headers) {
|
||||
if(header.indexOf("x-amz-") == 0) {
|
||||
amz.push(header + ":" + headers[header] + '\n');
|
||||
}
|
||||
}
|
||||
data += amz.sort().join('');
|
||||
|
||||
data += resource;
|
||||
|
||||
return Zotero.Commons.SHA1.b64_hmac_sha1(secretKey, data) + '=';
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Implements nsIRequestObserver
|
||||
|
|
Loading…
Reference in a new issue