www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

commit 453fed88bd175d3b1b0cefe7f349cd92fcdb5dcb
parent a678cfa5b98828a3a6ef56c7e7716cd575e2cd5c
Author: Dan Stillman <dstillman@zotero.org>
Date:   Tue, 27 Apr 2010 08:03:08 +0000

Zotero Commons updates:

- Store one item per IA bucket, with attachments stored as objects
- Use proper mediatype field based on Zotero item type
- Commons list is now pulled dynamically based on RDF stored at IA, without need for corresponding local item (which may have been deleted, etc.)
- Once available, OCRed PDFs can be pulled down by right-clicking on Commons and selecting Refresh
- Downloaded OCRed PDFs are now named the same as the existing attachment, with "(OCR)" appended
- The relations table is used to link downloaded OCRed PDFs to the IA file, so the downloaded file can be renamed without triggering another download
- The Commons view is marked for automatic refresh after an item is uploaded
- Added some progress notifications, though more are probably needed
- Other things

Also:

- Added Zotero.File.getBinaryContents(file)
- Erase an item's relations when the item is deleted, and purge orphaned ones
- Zotero.URI.eraseByPathPrefix(prefix) no longer prepends 'http://zotero.org' (which has been moved to Zotero.URI.defaultPrefix)
- New function Zotero.URI.eraseByURI(prefix)

Known Issues:

- Slow (some IA changes should be able to speed it up)
- Identifier format is likely temporary
- Sometimes it stops during setTimeout() calls for no apparent reason whatsoever
- Didn't test items with multiple attachments
- Not sure if Commons view will auto-refresh if you switch to it before the upload is done
- IA translator not yet updated
- Deleting items not supported by IA
- Date Added/Date Modified don't show up properly in Zotero for Commons items



Diffstat:
Mchrome/content/zotero/overlay.js | 102++++++++++++++++++++++++++++++++++++-------------------------------------------
Mchrome/content/zotero/overlay.xul | 5+----
Mchrome/content/zotero/xpcom/collectionTreeView.js | 79+++++++++++++++++++++++++++++++++----------------------------------------------
Mchrome/content/zotero/xpcom/commons.js | 1471+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mchrome/content/zotero/xpcom/data/group.js | 2+-
Mchrome/content/zotero/xpcom/data/item.js | 9++++++---
Mchrome/content/zotero/xpcom/data/relations.js | 35++++++++++++++++++++++++++++++-----
Mchrome/content/zotero/xpcom/file.js | 15+++++++++++++++
Mchrome/content/zotero/xpcom/itemTreeView.js | 21++++++++++++++-------
Mchrome/content/zotero/xpcom/uri.js | 13+++++++++----
Mchrome/content/zotero/xpcom/zotero.js | 4++++
Mdefaults/preferences/zotero.js | 1-
12 files changed, 1069 insertions(+), 688 deletions(-)

