Add Zotero Commons feature (integration between Zotero and Internet Archive).

- dropping Zotero items into a bucket puts them in that IA bucket
- double clicking a bucket takes you to that IA bucket

In order to enable Zotero Commons:
1) Get an access key and secret key at http://www.archive.org/account/s3.php
2) Go to about:config
3) Search "commons" (no quotes)
4) Set "extensions.zotero.commons.enabled" to true
5) Enter your S3 access key into "extensions.zotero.commons.accessKey"
6) Enter your S3 secret key into "extensions.zotero.commons.secretKey"
7) Enter your buckets into "extensions.zotero.commons.buckets" as a comma separated list

Note: Steps 4-7 take effect in new windows
This commit is contained in:
Ben Parr 2009-08-13 10:56:47 +00:00
parent bb3c2ff55d
commit ac79b1d05f
9 changed files with 636 additions and 6 deletions

View file

@ -1983,8 +1983,17 @@ var ZoteroPane = new function()
ZoteroPane.loadURI(uri);
event.stopPropagation();
}
else if(itemGroup.ref.id == 'commons-header') {
ZoteroPane.loadURI(Zotero.Commons.uri);
event.stopPropagation();
}
return;
}
if (itemGroup.isBucket()) {
ZoteroPane.loadURI(itemGroup.ref.uri);
event.stopPropagation();
}
}
else if (tree.id == 'zotero-items-tree') {
var viewOnDoubleClick = Zotero.Prefs.get('viewOnDoubleClick');

View file

@ -224,6 +224,26 @@ Zotero.CollectionTreeView.prototype.refresh = function()
this._showItem(new Zotero.ItemGroup('share', share));
}
}
var buckets = Zotero.Commons.buckets;
if(buckets.length) {
this._showItem(new Zotero.ItemGroup('separator', false));
var header = {
id: "commons-header",
label: "Commons", // TODO: localize
expand: function (buckets) {
if (!buckets) {
var buckets = Zotero.Commons.buckets;
}
for(var i = 0, len = buckets.length; i < len; i++) {
self._showItem(new Zotero.ItemGroup('bucket', buckets[i]), 1);
}
}
};
this._showItem(new Zotero.ItemGroup('header', header), null, null, true);
header.expand(buckets);
}
this._refreshHashMap();
@ -473,6 +493,9 @@ Zotero.CollectionTreeView.prototype.getImageSrc = function(row, col)
if (source.ref.id == 'group-libraries-header') {
collectionType = 'groups';
}
else if (source.ref.id == 'commons-header') {
collectionType = 'commons';
}
break;
case 'group':
@ -485,7 +508,7 @@ Zotero.CollectionTreeView.prototype.getImageSrc = function(row, col)
Zotero.CollectionTreeView.prototype.isContainer = function(row)
{
var itemGroup = this._getItemAtRow(row);
return itemGroup.isLibrary(true) || itemGroup.isCollection() || itemGroup.isHeader();
return itemGroup.isLibrary(true) || itemGroup.isCollection() || itemGroup.isHeader() || itemGroup.isBucket();
}
Zotero.CollectionTreeView.prototype.isContainerOpen = function(row)
@ -505,6 +528,9 @@ Zotero.CollectionTreeView.prototype.isContainerEmpty = function(row)
if (itemGroup.isHeader()) {
return false;
}
if (itemGroup.isBucket()) {
return true;
}
if (itemGroup.isGroup()) {
return !itemGroup.ref.hasCollections();
}
@ -562,6 +588,8 @@ Zotero.CollectionTreeView.prototype.toggleOpenState = function(row)
if (itemGroup.type == 'header') {
itemGroup.ref.expand();
}
else if(itemGroup.type == 'bucket') {
}
else {
if (itemGroup.isLibrary()) {
count = itemGroup.ref.expand();
@ -999,6 +1027,11 @@ Zotero.CollectionTreeView.prototype.canDrop = function(row, orient, dragData)
}
if (dataType == 'zotero/item') {
if(itemGroup.isBucket()) {
return true;
}
var ids = data;
var items = Zotero.Items.get(ids);
var skip = true;
@ -1139,6 +1172,11 @@ Zotero.CollectionTreeView.prototype.drop = function(row, orient)
else {
var targetLibraryID = null;
}
if(itemGroup.isBucket()) {
itemGroup.ref.uploadItems(ids);
return;
}
Zotero.DB.beginTransaction();
@ -1472,6 +1510,11 @@ Zotero.ItemGroup.prototype.isShare = function()
return this.type == 'share';
}
Zotero.ItemGroup.prototype.isBucket = function()
{
return this.type == 'bucket';
}
Zotero.ItemGroup.prototype.isTrash = function()
{
return this.type == 'trash';
@ -1535,6 +1578,9 @@ Zotero.ItemGroup.prototype.getName = function()
case 'share':
return this.ref.name;
case 'bucket':
return this.ref.name;
case 'trash':
return Zotero.getString('pane.collections.trash');
@ -1556,6 +1602,9 @@ Zotero.ItemGroup.prototype.getChildItems = function()
// Fake results if this is a shared library
case 'share':
return this.ref.getAll();
case 'bucket':
return this.ref.getItems();
case 'header':
return [];
@ -1661,6 +1710,9 @@ Zotero.ItemGroup.prototype.getChildTags = function() {
// TODO: implement?
case 'share':
return false;
case 'bucket':
return false;
case 'header':
return false;
@ -1703,4 +1755,4 @@ Zotero.ItemGroup.prototype.isSearchMode = function() {
return true;
}
}
}
}

View file

@ -0,0 +1,557 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright (c) 2006 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://chnm.gmu.edu
Licensed under the Educational Community License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.opensource.org/licenses/ecl1.php
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
***** END LICENSE BLOCK *****
*/
Zotero.Commons = new function() {
this.uri = 'http://www.archive.org/';
this.__defineGetter__('buckets', function () {
if(!Zotero.Prefs.get("commons.enabled")) {
return [];
}
var accessKey = Zotero.Prefs.get("commons.accessKey");
var secretKey = Zotero.Prefs.get("commons.secretKey");
var buckets = [];
var bucketNames = Zotero.Prefs.get("commons.buckets").split(',');
for(var i = 0, len = bucketNames.length; i < len; i++) {
if(bucketNames[i]) {
buckets.push(new Zotero.Commons.Bucket(bucketNames[i], accessKey, secretKey));
}
}
return buckets;
});
}
// accessKey and secretKey are passed to allow easy implementation
// of using multiple accounts
Zotero.Commons.Bucket = function(name, accessKey, secretKey) {
this.name = name;
this._accessKey = accessKey;
this._secretKey = secretKey;
this._items = null;
this._requestingItems = false;
}
Zotero.Commons.Bucket.prototype.apiUrl = 'http://s3.us.archive.org';
Zotero.Commons.Bucket.prototype.RDF_TRANSLATOR = {
'label': 'Zotero RDF',
'target': 'rdf',
'translatorID': '14763d24-8ba0-45df-8f52-b8d1108e7ac9',
'displayOptions': {
'exportFileData': true,
'exportNotes': true
}
};
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";
// return an array of items currently stored in this bucket
Zotero.Commons.Bucket.prototype.getItems = function() {
/*if(this._items) {
return this._items;
}
// avoid multiple requests to IA
if(this._requestingItems) {
return [];
}
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);
//req.channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE;
var self = this;
req.onreadystatechange = function() {
if (req.readyState == 4) {
if(req.status < 400) {
// TODO this is completely wrong and needs to change
// Need to figure out IA/Zotero interaction first
self._items = [];
var contents = req.responseXML.getElementsByTagName("Contents");
for(var i = 0, len = contents.length; i < len; i++) {
var keyParts = contents[i].getElementsByTagName('Key')[0].textContent.split('.');
if(keyParts.length == 2 && keyParts[1] == 'zip') {
var key = keyParts[0];
var item = Zotero.Items.getByLibraryAndKey(null, key);
if(item) {
self._items.push(item);
}
}
}
Zotero.Notifier.trigger('refresh', 'bucket', 'all');
}
else {
alert("Request to get the keys of bucket " + self.name + " failed.");
}
self._requestingItems = false;
}
};
req.send(null);
*/
return [];
}
// upload zipped Zotero RDF output of items to this bucket
Zotero.Commons.Bucket.prototype.uploadItems = function(ids) {
var items = Zotero.Items.get(ids);
if (!items) {
return;
}
var tmpDir = Zotero.getTempDirectory();
// export individual items through the Zotero RDF translation
for(var i = 0, len = items.length; i < len; i++) {
var item = items[i];
if(item.isRegularItem()) {
// generate file location for the export output
var rdfExportPath = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
rdfExportPath.initWithFile(tmpDir);
rdfExportPath.append(item.key);
// initialize and run the translator for this item
var translation = new Zotero.Translate("export");
translation.setItems([item]);
translation.setTranslator(this.RDF_TRANSLATOR.translatorID);
translation.setDisplayOptions(this.RDF_TRANSLATOR.displayOptions);
translation.setHandler("done", this._translateCallback);
translation.setLocation(rdfExportPath);
// add some data to translator needed by _translateCallback
translation._bucketData = {bucket: this, items: [item]};
translation.translate(); // synchronous
}
}
}
// Zips the output of the translation and then calls _putKey
// Called after a translation is done.
Zotero.Commons.Bucket.prototype._translateCallback = function(translation, successful) {
if(!successful) {
alert("Commons.TranslatorManager: tranlation failed for " + translation);
}
var data = translation._bucketData;
try {
var dir = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
dir.initWithPath(translation.path);
// create zip file
var zipFile = Zotero.getTempDirectory();
zipFile.append(dir.leafName + '.zip');
var zw = Components.classes["@mozilla.org/zipwriter;1"]
.createInstance(Components.interfaces.nsIZipWriter);
zw.open(zipFile, 0x04 | 0x08 | 0x20); // open rw, create, truncate
data.bucket._zipDirectory(data.bucket, dir, dir, zw);
data.uploadFile = zipFile;
// add observer so _putKey is called on zip completion
var observer = new Zotero.Commons.ZipWriterObserver(zw, data.bucket._putKey, data);
zw.processQueue(observer, null);
}
catch (e) {
alert("Commons: Upload failed: " + e);
}
}
// 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 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 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);
req.onreadystatechange = function() {
if (req.readyState == 4) {
if(req.status < 400) {
for(var i = 0, len = data.items.length; i < len; i++) {
var url1 = Zotero.URI.getItemURI(data.items[i]);
var predicate = self.relationPredicate;
var url2 = self.getKeyUrl(self.name, key);
if (Zotero.Relations.getByURIs(url1, predicate, url2).length
|| Zotero.Relations.getByURIs(url2, predicate, url1).length) {
Zotero.debug(url1 + " and " + url2 + " are already linked");
continue;
}
Zotero.Relations.add(null, url1, predicate, url2);
}
Zotero.debug("Commons: " + key + " was uploaded successfully.");
}
else if(req.status == 403) {
alert("Failed to upload " + key + " to IA: authentication failed.");
}
else if(req.status == 503) {
alert("Failed to upload " + key + " to IA: server unavailable.");
}
else {
alert("Failed to upload " + key + " to IA.");
}
}
};
req.sendAsBinary(content);
}
// return the content of an input nsiFile
Zotero.Commons.Bucket.prototype._readFileContents = function(bfile) {
var istream = Components.classes["@mozilla.org/network/file-input-stream;1"]
.createInstance(Components.interfaces.nsIFileInputStream);
istream.init(bfile, -1, -1, false);
var bstream = Components.classes["@mozilla.org/binaryinputstream;1"]
.createInstance(Components.interfaces.nsIBinaryInputStream);
bstream.setInputStream(istream);
return bstream.readBytes(bstream.available());
}
// Recursively add files and directories to zipWriter
Zotero.Commons.Bucket.prototype._zipDirectory = function(self, rootDir, dir, zipWriter) {
dir = dir.directoryEntries;
while(dir.hasMoreElements()) {
var file = dir.getNext();
file.QueryInterface(Components.interfaces.nsILocalFile);
var fileName = file.getRelativeDescriptor(rootDir);
if(fileName.indexOf('.') == 0) {
Zotero.debug('Skipping file ' + fileName);
continue;
}
// addEntryFile works for both files and directories
zipWriter.addEntryFile(
fileName,
Components.interfaces.nsIZipWriter.COMPRESSION_DEFAULT,
file,
true
);
if(file.isDirectory()) {
self._zipDirectory(self, rootDir, file, zipWriter);
continue;
}
}
}
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
Zotero.Commons.ZipWriterObserver = function (zipWriter, callback, callbackData) {
this._zipWriter = zipWriter;
this._callback = callback;
this._callbackData = callbackData;
}
Zotero.Commons.ZipWriterObserver.prototype = {
onStartRequest: function () {},
onStopRequest: function(req, context, status) {
this._zipWriter.close();
this._callback(this._callbackData);
}
}
/*
* A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
* in FIPS PUB 180-1
* Version 2.1a Copyright Paul Johnston 2000 - 2002.
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
* Distributed under the BSD License
* See http://pajhome.org.uk/crypt/md5 for details.
*/
Zotero.Commons.SHA1 = new function() {
// added by Ben Parr to expose function for Zotero
this.hex_sha1 = hex_sha1;
this.b64_sha1 = b64_sha1;
this.str_sha1 = str_sha1;
this.hex_hmac_sha1 = hex_hmac_sha1;
this.b64_hmac_sha1 = b64_hmac_sha1;
this.str_hmac_sha1 = str_hmac_sha1;
/*
* Configurable variables. You may need to tweak these to be compatible with
* the server-side, but the defaults work in most cases.
*/
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
/*
* These are the functions you'll usually want to call
* They take string arguments and return either hex or base-64 encoded strings
*/
function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));}
function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));}
function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));}
function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));}
function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));}
function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));}
/*
* Perform a simple self-test to see if the VM is working
*/
function sha1_vm_test()
{
return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d";
}
/*
* Calculate the SHA-1 of an array of big-endian words, and a bit length
*/
function core_sha1(x, len)
{
/* append padding */
x[len >> 5] |= 0x80 << (24 - len % 32);
x[((len + 64 >> 9) << 4) + 15] = len;
var w = Array(80);
var a = 1732584193;
var b = -271733879;
var c = -1732584194;
var d = 271733878;
var e = -1009589776;
for(var i = 0; i < x.length; i += 16)
{
var olda = a;
var oldb = b;
var oldc = c;
var oldd = d;
var olde = e;
for(var j = 0; j < 80; j++)
{
if(j < 16) w[j] = x[i + j];
else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
safe_add(safe_add(e, w[j]), sha1_kt(j)));
e = d;
d = c;
c = rol(b, 30);
b = a;
a = t;
}
a = safe_add(a, olda);
b = safe_add(b, oldb);
c = safe_add(c, oldc);
d = safe_add(d, oldd);
e = safe_add(e, olde);
}
return Array(a, b, c, d, e);
}
/*
* Perform the appropriate triplet combination function for the current
* iteration
*/
function sha1_ft(t, b, c, d)
{
if(t < 20) return (b & c) | ((~b) & d);
if(t < 40) return b ^ c ^ d;
if(t < 60) return (b & c) | (b & d) | (c & d);
return b ^ c ^ d;
}
/*
* Determine the appropriate additive constant for the current iteration
*/
function sha1_kt(t)
{
return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
(t < 60) ? -1894007588 : -899497514;
}
/*
* Calculate the HMAC-SHA1 of a key and some data
*/
function core_hmac_sha1(key, data)
{
var bkey = str2binb(key);
if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz);
var ipad = Array(16), opad = Array(16);
for(var i = 0; i < 16; i++)
{
ipad[i] = bkey[i] ^ 0x36363636;
opad[i] = bkey[i] ^ 0x5C5C5C5C;
}
var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz);
return core_sha1(opad.concat(hash), 512 + 160);
}
/*
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
* to work around bugs in some JS interpreters.
*/
function safe_add(x, y)
{
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
/*
* Bitwise rotate a 32-bit number to the left.
*/
function rol(num, cnt)
{
return (num << cnt) | (num >>> (32 - cnt));
}
/*
* Convert an 8-bit or 16-bit string to an array of big-endian words
* In 8-bit function, characters >255 have their hi-byte silently ignored.
*/
function str2binb(str)
{
var bin = Array();
var mask = (1 << chrsz) - 1;
for(var i = 0; i < str.length * chrsz; i += chrsz)
bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32);
return bin;
}
/*
* Convert an array of big-endian words to a string
*/
function binb2str(bin)
{
var str = "";
var mask = (1 << chrsz) - 1;
for(var i = 0; i < bin.length * 32; i += chrsz)
str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask);
return str;
}
/*
* Convert an array of big-endian words to a hex string.
*/
function binb2hex(binarray)
{
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var str = "";
for(var i = 0; i < binarray.length * 4; i++)
{
str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8)) & 0xF);
}
return str;
}
/*
* Convert an array of big-endian words to a base-64 string
*/
function binb2b64(binarray)
{
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var str = "";
for(var i = 0; i < binarray.length * 4; i += 3)
{
var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16)
| (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 )
| ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF);
for(var j = 0; j < 4; j++)
{
if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
}
}
return str;
}
}