diff --git a/chrome/content/zotero/overlay.js b/chrome/content/zotero/overlay.js @@ -1343,7 +1343,7 @@ var ZoteroPane = new function() } var itemGroup = this.itemsView._itemGroup; - if (!itemGroup.isTrash() && !this.canEdit()) { + if (!itemGroup.isTrash() && !itemGroup.isCommons() && !this.canEdit()) { this.displayCannotEditLibraryMessage(); return; } @@ -1383,6 +1383,9 @@ var ZoteroPane = new function() else if (itemGroup.isShare()) { return; } + else if (itemGroup.isCommons()) { + var prompt = toDelete; + } // Do nothing in trash view if any non-deleted items are selected else if (itemGroup.isTrash()) { var start = {}; @@ -1462,47 +1465,25 @@ var ZoteroPane = new function() } } - this.createCommonsBucket = 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.refreshCommonsBucket = function() { + this.refreshCommons = function() { if (this.collectionsView && this.collectionsView.selection - && this.collectionsView.selection.count > 0 + && this.collectionsView.selection.count == 1 && this.collectionsView.selection.currentIndex != -1) { - var bucket = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); - if (bucket && bucket.isBucket()) { - this.itemsView._itemGroup.ref._items = null; - this.itemsView.refresh(); - } - } - } - - this.removeCommonsBucket = 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()); + var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); + if (itemGroup && itemGroup.isCommons()) { + var self = this; + Zotero.Commons.syncBucketList(function () { + self.itemsView.refresh(); + self.itemsView.sort(); + + // On a manual refresh, also check for new OCRed files + Zotero.Commons.syncFiles(); + }); } } } - function editSelectedCollection() { if (!this.canEdit()) { @@ -1802,10 +1783,7 @@ var ZoteroPane = new function() exportFile: 9, loadReport: 10, emptyTrash: 11, - createCommonsBucket: 12, - syncBucketList: 13, - removeCommonsBucket: 14, - refreshCommonsBucket: 15 + refreshCommons: 12 }; var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); @@ -1873,14 +1851,8 @@ var ZoteroPane = new function() else if (itemGroup.isTrash()) { show = [m.emptyTrash]; } - // Header - else if (itemGroup.isHeader()) { - if (itemGroup.ref.id == 'commons-header') { - show = [m.createCommonsBucket, m.syncBucketList]; - } - } - else if (itemGroup.isBucket()) { - show = [m.removeCommonsBucket, m.refreshCommonsBucket]; + else if (itemGroup.isCommons()) { + show = [m.refreshCommons]; } // Group else if (itemGroup.isGroup()) { @@ -2261,7 +2233,7 @@ var ZoteroPane = new function() return; } - if (tree.id == 'zotero-collections-tree') { + if (tree.id == 'zotero-collections-tree') { // Ignore triple clicks for collections if (event.detail != 2) { return; @@ -2295,15 +2267,12 @@ 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); + if (itemGroup.isCommons()) { + // TODO: take to a search of the user's buckets? + //ZoteroPane.loadURI(itemGroup.ref.uri); event.stopPropagation(); } } @@ -2325,6 +2294,18 @@ var ZoteroPane = new function() var item = ZoteroPane.getSelectedItems()[0]; if (item) { if (item.isRegularItem()) { + // Double-click on Commons item should load IA page + var itemGroup = ZoteroPane.collectionsView._getItemAtRow( + ZoteroPane.collectionsView.selection.currentIndex + ); + + if (itemGroup.isCommons()) { + var bucket = Zotero.Commons.getBucketFromItem(item); + ZoteroPane.loadURI(bucket.uri); + event.stopPropagation(); + return; + } + if (!viewOnDoubleClick) { return; } @@ -2879,6 +2860,8 @@ var ZoteroPane = new function() return this.addItemFromPage(itemType, saveSnapshot, row); } + var self = this; + Zotero.MIME.getMIMETypeFromURL(url, function (mimeType, hasNativeHandler) { // If native type, save using a hidden browser if (hasNativeHandler) { @@ -2943,7 +2926,16 @@ var ZoteroPane = new function() var collectionID = false; } - Zotero.Attachments.importFromURL(url, false, false, false, collectionID, null, libraryID); + var attachmentItem = Zotero.Attachments.importFromURL(url, false, false, false, collectionID, mimeType, libraryID); + + // importFromURL() doesn't trigger the notifier until + // after download is complete + // + // TODO: add a callback to importFromURL() + setTimeout(function () { + self.selectItem(attachmentItem.id); + }, 1001); + return; } } diff --git a/chrome/content/zotero/overlay.xul b/chrome/content/zotero/overlay.xul @@ -107,10 +107,7 @@ <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.createCommonsBucket();"/><!--TODO localize --> - <menuitem label="Sync Bucket List with IA" oncommand="Zotero.Commons.syncBucketList();"/><!--TODO localize --> - <menuitem label="Remove Bucket from List" oncommand="ZoteroPane.removeCommonsBucket();"/><!--TODO localize --> - <menuitem label="Refresh Bucket" oncommand="ZoteroPane.refreshCommonsBucket();"/><!--TODO localize --> + <menuitem label="Refresh" oncommand="ZoteroPane.refreshCommons();"/><!--TODO localize --> </popup> <popup id="zotero-itemmenu" onpopupshowing="ZoteroPane.buildItemContextMenu();"> <menuitem label="&zotero.items.menu.showInLibrary;" oncommand="ZoteroPane.selectItem(this.parentNode.getAttribute('itemID'), true)"/> diff --git a/chrome/content/zotero/xpcom/collectionTreeView.js b/chrome/content/zotero/xpcom/collectionTreeView.js @@ -39,7 +39,7 @@ Zotero.CollectionTreeView = function() this._treebox = null; this.itemToSelect = null; this._highlightedRows = {}; - this._unregisterID = Zotero.Notifier.registerObserver(this, ['collection', 'search', 'share', 'group', 'bucket']); + this._unregisterID = Zotero.Notifier.registerObserver(this, ['collection', 'search', 'share', 'group', 'commons']); this.showDuplicates = false; } @@ -194,7 +194,7 @@ Zotero.CollectionTreeView.prototype.refresh = function() var groups = Zotero.Groups.getAll(); if (groups.length) { - this._showItem(new Zotero.ItemGroup('separator', false)); + this._showItem(new Zotero.ItemGroup('separator')); var header = { id: "group-libraries-header", label: "Group Libraries", // TODO: localize @@ -234,30 +234,15 @@ Zotero.CollectionTreeView.prototype.refresh = function() var shares = Zotero.Zeroconf.instances; if (shares.length) { - this._showItem(new Zotero.ItemGroup('separator', false)); + this._showItem(new Zotero.ItemGroup('separator')); for each(var share in shares) { this._showItem(new Zotero.ItemGroup('share', share)); } } - var buckets = Zotero.Commons.buckets; - if(buckets) { - 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); + if (Zotero.Commons.enabled) { + this._showItem(new Zotero.ItemGroup('separator')); + this._showItem(new Zotero.ItemGroup('commons'), null, null, true); } this._refreshHashMap(); @@ -278,7 +263,11 @@ Zotero.CollectionTreeView.prototype.reload = function() for (var i=0; i<this.rowCount; i++) { if (this.isContainer(i) && this.isContainerOpen(i)) { - openCollections.push(this._getItemAtRow(i).ref.id); + var itemGroup = this._getItemAtRow(i); + if (!itemGroup.isCollection()) { + continue; + } + openCollections.push(itemGroup.ref.id); } } @@ -387,7 +376,7 @@ Zotero.CollectionTreeView.prototype.notify = function(action, type, ids) this.rememberSelection(savedSelection); } else if (action == 'modify' || action == 'refresh') { - if (type != 'bucket') { + if (type != 'commons') { this.reload(); } this.rememberSelection(savedSelection); @@ -433,7 +422,7 @@ Zotero.CollectionTreeView.prototype.notify = function(action, type, ids) this.rememberSelection(savedSelection); break; - case 'bucket': + case 'commons': this.reload(); } } @@ -522,14 +511,15 @@ 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': collectionType = 'library'; break; + + case 'commons': + collectionType = 'commons'; + break; } return "chrome://zotero/skin/treesource-" + collectionType + ".png"; } @@ -537,7 +527,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() || itemGroup.isBucket(); + return itemGroup.isLibrary(true) || itemGroup.isCollection() || itemGroup.isHeader() || itemGroup.isCommons(); } Zotero.CollectionTreeView.prototype.isContainerOpen = function(row) @@ -557,9 +547,6 @@ Zotero.CollectionTreeView.prototype.isContainerEmpty = function(row) if (itemGroup.isHeader()) { return false; } - if (itemGroup.isBucket()) { - return true; - } if (itemGroup.isGroup()) { return !itemGroup.ref.hasCollections(); } @@ -617,8 +604,6 @@ 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(); @@ -1063,12 +1048,14 @@ Zotero.CollectionTreeView.prototype.canDrop = function(row, orient, dragData) { var itemGroup = this._getItemAtRow(row); //the collection we are dragging over - if (!itemGroup.editable) { + if (!itemGroup.editable + // Commons can be dropped on but not edited + && !itemGroup.isCommons()) { return false; } if (dataType == 'zotero/item') { - if(itemGroup.isBucket()) { + if(itemGroup.isCommons()) { return true; } @@ -1236,8 +1223,8 @@ Zotero.CollectionTreeView.prototype.drop = function(row, orient) var targetLibraryID = null; } - if(itemGroup.isBucket()) { - itemGroup.ref.uploadItems(ids); + if(itemGroup.isCommons()) { + Zotero.Commons.uploadItems(ids); return; } @@ -1638,9 +1625,9 @@ Zotero.ItemGroup.prototype.isShare = function() return this.type == 'share'; } -Zotero.ItemGroup.prototype.isBucket = function() +Zotero.ItemGroup.prototype.isCommons = function() { - return this.type == 'bucket'; + return this.type == 'commons'; } Zotero.ItemGroup.prototype.isTrash = function() @@ -1667,7 +1654,7 @@ Zotero.ItemGroup.prototype.isWithinGroup = function () { } Zotero.ItemGroup.prototype.__defineGetter__('editable', function () { - if (this.isTrash() || this.isShare()) { + if (this.isTrash() || this.isShare() || this.isCommons()) { return false; } if (!this.isWithinGroup()) { @@ -1726,9 +1713,6 @@ 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'); @@ -1739,6 +1723,9 @@ Zotero.ItemGroup.prototype.getName = function() case 'header': return this.ref.label; + case 'commons': + return "Commons"; + default: return ""; } @@ -1751,8 +1738,8 @@ Zotero.ItemGroup.prototype.getChildItems = function() case 'share': return this.ref.getAll(); - case 'bucket': - return this.ref.getItems(); + case 'commons': + return Zotero.Commons.getItems(); case 'header': return []; @@ -1865,7 +1852,7 @@ Zotero.ItemGroup.prototype.getChildTags = function() { case 'share': return false; - case 'bucket': + case 'commons': return false; case 'header': diff --git a/chrome/content/zotero/xpcom/commons.js b/chrome/content/zotero/xpcom/commons.js @@ -27,196 +27,515 @@ Zotero.Commons = new function() { this.uri = 'http://www.archive.org/'; this.apiUrl = 'http://s3.us.archive.org'; - - this.__defineGetter__('buckets', function () { - if(!Zotero.Prefs.get("commons.enabled")) { - return false; - } - - 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; + this.postCreateBucketDelay = 2000; + + this.__defineGetter__('enabled', function () { + return Zotero.Prefs.get("commons.enabled"); }); - - this.createBucket = function (bucketName) { - var accessKey = Zotero.Prefs.get("commons.accessKey"); - var secretKey = Zotero.Prefs.get("commons.secretKey"); - + + this.__defineGetter__('userIdentifier', function () { + return Zotero.Prefs.get("commons.accessKey"); + }); + + this.__defineGetter__('accessKey', function () { + return Zotero.Prefs.get("commons.accessKey"); + }); + + this.__defineGetter__('secretKey', function () { + return Zotero.Prefs.get("commons.secretKey"); + }); + + this.RDF_TRANSLATOR = { + 'label': 'Zotero RDF', + 'target': 'rdf', + 'translatorID': '14763d24-8ba0-45df-8f52-b8d1108e7ac9', + 'displayOptions': { + 'exportFileData': true, + 'exportNotes': true + } + }; + + this.RDF_IMPORT_TRANSLATOR = { + 'translatorID': '5e3ad958-ac79-463d-812b-a86a9235c28f', + } + + this.ERROR_BUCKET_EXISTS = 1; + + this.refreshNeeded = true; + + var _buckets = {}; + var _bucketsLoading = false; + var _requestingItems = false; + + + this.createBucket = function (item, onBucketQueued, onBucketCreated, waitForCreation) { var headers = { "x-archive-auto-make-bucket":"1", "x-archive-meta01-collection":"scholarworkspaces", "x-archive-meta02-collection":"zoterocommons", - "x-archive-meta-mediatype":"texts", "x-archive-meta-sponsor":"Andrew W. Mellon Foundation", "x-archive-meta01-language":"eng" }; - - var callback = function (req) { + + var itemTitle = item.getDisplayTitle(); + if (itemTitle) { + headers["x-archive-meta-title"] = itemTitle; + } + + var itemLanguage = item.getField('language'); + // TODO: use proper language code? + if (itemLanguage) { + headers["x-archive-meta01-language"] = itemLanguage; + } + + var itemType = Zotero.ItemTypes.getName(item.itemTypeID); + switch (itemType) { + case 'artwork': + case 'map': + headers["x-archive-meta-mediatype"] = "image"; + break; + + case 'radioBroadcast': + case 'audioRecording': + headers["x-archive-meta-mediatype"] = "audio"; + break; + + case 'tvBroadcast': + case 'videoRecording': + headers["x-archive-meta-mediatype"] = "movies"; + break; + + default: + headers["x-archive-meta-mediatype"] = "texts"; + } + + var requestCallback = function (req, id, tries) { + Zotero.debug(req.status); + 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 bucket = new Zotero.Commons.Bucket(id); + + if (waitForCreation) { + Zotero.debug('Waiting for bucket creation'); + + + Zotero.debug('-------'); + Zotero.debug(setTimeout); + + setTimeout(function () { + var tries = 15; + bucket.exists(function (found) { + if (found == 1) { + onBucketCreated(bucket); + } + else if (!found) { + alert("Commons: Bucket " + bucket.name + " not found after creation"); + } + else { + alert("Commons: Error checking for bucket " + bucket.name + " after creation"); + } + }, tries); + }, Zotero.Commons.postCreateBucketDelay); + + if (onBucketQueued) { + onBucketQueued(); + } + } + else { + if (onBucketQueued) { + onBucketQueued(); + } + if (onBucketCreated) { + onBucketCreated(bucket); + } } } else { - Zotero.debug(req.status); Zotero.debug(req.responseText); - 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."); + // DEBUG: What is this for? + if (req.status == 409) { + tries++; + // Max tries + if (tries > 3) { + alert("Upload failed"); + return; + } + + id = Zotero.Commons.getNewBucketName(); + Zotero.Commons.createAuthenticatedRequest( + "PUT", "/" + id, headers, this.accessKey, this.secretKey, + function (req) { + requestCallback(req, id, tries); + } + ); + } else { - alert("Bucket creation failed: server error " + req.status); + Zotero.debug(req.status); + Zotero.debug(req.responseText); + + alert("Upload failed"); } } - } + }; - var req = this.createAuthenticatedRequest( - "PUT", "/" + bucketName, headers, accessKey, secretKey, callback + var id = Zotero.Commons.getNewBucketName(); + Zotero.Commons.createAuthenticatedRequest( + "PUT", "/" + id, headers, this.accessKey, this.secretKey, + function (req) { + requestCallback(req, id, 1); + } ); } - - this.syncBucketList = function () { + + + this.syncBucketList = function (callback) { var accessKey = Zotero.Prefs.get("commons.accessKey"); var secretKey = Zotero.Prefs.get("commons.secretKey"); - - // get list of buckets from IA - var callback = function (req) { + + if (_bucketsLoading) { + Zotero.debug("Already loading buckets"); + return; + } + + _bucketsLoading = true; + + var syncCallback = 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."); + alert("Unable to retrieve items list from the Internet Archive: server unavailable."); } else { - alert("Bucket list sync failed: server error " + req.status); + alert("Unable to retrieve items list from the Internet Archive: server error " + req.status); } + _bucketsLoading = false; + 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 currentBuckets = []; + var IABuckets = []; - 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; + for (var name in _buckets) { + currentBuckets.push(name); } + currentBuckets.sort(); + + Zotero.debug('=========='); + Zotero.debug("CURRENT BUCKETS"); + Zotero.debug(currentBuckets); + - var newPrefBucketNames = []; - var iaBucketNames = []; var buckets = req.responseXML.getElementsByTagName("Bucket"); - for(var i = 0, len = buckets.length; i < len; i++) { + 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); - } + IABuckets.push(bucketName); } - iaBucketNames.sort(); + IABuckets.sort(); - // newPrefBucketNames currently contains intersection - // of prefBucketNames and iaBucketNames - var askToAddBuckets = zu.arrayDiff(prefBucketNames, iaBucketNames); - var askToRemoveBuckets = zu.arrayDiff(iaBucketNames, prefBucketNames); + Zotero.debug("IA BUCKETS"); + Zotero.debug(IABuckets); - // 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; - } + var addBuckets = Zotero.Utilities.prototype.arrayDiff(IABuckets, currentBuckets); + var removeBuckets = Zotero.Utilities.prototype.arrayDiff(currentBuckets, IABuckets); + + Zotero.debug("ADD"); + Zotero.debug(addBuckets); + Zotero.debug("REMOVE"); + Zotero.debug(removeBuckets); + + for each(var name in removeBuckets) { + delete _buckets[name]; } - // 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]); - } + for each(var name in addBuckets) { + _buckets[name] = new Zotero.Commons.Bucket(name); } - 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."); + //if(prefChanged) { + //Zotero.Notifier.trigger('add', 'bucket', true); + //} + + _bucketsLoading = false; + + if (callback) { + callback(); } - } + }; var req = this.createAuthenticatedRequest( - "GET", "/", {}, accessKey, secretKey, callback + "GET", "/", {}, accessKey, secretKey, syncCallback, null, false, true ); } - // remove bucketName from preference, and refresh left pane in Zotero - this.removeBucket = function (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]); + this.getNewBucketName = function () { + return "zc-" + this.accessKey + "-" + Zotero.ID.getBigInt(9999999999); + } + + + this.getBucketFromItem = function (item) { + return this.getBucketFromItemID(item.id); + } + + + this.getBucketFromItemID = function (itemID) { + for each(var bucket in _buckets) { + if (bucket.item.id == itemID) { + return bucket; + } + } + return false; + } + + + /** + * Return an array of items belonging to this user + */ + this.getItems = function() { + Zotero.debug('========'); + Zotero.debug('calling getItems()'); + + if (!this.refreshNeeded) { + Zotero.debug("Commons: Buckets already loaded. Returing existing item set"); + return _getItemsFromBuckets(); + } + + if (_requestingItems) { + Zotero.debug("Commons: Already requesting items in Zotero.Commons.getItem()", 2); + return; + } + + _requestingItems = true; + + // First update the list of buckets + var req = this.syncBucketList(function () { + Zotero.Commons.refreshNeeded = false; + Zotero.Notifier.trigger('refresh', 'commons', []); + _requestingItems = false; + }); + + // Browser offline + if (!req) { + _requestingItems = false; + } + + // List isn't yet available + return []; + } + + + this.syncFiles = function () { + if (Zotero.Commons.refreshNeeded) { + throw ("Buckets must be loaded before syncing files in Zotero.Commons.syncFiles()"); + } + + Zotero.debug("Getting updated files from all buckets"); + + var progressWin = null; + var icon = 'chrome://zotero/skin/treeitem-attachment-pdf.png'; + + for each(var bucket in _buckets) { + bucket.syncFiles(function (attachment) { + if (!progressWin) { + progressWin = new Zotero.ProgressWindow(); + progressWin.changeHeadline("Downloading OCRed PDFs"); // TODO: localize + } + progressWin.addLines([attachment.getField('title')], [icon]); + progressWin.show(); + progressWin.startCloseTimer(8000); + }); + } + } + + + this.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<len; i++) { + if (items[i].isRegularItem()) { + itemsToUpload = true; + break; } } - newPrefBucketNames.sort(); - Zotero.Prefs.set("commons.buckets", newPrefBucketNames.join(',')); - Zotero.Notifier.trigger('add', 'bucket', true); + + var pr = Components.classes["@mozilla.org/network/default-prompt;1"] + .getService(Components.interfaces.nsIPrompt); + + if (!itemsToUpload) { + Zotero.debug("No regular items to upload"); + pr.alert("", "Only items with bibliographic metadata can be added to the Zotero Commons."); + return; + } + + var buttonFlags = (pr.BUTTON_POS_0) * (pr.BUTTON_TITLE_IS_STRING) + + (pr.BUTTON_POS_1) * (pr.BUTTON_TITLE_CANCEL); + var index = pr.confirmEx( + "Zotero Commons Upload", + "By uploading items to Zotero Commons you agree to the terms of use at zotero.org and archive.org. " + + "Please make sure metadata for your item(s) is set properly." + + "\n\n " + + "Continue to upload items to the Internet Archive?", + buttonFlags, + "Upload", + null, null, null, {} + ); + + // if user chooses 'cancel', exit + if (index != 0) return; + + + var progressWin = new Zotero.ProgressWindow(); + + var tmpDir = Zotero.getTempDirectory(); + + // Create buckets one at a time, pulling items off a stack + var process = function (items) { + if (!items.length) { + Zotero.debug("Commons: No more items to upload"); + return; + } + + let item = items.shift(); + + // Skip notes and attachments + if (!item.isRegularItem()) { + process(items); + return; + } + + // TODO: check relations table to see if this item already has a bucket + + // TODO: localize + progressWin.changeHeadline("Uploading items to IA"); + progressWin.addLines([item.getField('title')], [item.getImageSrc()]); + progressWin.show(); + + Zotero.Commons.createBucket( + item, + // onBucketQueued + function () { + // Start next item while waiting for bucket creation + process(items); + }, + // onBucketCreated + function (bucket) { + // Link item to new bucket + var url1 = Zotero.URI.getItemURI(item); + var predicate = bucket.relationPredicate; + var url2 = bucket.uri; + Zotero.Relations.add(null, url1, predicate, url2); + + // + // Export item and attachments to RDF and files + // + + var key = Zotero.ID.getKey(); + + var outputDir = Zotero.getTempDirectory(); + // TEMP + //outputDir.append(bucket.name); + outputDir.append(key); + + var translation = new Zotero.Translate("export"); + translation.setItems([item]); + translation.setTranslator(Zotero.Commons.RDF_TRANSLATOR.translatorID); + translation.setDisplayOptions(Zotero.Commons.RDF_TRANSLATOR.displayOptions); + translation.setHandler("done", function (translation, success) { + if (!success) { + alert("Commons: Translation failed for " + translation); + return; + } + + try { + // Upload RDF file + var rdfFile = outputDir.clone(); + // TEMP + //rdfFile.append(bucket.name + ".rdf"); + rdfFile.append(key + ".rdf"); + rdfFile.moveTo(null, bucket.name + ".rdf"); + bucket.putFile(rdfFile, "application/rdf+xml", function (uri) { + // TEMP + rdfFile.moveTo(null, key + ".rdf"); + + // Then create ZIP file from item + var zipFile = Zotero.getTempDirectory(); + // TEMP + //zipFile.append(outputDir.leafName + '.zip'); + zipFile.append(key + '.zip'); + + var zw = Components.classes["@mozilla.org/zipwriter;1"] + .createInstance(Components.interfaces.nsIZipWriter); + zw.open(zipFile, 0x04 | 0x08 | 0x20); // open rw, create, truncate + + bucket.zipDirectory(outputDir, outputDir, zw); + + // Upload file after zipping + var observer = new Zotero.Commons.ZipWriterObserver(zw, function () { + bucket.putFile(zipFile, "application/zip", function (uri) { + // TODO: localize + Zotero.debug('-=-=-=-=-='); + Zotero.debug(items.length); + if (!items.length) { + progressWin.startCloseTimer(5000); + } + + Zotero.Commons.refreshNeeded = true; + }); + }); + zw.processQueue(observer, null); + }); + } + catch (e) { + alert("Zotero Commons upload failed:\n\n" + e); + } + }); + translation.setLocation(outputDir); + translation.translate(); // synchronous + }, + true + ); + } + + process(items); + } + + + this.deleteItems = function (ids) { + Zotero.debug("Unimplemented"); + return; + + // TODO: Confirm + + for each(var id in ids) { + var bucket = this.getBucketFromItemID(id); + bucket.erase(); + } } - this.createAuthenticatedRequest = function (method, resource, headers, accessKey, secretKey, callback, data, sendAsBinary) { + this.createAuthenticatedRequest = function (method, resource, headers, accessKey, secretKey, callback, data, sendAsBinary, noCache) { + var url = Zotero.Commons.apiUrl + resource; + + Zotero.debug("Commons HTTP " + method + ": " + url); + var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] .createInstance(Components.interfaces.nsIXMLHttpRequest); - req.open(method, Zotero.Commons.apiUrl + resource, true); + req.open(method, url, true); var d = new Date(); headers["Date"] = d.toUTCString(); @@ -244,6 +563,10 @@ Zotero.Commons = new function() { req.setRequestHeader(header, headers[header]); } + if (noCache) { + req.channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE; + } + if (data) { if (sendAsBinary) { req.sendAsBinary(data); @@ -278,119 +601,360 @@ Zotero.Commons = new function() { return req; } + + + + function _getItemsFromBuckets() { + var items = []; + for (var name in _buckets) { + Zotero.debug('getting bucket ' + name); + var item = _buckets[name].item; + // Only return available items + if (item) { + Zotero.debug('found'); + items.push(item); + } + } + return items; + } + + + /* + function _createUniqueBucket(name, callback) { + var uploadErrorMsg = "An error occurred uploading a file to the Internet Archive"; + + var bucket = new Zotero.Commons.Bucket(name); + + bucket.create(function (success, error) { + if (success) { + callback(name); + return; + } + + if (error == Zotero.Commons.ERROR_BUCKET_EXISTS) { + var maxTries = 5; + + // Append suffixes until we find a unique name + var checkFunc = function (name, suffix, callback) { + var bucket = new Zotero.Commons.Bucket(name); + bucket.create(function (success, error) { + if (success) { + callback(name); + return; + } + + if (error == Zotero.Commons.ERROR_BUCKET_EXISTS) { + maxTries--; + // If we've exceeded maxTries, just use a random suffix + if (maxTries < 1) { + suffix = Zotero.ID.getKey(); + name = name.replace(/\-[0-9]+$/, "-" + suffix); + callback(name); + return; + } + + suffix++; + name = name.replace(/\-[0-9]+$/, "-" + suffix); + checkFunc(name, suffix, callback); + return; + } + + alert(uploadErrorMsg); + }); + } + + checkFunc(name + "-1", 1, callback); + return; + } + + alert(uploadErrorMsg); + }); + } + */ + + + this.debug = function (message, level) { + Zotero.debug("Commons: " + message, level); + } } -// accessKey and secretKey are passed to allow easy implementation -// of using multiple accounts -Zotero.Commons.Bucket = function(name, accessKey, secretKey) { +Zotero.Commons.Bucket = function (name) { this.name = name; - this.accessKey = accessKey; - this.secretKey = secretKey; + this.accessKey = Zotero.Prefs.get("commons.accessKey"); + this.secretKey = Zotero.Prefs.get("commons.secretKey"); this._items = null; this._requestingItems = false; this._needRefresh = false; + + this._item = null; + this._metadataLoading = false; + this._itemDataLoaded = false; + this._itemDataLoading = false; } -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__('item', function () { + Zotero.debug("REQUESTING ITEM FOR " + this.name); + + if (this._item) { + Zotero.debug("RETURNING"); + return this._item; } -}; + +/* var item = new Zotero.Item('document'); + item.setField('title', this.name); + return item; +*/ + + Zotero.debug("LOADING"); + + this._loadMetadata(function () { + Zotero.debug("LOADED"); + Zotero.Notifier.trigger('refresh', 'commons', []); + }); + + return false; +}); Zotero.Commons.Bucket.prototype.__defineGetter__('uri', function () { return 'http://www.archive.org/details/' + this.name; }); +Zotero.Commons.Bucket.prototype.__defineGetter__('downloadURI', function () { + return 'http://www.archive.org/download/' + this.name; +}); -Zotero.Commons.Bucket.prototype.getKeyUrl = function(name, key) { - return 'http://' + name + '.s3.us.archive.org/' + key; - -} - +Zotero.Commons.Bucket.prototype.__defineGetter__('metadataURI', function () { + return this.downloadURI + '/' + this.name + '_meta.xml'; +}); -Zotero.Commons.Bucket.prototype.relationPredicate = "owl:sameAs"; +Zotero.Commons.Bucket.prototype.__defineGetter__('apiURI', function() { + return Zotero.Commons.apiUrl + '/' + this.name; +}); +Zotero.Commons.Bucket.prototype.__defineGetter__('rdfURI', function() { + return Zotero.Commons.apiUrl + '/' + this.name + '/' + this.name + '.rdf'; +}); -// deletes selected items from IA. -Zotero.Commons.Bucket.prototype.deleteItems = function(ids) { - var method = "DELETE"; - var headers = { - "x-archive-cascade-delete":"1" - }; +Zotero.Commons.Bucket.prototype.relationPredicate = "owl:sameAs"; - Zotero.debug("Commons: called to delete: " + ids.toString()); +Zotero.Commons.Bucket.prototype.exists = function (callback, maxTries, tries) { + if (!tries) { + tries = 0; + } + var self = this; + + Zotero.Utilities.HTTP.doHead(this.uri, function (xmlhttp) { + switch (xmlhttp.status) { + case 200: + callback(1); + return; + + case 404: + case 503: // IA returns this for missing buckets + tries++; + + if (tries >= maxTries) { + callback(0); + return; + } + + var delay = Zotero.Commons.postCreateBucketDelay * tries; + var seconds = delay / 1000; + Zotero.debug("Bucket " + self.name + " doesn't yet exist -- retrying in " + seconds + " seconds"); + + Zotero.debug('---------'); + Zotero.debug(setTimeout); + + setTimeout(function () { + self.exists(callback, maxTries, tries); + }, delay); + return; + + default: + Zotero.debug(xmlhttp.status); + Zotero.debug(xmlhttp.responseText); + callback(-1); + return; + } + }); +} - // for each id passed in, issue delete request for <key>.ZIP file - var items = Zotero.Items.get(ids); - - for(var i = 0, len = items.length; i < len; i++) { - var item = items[i]; - var zipName = item.key + ".zip"; - // 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: Deleting: " + resource); +/** + * Get OCRed PDFs from files in this bucket + * + * @param {Function} callback Function to run after adding each file -- + * function is passed the new attachment item + * (which due to a Zotero.Attachments.importFromURL() + * limitation will still be downloading) + */ +Zotero.Commons.Bucket.prototype.syncFiles = function (callback) { + // Find local item if it exists + var rels = Zotero.Relations.getByURIs(null, this.relationPredicate, this.uri); + if (!rels.length) { + Zotero.debug("No local items linked to URI " + this.uri); + return; + } + if (rels.length > 1) { + throw ("Commons: More than one local item linked to remote bucket " + this.name); + } + var item = Zotero.URI.getURIItem(rels[0].subject); + if (!item) { + Zotero.debug("Linked local item not found for URI " + this.uri, 2); + return; + } + + // Get array of possible attachment names + var pdfFileNames = []; + var pdfTitles = []; + var ids = item.getAttachments(); + for each(var id in ids) { + var attachment = Zotero.Items.get(id); + var fileName = attachment.getFilename(); + if (!fileName || !fileName.match(/.+\.pdf$/)) { + continue; + } + pdfFileNames.push(fileName); + pdfTitles.push(attachment.getField('title')); + } + + if (this._requestingItems) { + throw ("Commons: Already requesting items for bucket"); + } + + this._requestingItems = true; + + var self = this; + + // Check for a full-text ("derived" in IA terms) OCRed version of the PDF, + // and if found add it as an attachment to the corresponding Zotero client item + Zotero.Utilities.HTTP.doGet(this.apiURI, 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; + } - // 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); + Zotero.debug(xmlhttp.responseText); + + // TODO: replace original PDF? + + var contents = xmlhttp.responseXML.getElementsByTagName("Contents"); + + // loop through files listed in bucket contents file + for(var i=0, len=contents.length; i<len; i++) { + var IAFileName = contents[i].getElementsByTagName('Key')[0].textContent; + // We care only about OCRed PDFs + if (!IAFileName.match(/\_text.pdf$/)) { + Zotero.debug("Skipping file " + IAFileName); + continue; } - 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) { - alert("Failed to delete " + resource + " at IA: server unavailable."); + + // Check if there's a relation for this PDF + + var IAFileURI = self.downloadURI + "/" + IAFileName; + var rels = Zotero.Relations.getByURIs(null, self.relationPredicate, IAFileURI); + if (rels.length) { + Zotero.debug("Commons: " + IAFileName + " has already been downloaded -- skipping"); + continue; + } + + var title = null; + var baseName = null; + for (var i in pdfFileNames) { + var fn = pdfFileNames[i]; + var n = IAFileName.replace("_text.pdf", ".pdf"); + var pos = n.lastIndexOf(fn); + if (pos == -1) { + continue; } - else { - alert("Failed to delete " + resource + " at IA."); - Zotero.debug("Commons: delete failed with status code: " + req.status); + // Ignore if filename matches in the middle for some crazy reason + if (pos + fn.length != n.length) { + continue; } + title = Zotero.localeJoin([pdfTitles[i], '(OCR)']); + baseName = fn.match(/(.+)\.pdf/)[1]; + break; } - }; - - Zotero.Commons.createAuthenticatedRequest( - method, resource, headers, self.accessKey, self.secretKey, callback - ); + + // If not, import PDF + var newAttachment = Zotero.Attachments.importFromURL( + IAFileURI, item.id, title, baseName, null, 'application/pdf' + ); + if (!(newAttachment instanceof Zotero.Item)) { + throw (newAttachment + " is not a Zotero.Item in Zotero.Commons.Bucket.syncFiles()"); + } + + // Add a relation linking the new attachment to the IA file + var uri = Zotero.URI.getItemURI(newAttachment); + Zotero.Relations.add(null, uri, self.relationPredicate, IAFileURI); + + callback(newAttachment); + } - // Delete Zotero RDF file - zipName = item.key + ".rdf"; - resource = '/' + self.name + '/' + zipName; - Zotero.debug("Commons: Deleting: " + resource); + self._requestingItems = false; + }); +} - Zotero.Commons.createAuthenticatedRequest( - method, resource, headers, self.accessKey, self.secretKey - ); - } + +// deletes selected items from IA. +Zotero.Commons.Bucket.prototype.erase = function () { + var method = "DELETE"; + var headers = { + "x-archive-cascade-delete":"1" + }; + + var resource = '/' + this.name; + var self = this; + + // Delete IA bucket + 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) { + alert("Failed to delete " + resource + " at IA: server unavailable."); + } + else { + alert("Failed to delete " + resource + " at IA."); + Zotero.debug("Commons: delete failed with status code: " + req.status); + } + } + }; + + Zotero.Commons.createAuthenticatedRequest( + method, resource, headers, self.accessKey, self.secretKey, callback + ); } -Zotero.Commons.Bucket.prototype.updateMetadata = function(key, action, data) { - Zotero.debug("updating metadata..."); +// UNUSED +Zotero.Commons.Bucket.prototype.updateMetadata = function(action, item, callback) { + Zotero.debug("Updating bucket metadata"); + var method = "PUT"; - self = this; - var headers2 = { + var headers = { "x-archive-ignore-preexisting-bucket":"1", "x-archive-meta01-collection":"scholarworkspaces", "x-archive-meta02-collection":"zoterocommons", @@ -399,63 +963,43 @@ Zotero.Commons.Bucket.prototype.updateMetadata = function(key, action, data) { }; var meta = null; - var resource3 = encodeURI('http://archive.org/download/' + self.name + '/' + self.name + '_meta.xml'); + var resource = encodeURI('http://archive.org/download/' + this.name + '/' + this.name + '_meta.xml'); - // get previous metadata. multiple language support difficult via IA s3. - Zotero.Utilities.HTTP.doGet(resource3, function(xmlHttp) { - - // recreate headers of languages already specified in metadata - var languages = xmlHttp.responseXML.getElementsByTagName("metadata")[0].getElementsByTagName("language"); - - if (data && data.items[0]) { - var itemLanguage = data.items[0].getField('language'); - var langSet = false; - - for(var i = 0, len = languages.length; i < len; i++) { - meta = "x-archive-meta0"+(i+1)+"-language"; - headers2[meta] = languages[i].textContent; - if (languages[i].textContent == itemLanguage) langSet = true; - } + var self = this; - // add language for item if not already specified - if (!langSet) { - meta = "x-archive-meta0"+(i+1)+"-language"; - headers2[meta] = data.items[0].getField('language'); - } + // get previous metadata. multiple language support difficult via IA s3. + Zotero.Utilities.HTTP.doGet(resource, function (xmlhttp) { + if (xmlhttp.status == 404 || (xmlhttp.status == 200 && !xmlhttp.responseXML)) { + Zotero.Commons.error("Error updating bucket metadata"); + return; } - // preserve metatdate for old zotero items - var text = xmlHttp.responseText; - var zitem = text.match(/<zoterokey(.*)ZIP/g); - var zlen = 0; - - if (zitem) Zotero.debug(zitem.length); - if (zitem) zlen = zitem.length; - - for(var i=0; i < zlen; i++) { - var zitemp = zitem[0].split('>'); - meta = "x-archive-meta-" + zitemp[0].substr(1); - Zotero.debug("Commons: found old zotero key: " + meta + " = " + zitemp[1]); - - // if action is delete, don't add - if (action != "delete" && meta.substr(9).toUpperCase() != key) - headers2[meta] = zitemp[1]; + if (itemTitle) { + headers["x-archive-meta-title"] = itemTitle; } - // adding headers in this way allows for easy scraping from zotero commons pages. - if (action == "add") { - meta = "x-archive-meta-" + "zoterokey" + key; - headers2[meta] = data.items[0].getField('title')+"|"+key+".ZIP"; + // recreate headers of languages already specified in metadata + var languages = xmlhttp.responseXML.getElementsByTagName("metadata")[0].getElementsByTagName("language"); + var itemLanguage = item.getField('language'); + + for (var i = 0, len = languages.length; i < len; i++) { + meta = "x-archive-meta0"+(i+1)+"-language"; + headers[meta] = languages[i].textContent; } - Zotero.debug(headers2); - resource2 = '/' + self.name; + Zotero.debug(headers); + resource = "/" + this.name; - var callback = function (req) { + var updateCallback = function (req) { + Zotero.debug('========'); + Zotero.debug("UPDATE"); + Zotero.debug(req.status); if(req.status < 202) { - Zotero.debug("Commons: " + resource2 + " metadata updated successfully."); - // if adding item, upload file - data.bucket.putKeyCallback(data); + Zotero.debug("Commons: " + resource + " metadata updated successfully."); + + if (callback) { + callback(); + } } else { Zotero.debug(req.status); @@ -474,286 +1018,25 @@ Zotero.Commons.Bucket.prototype.updateMetadata = function(key, action, data) { }; Zotero.Commons.createAuthenticatedRequest( - method, resource2, headers2, self.accessKey, self.secretKey, callback + method, resource, headers, self.accessKey, self.secretKey, updateCallback ); }); } -// return an array of items currently stored in this bucket -Zotero.Commons.Bucket.prototype.getItems = function() { - var method = "GET"; - var resource = '/' + this.name; - - if(this._items && !this._needRefresh) { - Zotero.debug("Commons: items already set. Returing existing items set"); - return this._items; - } - else { - Zotero.debug("Commons: items need refresh. re-getting..."); - } - - // avoid multiple requests to IA - if(this._requestingItems) { - Zotero.debug("Commons: already requesting items"); - return []; - } - - this._requestingItems = true; - var self = this; - self._items = []; - - // get a list of keys (files) associated with this bucket - 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. - - // TODO: replace original PDF? - - var contents = null; - contents = xmlhttp.responseXML.getElementsByTagName("Contents"); - - // loop through files listed in bucket contents file - for(var i = 0, len = contents.length; i < len; i++) { - var keyParts = contents[i].getElementsByTagName('Key')[0].textContent.split('.'); - - // if key file is Zotero ZIP export item - // TODO: check to see if really a zotero item, not just a IA zip - if(keyParts.length == 2 && keyParts[1] == 'zip') { - var key = keyParts[0]; - Zotero.debug("Commons: found key in IA response: " + key); - - // see if the ZIP item corresponds to a zotero item (ZIP name = item key) - // This of course only works for the creator of the bucket. - // Others will get Zotero items from the IA bucket via a translator. - var item = Zotero.Items.getByLibraryAndKey(null, key); - - if(item) { - Zotero.debug("Commons: found item:" + item.id); - itemIDs.push(item.id); - this._needRefresh = false; - - // loop through attachments of this item and look for missing OCR'd PDFs - var attachmentIDs = item.getAttachments(); - for(var j = 0, len2 = attachmentIDs.length; j < len2; j++) { - var attachedItem = Zotero.Items.get(attachmentIDs[j]); - var fileName = attachedItem.getFilename(); - - // Since we have to upload all files without spaces in the name (or they won't be OCR'd), - // we need to look for the hyphenated version of actual attachment name. - // A space next to a hyphen should be deleted, not repleaced with another hyphen - if (fileName && fileName.substr(fileName.length-9) != "_text.pdf") { - var haveOCRVersion = false; - var IAfileName = fileName.substr(0,fileName.length-4).replace(/ /g,'-') + "_text.pdf"; - IAfileName = IAfileName.replace(/-+/g,'-'); - Zotero.debug("Commons: OCR'd file for this attachment would be: " + IAfileName); - - // check to see if we already have the OCR'd PDF attached to the zotero item - for(var k = 0, len3 = attachmentIDs.length; k < len3; k++) { - var attachedItem = Zotero.Items.get(attachmentIDs[k]); - if (attachedItem.getFilename() == IAfileName) - haveOCRVersion = true; - } - - // if we need to get the OCR version... - if (!haveOCRVersion) { - - // set up new attachment - var attachmentUri = "http://s3.us.archive.org/"+self.name+"/" + IAfileName; - var mimeType = "application/pdf"; - - // scan bucket contents to see if the OCR'd PDF is available - for(var con = 0, len = contents.length; con < len; con++) { - var keys = contents[con].getElementsByTagName("Key"); - - for(var l = 0, len4 = keys.length; l < len4; l++) { - if (keys[l].textContent == IAfileName) { - Zotero.debug("Commons: about to get OCR file from: " + attachmentUri); - Zotero.Attachments.importFromURL(attachmentUri, item.id, null, null, null, mimeType, null); - } - else { - //Zotero.debug("Commons: no OCR'd PDF of this attachment is available."); - } - } // for each key - } // for each contents - } // end if need to get PDF - else { - Zotero.debug("Commons: do not need OCR'd PDF for attachment " + fileName); - } - } - } - - self._items.push(item); - } - } - } - - Zotero.Notifier.trigger('refresh', 'bucket', itemIDs); - - self._requestingItems = false; - }); - - // Browser offline - if (!req) { - self._requestingItems = false; - } - - // List isn't yet available - return []; -} - - -// upload zipped Zotero RDF output of items to this bucket -Zotero.Commons.Bucket.prototype.uploadItems = function(ids) { - this._items = null; - 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<len; i++) { - if (items[i].isRegularItem()) { - itemsToUpload = true; - break; - } - } - - var pr = Components.classes["@mozilla.org/network/default-prompt;1"] - .getService(Components.interfaces.nsIPrompt); - - if (!itemsToUpload) { - Zotero.debug("No regular items to upload"); - pr.alert("", "Only items with bibliographic metadata can be added to the Zotero Commons."); - return; - } - - var buttonFlags = (pr.BUTTON_POS_0) * (pr.BUTTON_TITLE_IS_STRING) - + (pr.BUTTON_POS_1) * (pr.BUTTON_TITLE_CANCEL); - var index = pr.confirmEx( - "Zotero Commons Upload", - "By uploading items to Zotero Commons you agree to the terms of use at zotero.org and archive.org. " - + "Please make sure metadata for your item(s) is set properly." - + "\n\n " - + "Continue to upload items to the Internet Archive?", - buttonFlags, - "Upload", - null, null, null, {} - ); - - // if user chooses 'cancel', exit. - if (index != 0) 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, success) { - if(!success) { - alert("Commons: translation failed for " + translation); - return; - } - - var data = translation._bucketData; - - try { - var dir = Components.classes["@mozilla.org/file/local;1"] - .createInstance(Components.interfaces.nsILocalFile); - dir.initWithPath(translation.path); - - - // capture RDF file because it needs to be sent along with all PDFs - var rdfPath = translation.path + "/" + dir.leafName + ".rdf"; - var rdfFile = Components.classes["@mozilla.org/file/local;1"] - .createInstance(Components.interfaces.nsILocalFile); - rdfFile.initWithPath(rdfPath); - Zotero.debug("Commons: RDF: " + rdfFile.path); - - // send one copy of RDF file with name of zotero item key. - // this allows us to very easily roundtrip zotero items. - data.uploadFile = rdfFile; - data.mimetype = "application/rdf+xml"; - data.bucket.putKeyCallback(data); - - // create zip file - var zipFile = Zotero.getTempDirectory(); - zipFile.append(dir.leafName + '.zip'); - Zotero.debug("Commons: created zipFile: " + dir.leafName); - - 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; - 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); - } - catch (e) { - alert("Commons: Upload failed: " + e); - } -} - - -Zotero.Commons.Bucket.prototype.putKeyCallback = function(data) { - var self = data.bucket; - var keyHyphened = data.uploadFile.leafName.replace(/ /g,'-'); - var key = data.uploadFile.leafName; +Zotero.Commons.Bucket.prototype.putFile = function (file, mimeType, callback) { + var fileName = file.leafName; + var fileNameHyphened = fileName.replace(/ /g,'-'); var method = "PUT"; - var resource = encodeURI('/' + self.name + '/' + keyHyphened); - var content = self.readFileContents(data.uploadFile); + var resource = encodeURI('/' + this.name + '/' + fileNameHyphened); + var content = Zotero.File.getBinaryContents(file); var headers = {}; + var self = this; - Zotero.debug("Uploading: " + resource); - - var callback = function (req) { + var putCallback = function (req) { + // Success if (req.status == 201) { - for (var i = 0, len = data.items.length; i < len; i++) { + /*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, keyHyphened); @@ -764,58 +1047,43 @@ Zotero.Commons.Bucket.prototype.putKeyCallback = function(data) { continue; } Zotero.Relations.add(null, url1, predicate, url2); + }*/ + Zotero.debug("Commons: " + fileName + " was uploaded successfully."); + //this._needRefresh = true; + //Zotero.Notifier.trigger('refresh', 'bucket', null); + + if (callback) { + callback(req.channel.URI.spec); } - Zotero.debug("Commons: " + key + " was uploaded successfully."); - this._needRefresh = true; - //Zotero.Notifier.trigger('refresh', 'bucket', null); + return; + } + + // Error + Zotero.debug(req.status); + Zotero.debug(req.responseText); + + if (req.status == 404) { + alert("Failed to upload " + fileName + " to IA: bucket not found"); + } + else if (req.status == 403) { + alert("Failed to upload " + fileName + " to IA: authentication failed."); + } + else if (req.status == 503) { + alert("Failed to upload " + fileName + " to IA: server unavailable."); } else { - Zotero.debug(req.status); - Zotero.debug(req.responseText); - - 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. status is " + req.status); - } + alert("Failed to upload " + fileName + " to IA. status is " + req.status); } }; Zotero.Commons.createAuthenticatedRequest( - method, resource, headers, self.accessKey, self.secretKey, callback, content, true + method, resource, headers, this.accessKey, this.secretKey, putCallback, content, true ); } -// Does the put call to IA, puting data.uploadFile into the bucket -// Changed to be a generic function to put something to IA -Zotero.Commons.Bucket.prototype.putKey = function(data, skipMeta) { - var self = data.bucket; - var key = data.uploadFile.leafName.substr(0,data.uploadFile.leafName.length-4); - var action = "add"; - - // updateMetadata calls putKeyCallback after metadata request is successful. - if (!skipMeta) self.updateMetadata(key, action, data); -} - - -// 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) { +Zotero.Commons.Bucket.prototype.zipDirectory = function(rootDir, dir, zipWriter) { dir = dir.directoryEntries; while(dir.hasMoreElements()) { var file = dir.getNext(); @@ -836,19 +1104,98 @@ Zotero.Commons.Bucket.prototype.zipDirectory = function(self, rootDir, dir, zipW ); if(file.isDirectory()) { - self.zipDirectory(self, rootDir, file, zipWriter); + this.zipDirectory(rootDir, file, zipWriter); continue; } } } +Zotero.Commons.Bucket.prototype._loadMetadata = function (callback) { + if (this._metadataLoading) { + return; + } + + this._metadataLoading = true; + var self = this; + + Zotero.Utilities.HTTP.doGet(this.rdfURI, function (xmlhttp) { + // If RDF not available, use the bucket metadata + if (xmlhttp.status != 200) { + Zotero.debug("RDF for bucket " + self.name + " not available"); + + /*Zotero.Utilities.HTTP.doGet(self.metadataURI, function (xmlhttp) { + if (xmlhttp.status != 200) { + Zotero.debug(xmlhttp.status); + Zotero.debug(xmlhttp.responseText); + return; + } + + Zotero.debug(xmlhttp.responseText); + + // Strip XML declaration and convert to E4X + var xml = new XML(xmlhttp.responseText.replace(/<\?xml.*\?>/, '')); + var title = xml.title; + if (!self._item) { + self._item = new Zotero.Item("document"); + self._item.id = Zotero.ID.getBigInt(); + } + self._item.setField('title', title); + + self._metadataLoading = false; + + if (callback) { + callback(); + } + });*/ + return; + } + + Zotero.debug(xmlhttp.responseText); + + var translate = new Zotero.Translate("import"); + translate.setString(xmlhttp.responseText); + translate.getTranslators() + translate.setTranslator(Zotero.Commons.RDF_IMPORT_TRANSLATOR.translatorID); + translate.setHandler("itemDone", function (translation, item) { + var typeID = Zotero.ItemTypes.getID(item.itemType); + var newItem = new Zotero.Item(typeID); + newItem.id = Zotero.ID.getBigInt(); + + for (var field in item) { + // Skip empty fields + if (!item[field]) { + continue; + } + var fieldID = Zotero.ItemFields.getID(field); + if (!fieldID) { + continue; + } + Zotero.debug('setting field ' + fieldID + ' to ' + item[field]); + try { + newItem.setField(fieldID, item[field]); + } + catch(e) { Zotero.debug(e); } + } + + self._item = newItem; + self._metadataLoading = false; + + if (callback) { + callback(); + } + return; + }); + translate.translate(false, false); + }); +} + + // Implements nsIRequestObserver -Zotero.Commons.ZipWriterObserver = function (zipWriter, callback, callbackData) { +Zotero.Commons.ZipWriterObserver = function (zipWriter, callback) { this._zipWriter = zipWriter; this._callback = callback; - this._callbackData = callbackData; } Zotero.Commons.ZipWriterObserver.prototype = { @@ -856,7 +1203,7 @@ Zotero.Commons.ZipWriterObserver.prototype = { onStopRequest: function(req, context, status) { this._zipWriter.close(); - this._callback(this._callbackData); + this._callback(); } } diff --git a/chrome/content/zotero/xpcom/data/group.js b/chrome/content/zotero/xpcom/data/group.js @@ -338,7 +338,7 @@ Zotero.Group.prototype.erase = function() { Zotero.DB.query(sql, this.libraryID); var prefix = "groups/" + this.id; - Zotero.Relations.eraseByPathPrefix(prefix); + Zotero.Relations.eraseByURIPrefix(Zotero.URI.defaultPrefix + prefix); // Delete group sql = "DELETE FROM groups WHERE groupID=?"; diff --git a/chrome/content/zotero/xpcom/data/item.js b/chrome/content/zotero/xpcom/data/item.js @@ -741,7 +741,7 @@ Zotero.Item.prototype.setField = function(field, value, loadIn) { } if (!Zotero.ItemFields.isValidForType(fieldID, this.itemTypeID)) { - var msg = '"' + field + "' is not a valid field for type " + this.itemTypeID; + var msg = "'" + field + "' is not a valid field for type " + this.itemTypeID; if (loadIn) { Zotero.debug(msg + " -- ignoring value '" + value + "'", 2); @@ -1240,7 +1240,7 @@ Zotero.Item.prototype.save = function() { if (Zotero.ItemFields.getID('accessDate') == fieldID && this.getField(fieldID) == 'CURRENT_TIMESTAMP') { - value = Zotero.DB.transactionDateTime; + value = Zotero.DB.transactionDateTime; } var dataType = ZU.getSQLDataType(value); @@ -3944,7 +3944,10 @@ Zotero.Item.prototype.erase = function() { //Zotero.Fulltext.clearItemContent(this.id); } - + // Remove relations + var relation = Zotero.URI.getItemURI(this); + Zotero.Relations.eraseByURIPrefix(relation); + Zotero.DB.query('DELETE FROM annotations WHERE itemID=?', this.id); Zotero.DB.query('DELETE FROM highlights WHERE itemID=?', this.id); Zotero.DB.query('DELETE FROM deletedItems WHERE itemID=?', this.id); diff --git a/chrome/content/zotero/xpcom/data/relations.js b/chrome/content/zotero/xpcom/data/relations.js @@ -31,8 +31,6 @@ Zotero.Relations = new function () { owl: 'http://www.w3.org/2002/07/owl#' }; - var _prefix = "http://zotero.org/"; - this.get = function (id) { if (typeof id != 'number') { throw ("id '" + id + "' must be an integer in Zotero.Relations.get()"); @@ -165,13 +163,40 @@ Zotero.Relations = new function () { } - this.eraseByPathPrefix = function (prefix) { - prefix = _prefix + prefix + '%'; + this.eraseByURIPrefix = function (prefix) { + prefix = prefix + '%'; sql = "DELETE FROM relations WHERE subject LIKE ? OR object LIKE ?"; Zotero.DB.query(sql, [prefix, prefix]); } + this.eraseByURI = function (uri) { + sql = "DELETE FROM relations WHERE subject=? OR object=?"; + Zotero.DB.query(sql, [uri, uri]); + } + + + this.purge = function () { + var sql = "SELECT subject FROM relations UNION SELECT object FROM relations"; + var uris = Zotero.DB.columnQuery(sql); + if (uris) { + var prefix = Zotero.URI.defaultPrefix; + Zotero.DB.beginTransaction(); + for each(var uri in uris) { + // Skip URIs that don't begin with the default prefix, + // since they don't correspond to local items + if (uri.indexOf(prefix) == -1) { + continue; + } + if (!Zotero.URI.getURIItem(uri)) { + this.eraseByURI(uri); + } + } + Zotero.DB.commitTransaction(); + } + } + + this.xmlToRelation = function (xml) { var relation = new Zotero.Relation; var libraryID = xml.@libraryID.toString(); @@ -196,7 +221,7 @@ Zotero.Relations = new function () { var [prefix, value] = uri.split(':'); if (prefix && value) { if (!_namespaces[prefix]) { - throw ("Invalid prefix '" + prefix + "' in Zotero.Relations.add()"); + throw ("Invalid prefix '" + prefix + "' in Zotero.Relations._getPrefixAndValue()"); } return [prefix, value]; } diff --git a/chrome/content/zotero/xpcom/file.js b/chrome/content/zotero/xpcom/file.js @@ -137,6 +137,21 @@ Zotero.File = new function(){ } + + /** + * Return the contents of a file as a byte array + */ + this.getBinaryContents = 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()); + } + + /* * Return the contents of a URL as a string * diff --git a/chrome/content/zotero/xpcom/itemTreeView.js b/chrome/content/zotero/xpcom/itemTreeView.js @@ -52,7 +52,7 @@ Zotero.ItemTreeView = function(itemGroup, sourcesOnly) this._dataItems = []; this.rowCount = 0; - this._unregisterID = Zotero.Notifier.registerObserver(this, ['item', 'collection-item', 'share-items', 'bucket']); + this._unregisterID = Zotero.Notifier.registerObserver(this, ['item', 'collection-item', 'share-items', 'commons']); } @@ -260,6 +260,7 @@ Zotero.ItemTreeView.prototype.refresh = function() */ Zotero.ItemTreeView.prototype.notify = function(action, type, ids, extraData) { + Zotero.debug('============='); if (!this._treebox || !this._treebox.treeBody) { Components.utils.reportError("Treebox didn't exist in itemTreeView.notify()"); return; @@ -269,7 +270,7 @@ Zotero.ItemTreeView.prototype.notify = function(action, type, ids, extraData) Zotero.debug("Item row map didn't exist in itemTreeView.notify()"); return; } - + Zotero.debug(1); var itemGroup = this._itemGroup; var madeChanges = false; @@ -284,9 +285,10 @@ Zotero.ItemTreeView.prototype.notify = function(action, type, ids, extraData) this.refresh(); } } - else if (type == 'bucket') { - if (itemGroup.isBucket()) { + else if (type == 'commons') { + if (itemGroup.isCommons()) { this.refresh(); + this.sort(); } } else if (savedSelection.length == 1 && savedSelection[0] == ids[0]) { @@ -455,6 +457,7 @@ Zotero.ItemTreeView.prototype.notify = function(action, type, ids, extraData) } else if(action == 'add') { + Zotero.debug(2); // If saved search or trash, just re-run search if (itemGroup.isSearch() || itemGroup.isTrash()) { this.refresh(); @@ -499,6 +502,7 @@ Zotero.ItemTreeView.prototype.notify = function(action, type, ids, extraData) if(madeChanges) { + Zotero.debug(3); // If adding and this is the active window, select the item if(action == 'add' && ids.length===1 && activeWindow) { @@ -511,7 +515,7 @@ Zotero.ItemTreeView.prototype.notify = function(action, type, ids, extraData) // Reset to Info tab this._ownerDocument.getElementById('zotero-view-tabbox').selectedIndex = 0; - + Zotero.debug(4); this.selectItem(ids[0]); } // If single item is selected and was modified @@ -540,6 +544,7 @@ Zotero.ItemTreeView.prototype.notify = function(action, type, ids, extraData) } else { + Zotero.debug(4); var previousRow = this._itemRowMap[ids[0]]; if (sort) { @@ -1112,6 +1117,8 @@ Zotero.ItemTreeView.prototype.sort = function(itemID) */ Zotero.ItemTreeView.prototype.selectItem = function(id, expand, noRecurse) { + Zotero.debug('============='); + Zotero.debug(Zotero.suppressUIUpdates); // Don't change selection if UI updates are disabled (e.g., during sync) if (Zotero.suppressUIUpdates) { return; @@ -1267,8 +1274,8 @@ Zotero.ItemTreeView.prototype.deleteSelection = function (force) else if (itemGroup.isCollection()) { itemGroup.ref.removeItems(ids); } - else if (itemGroup.isBucket()) { - itemGroup.ref.deleteItems(ids); + else if (itemGroup.isCommons()) { + Zotero.Commons.deleteItems(ids); } this._treebox.endUpdateBatch(); } diff --git a/chrome/content/zotero/xpcom/uri.js b/chrome/content/zotero/xpcom/uri.js @@ -25,6 +25,8 @@ Zotero.URI = new function () { + this.__defineGetter__('defaultPrefix', function () 'http://zotero.org/'); + var _baseURI = ZOTERO_CONFIG.BASE_URI; var _apiURI = ZOTERO_CONFIG.API_URI; @@ -166,12 +168,12 @@ Zotero.URI = new function () { if (itemURI.indexOf(localUserURI) == 0) { itemURI = itemURI.substr(localUserURI.length); var libraryType = 'user'; - var libraryTypeID = null; + var libraryID = null; } } */ var libraryType = 'user'; - var libraryTypeID = null; + var libraryID = null; } // If not found, try global URI @@ -186,7 +188,7 @@ Zotero.URI = new function () { throw ("Invalid library URI '" + itemURI + "' in Zotero.URI.getURIItem()"); } var libraryType = matches[1].substr(0, matches[1].length-1); - var libraryTypeID = matches[2]; + var libraryID = matches[2]; itemURI = itemURI.replace(typeRE, ''); } @@ -202,7 +204,10 @@ Zotero.URI = new function () { } if (libraryType == 'group') { - var libraryID = Zotero.Groups.getLibraryIDFromGroupID(libraryTypeID); + if (!Zotero.Libraries.exists(libraryID)) { + return false; + } + var libraryID = Zotero.Groups.getLibraryIDFromGroupID(libraryID); return Zotero.Items.getByLibraryAndKey(libraryID, itemKey); } } diff --git a/chrome/content/zotero/xpcom/zotero.js b/chrome/content/zotero/xpcom/zotero.js @@ -1033,6 +1033,8 @@ var Zotero = new function(){ * Returns true if an object (or associative array) has at least one value */ function hasValues(obj) { + Zotero.debug("WARNING: Zotero.isEmpty() is deprecated! Use Zotero.Utilities.isEmpty(obj)", 2); + for (var i in obj) { return true; } @@ -1239,6 +1241,8 @@ var Zotero = new function(){ Zotero.Tags.purge(); Zotero.Fulltext.purgeUnusedWords(); Zotero.Items.purge(); + // DEBUG: this might not need to be permanent + Zotero.Relations.purge(); if (!skipStoragePurge && Zotero.Utilities.prototype.probability(10)) { Zotero.Sync.Storage.purgeDeletedStorageFiles('zfs'); diff --git a/defaults/preferences/zotero.js b/defaults/preferences/zotero.js @@ -110,7 +110,6 @@ 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", '');