View file

@ -49,7 +49,7 @@ Zotero.ItemTreeView = function(itemGroup, sourcesOnly)
this._dataItems = [];
this.rowCount = 0;
this._unregisterID = Zotero.Notifier.registerObserver(this, ['item', 'collection-item', 'share-items']);
this._unregisterID = Zotero.Notifier.registerObserver(this, ['item', 'collection-item', 'share-items', 'bucket']);
}
@ -281,6 +281,11 @@ Zotero.ItemTreeView.prototype.notify = function(action, type, ids, extraData)
this.refresh();
}
}
else if (type == 'bucket') {
if (itemGroup.isBucket()) {
this.refresh();
}
}
else if (savedSelection.length == 1 && savedSelection[0] == ids[0]) {
this.selection.clearSelection();
this.rememberSelection(savedSelection);
@ -2389,4 +2394,4 @@ Zotero.ItemTreeView.TreeRow.prototype.numAttachments = function(includeTrashed)
return this.ref.numAttachments(includeTrashed);
else
return 0;
}
}

View file

@ -25,7 +25,7 @@ Zotero.Notifier = new function(){
var _disabled = false;
var _types = [
'collection', 'creator', 'search', 'share', 'share-items', 'item',
'collection-item', 'item-tag', 'tag', 'group'
'collection-item', 'item-tag', 'tag', 'group', 'bucket'
];
var _inTransaction;
var _locked = false;

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 923 B

View file

@ -19,6 +19,7 @@ var xpcomFiles = [
'annotate',
'attachments',
'collectionTreeView',
'commons',
'csl',
'dataServer',
'data_access',

View file

@ -100,6 +100,12 @@ pref("extensions.zotero.integration.realWindow", false);
// Zeroconf
pref("extensions.zotero.zeroconf.server.enabled", false);
// Zotero Commons
pref("extensions.zotero.commons.enabled", false);
pref("extensions.zotero.commons.buckets", '');
pref("extensions.zotero.commons.accessKey", '');
pref("extensions.zotero.commons.secretKey", '');
// Annotation settings
pref("extensions.zotero.annotations.warnOnClose", true);
@ -123,4 +129,4 @@ pref("extensions.zotero.proxies.transparent", true);
pref("extensions.zotero.purge.creators", false);
pref("extensions.zotero.purge.fulltext", false);
pref("extensions.zotero.purge.items", false);
pref("extensions.zotero.purge.tags", false);
pref("extensions.zotero.purge.tags", false);