From 91459f95f747aa6cfa06952d530d2d53751dab7f Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Thu, 14 May 2009 18:23:40 +0000 Subject: [PATCH] 2.0b3 megacommit - Support for group libraries - General support for multiple libraries of different types - Streamlined sync support - Using solely libraryID and key rather than itemID, and removed all itemID-changing code - Combined two requests for increased performance and decreased server load - Added warning on user account change - Provide explicit error message on SSL failure - Removed snapshot and link toolbar buttons and changed browser context menu options and drags to create parent items + snapshots - Closes #786, Add numPages field - Fixes #1063, Duplicate item with tags broken in Sync Preview - Added better purging of deleted tags - Added local user key before first sync - Add clientDateModified to all objects for more flexibility in syncing - Added new triples-based Relation object type, currently used to store links between items copied between local and group libraries - Updated zotero.org translator for groups - Additional trigger-based consistency checks - Fixed broken URL drag in Firefox 3.5 - Disabled zeroconf menu option (no longer functional) Developer-specific changes: - Overhauled data layer - Data object constructors no longer take arguments (return to 1.0-like API) - Existing objects can be retrieved by setting id or library/key properties - id/library/key must be set for new objects before other fields - New methods: - ZoteroPane.getSelectedLibraryID() - ZoteroPane.getSelectedGroup(asID) - ZoteroPane.addItemFromDocument(doc, itemType, saveSnapshot) - ZoteroPane.addItemFromURL(url, itemType) - ZoteroPane.canEdit() - Zotero.CollectionTreeView.selectLibrary(libraryID) - New Zotero.URI methods - Changed methods - Many data object methods now take a libraryID - ZoteroPane.addAttachmentFromPage(link, itemID) - Removed saveItem and saveAttachments parameters from Zotero.Translate constructor - translate() now takes a libraryID, null for local library, or false to not save items (previously on constructor) - saveAttachments is now a translate() parameter - Zotero.flattenArguments() better handles passed objects - Zotero.File.getFileHash() (not currently used) --- chrome/content/zotero/advancedSearch.js | 9 +- chrome/content/zotero/bindings/itembox.xml | 20 +- chrome/content/zotero/bindings/merge.xml | 4 +- chrome/content/zotero/bindings/noteeditor.xml | 7 +- chrome/content/zotero/bindings/relatedbox.xml | 16 +- .../content/zotero/bindings/tagselector.xml | 32 +- chrome/content/zotero/browser.js | 34 +- chrome/content/zotero/fileInterface.js | 3 +- chrome/content/zotero/lookup.js | 12 +- chrome/content/zotero/overlay.js | 610 +++++-- chrome/content/zotero/overlay.xul | 26 +- .../content/zotero/preferences/preferences.js | 3 +- .../zotero/preferences/preferences.xul | 11 +- chrome/content/zotero/selectItemsDialog.xul | 2 +- chrome/content/zotero/xpcom/attachments.js | 57 +- .../zotero/xpcom/collectionTreeView.js | 666 +++++-- .../content/zotero/xpcom/data/collection.js | 402 +++-- .../content/zotero/xpcom/data/collections.js | 17 - chrome/content/zotero/xpcom/data/creator.js | 181 +- chrome/content/zotero/xpcom/data/creators.js | 29 +- .../content/zotero/xpcom/data/dataObjects.js | 159 +- chrome/content/zotero/xpcom/data/group.js | 470 +++++ chrome/content/zotero/xpcom/data/groups.js | 58 + chrome/content/zotero/xpcom/data/item.js | 732 +++++--- chrome/content/zotero/xpcom/data/items.js | 29 +- chrome/content/zotero/xpcom/data/libraries.js | 44 + chrome/content/zotero/xpcom/data/relation.js | 149 ++ chrome/content/zotero/xpcom/data/relations.js | 149 ++ chrome/content/zotero/xpcom/data/tag.js | 159 +- chrome/content/zotero/xpcom/data/tags.js | 93 +- chrome/content/zotero/xpcom/data_access.js | 3 + chrome/content/zotero/xpcom/file.js | 19 + chrome/content/zotero/xpcom/fulltext.js | 2 +- chrome/content/zotero/xpcom/ingester.js | 12 +- chrome/content/zotero/xpcom/itemTreeView.js | 243 +-- chrome/content/zotero/xpcom/notifier.js | 4 +- chrome/content/zotero/xpcom/schema.js | 67 +- chrome/content/zotero/xpcom/search.js | 171 +- chrome/content/zotero/xpcom/storage.js | 36 +- chrome/content/zotero/xpcom/sync.js | 1559 ++++++++++------- chrome/content/zotero/xpcom/translate.js | 82 +- chrome/content/zotero/xpcom/uri.js | 112 ++ chrome/content/zotero/xpcom/zotero.js | 70 +- chrome/skin/default/zotero/group_add.png | Bin 0 -> 807 bytes chrome/skin/default/zotero/merge.css | 2 +- chrome/skin/default/zotero/overlay.css | 10 + .../skin/default/zotero/treesource-groups.png | Bin 0 -> 753 bytes components/zotero-protocol-handler.js | 7 +- components/zotero-service.js | 6 + install.rdf | 2 +- system.sql | 8 +- translators/zotero.org.js | 22 +- triggers.sql | 441 ++++- update.rdf | 2 +- userdata.sql | 123 +- 55 files changed, 5370 insertions(+), 1816 deletions(-) create mode 100644 chrome/content/zotero/xpcom/data/group.js create mode 100644 chrome/content/zotero/xpcom/data/groups.js create mode 100644 chrome/content/zotero/xpcom/data/libraries.js create mode 100644 chrome/content/zotero/xpcom/data/relation.js create mode 100644 chrome/content/zotero/xpcom/data/relations.js create mode 100644 chrome/content/zotero/xpcom/uri.js create mode 100755 chrome/skin/default/zotero/group_add.png create mode 100755 chrome/skin/default/zotero/treesource-groups.png diff --git a/chrome/content/zotero/advancedSearch.js b/chrome/content/zotero/advancedSearch.js index d667d4c17b..ac8cada794 100644 --- a/chrome/content/zotero/advancedSearch.js +++ b/chrome/content/zotero/advancedSearch.js @@ -29,7 +29,14 @@ var ZoteroAdvancedSearch = new function() { var itemGroup = { isSearchMode: function() { return true; }, getChildItems: function () { - var ids = _searchBox.search.search(); + var search = _searchBox.search.clone(); + // FIXME: Hack to exclude group libraries for now + var groups = Zotero.Groups.getAll(); + for each(var group in groups) { + search.addCondition('libraryID', 'isNot', group.libraryID); + } + //var search = _searchBox.search; + var ids = search.search(); return Zotero.Items.get(ids); }, isLibrary: function () { return false; }, diff --git a/chrome/content/zotero/bindings/itembox.xml b/chrome/content/zotero/bindings/itembox.xml index 20178b7f32..c14c668905 100644 --- a/chrome/content/zotero/bindings/itembox.xml +++ b/chrome/content/zotero/bindings/itembox.xml @@ -407,7 +407,7 @@ this._tabIndexMaxInfoFields = Math.max(this._tabIndexMaxInfoFields, tabindex); if (fieldIsClickable && - !this.item.isPrimaryField(fieldName) && + !Zotero.Items.isPrimaryField(fieldName) && Zotero.ItemFields.isFieldOfBase(Zotero.ItemFields.getID(fieldName), 'date')) { this.addDateRow(fieldNames[i], this.item.getField(fieldName, true), tabindex); continue; @@ -1688,7 +1688,7 @@ @@ -1802,6 +1802,7 @@ null null + + + + + + + + @@ -170,7 +183,7 @@ var tagsToggleBox = this.id('tags-toggle'); if (fetch || this._dirty) { - this._tags = Zotero.Tags.getAll(this._types); + this._tags = Zotero.Tags.getAll(this._types, this.libraryID); // Remove children while (tagsToggleBox.hasChildNodes()){ @@ -414,7 +427,7 @@ - + + diff --git a/chrome/content/zotero/browser.js b/chrome/content/zotero/browser.js index 49b053237b..27cc649122 100644 --- a/chrome/content/zotero/browser.js +++ b/chrome/content/zotero/browser.js @@ -112,11 +112,14 @@ var Zotero_Browser = new function() { function(e) { Zotero_Browser.chromeUnload(e) }, false); } - /* - * Scrapes a page (called when the capture icon is clicked); takes a collection - * ID as the argument + /** + * Scrapes a page (called when the capture icon is clicked) + * + * @param {Integer} libraryID + * @param {Integer} collectionID + * @return void */ - function scrapeThisPage(saveLocation) { + function scrapeThisPage(libraryID, collectionID) { if (!Zotero.stateCheck()) { Zotero_Browser.progress.changeHeadline(Zotero.getString("ingester.scrapeError")); var desc = Zotero.getString("ingester.scrapeError.transactionInProgress.previousError") @@ -126,7 +129,7 @@ var Zotero_Browser = new function() { Zotero_Browser.progress.startCloseTimer(8000); return; } - _getTabObject(this.tabbrowser.selectedBrowser).translate(saveLocation); + _getTabObject(this.tabbrowser.selectedBrowser).translate(libraryID, collectionID); } /* @@ -192,6 +195,8 @@ var Zotero_Browser = new function() { /* * called to show the collection selection popup + * + * not currently used */ function showPopup(collectionID, parentElement) { if(_scrapePopupShowing && parentElement.hasChildNodes()) { @@ -676,19 +681,18 @@ Zotero_Browser.Tab.prototype._attemptLocalFileImport = function(doc) { } /* - * translate a page, saving in saveLocation + * translate a page + * + * @param {Integer} libraryID + * @param {Integer} collectionID */ -Zotero_Browser.Tab.prototype.translate = function(saveLocation) { +Zotero_Browser.Tab.prototype.translate = function(libraryID, collectionID) { if(this.page.translators && this.page.translators.length) { Zotero_Browser.progress.show(); Zotero_Browser.isScraping = true; - if(saveLocation) { - saveLocation = Zotero.Collections.get(saveLocation); - } else { // save to currently selected collection, if a collection is selected - try { - saveLocation = ZoteroPane.getSelectedCollection(); - } catch(e) {} + if(collectionID) { + collection = Zotero.Collections.get(collectionID); } var me = this; @@ -701,9 +705,9 @@ Zotero_Browser.Tab.prototype.translate = function(saveLocation) { this.page.hasBeenTranslated = true; } this.page.translate.clearHandlers("itemDone"); - this.page.translate.setHandler("itemDone", function(obj, item) { Zotero_Browser.itemDone(obj, item, saveLocation) }); + this.page.translate.setHandler("itemDone", function(obj, item) { Zotero_Browser.itemDone(obj, item, collection) }); - this.page.translate.translate(); + this.page.translate.translate(libraryID); } } diff --git a/chrome/content/zotero/fileInterface.js b/chrome/content/zotero/fileInterface.js index 7dd4591c43..e1dfabee58 100644 --- a/chrome/content/zotero/fileInterface.js +++ b/chrome/content/zotero/fileInterface.js @@ -330,7 +330,8 @@ var Zotero_File_Interface = new function() { } else { var searchRef = ZoteroPane.getSelectedSavedSearch(); if(searchRef) { - var search = new Zotero.Search(searchRef.id); + var search = new Zotero.Search(); + search.id = searchRef.id; name = search.name; } } diff --git a/chrome/content/zotero/lookup.js b/chrome/content/zotero/lookup.js index 7f7a9c5a64..1235737d06 100644 --- a/chrome/content/zotero/lookup.js +++ b/chrome/content/zotero/lookup.js @@ -18,7 +18,7 @@ const Zotero_Lookup = new function () { } } - var translate = new Zotero.Translate("search", true, false); + var translate = new Zotero.Translate("search"); translate.setSearch(item); // be lenient about translators @@ -38,15 +38,17 @@ const Zotero_Lookup = new function () { } }); - var saveLocation = false; + var libraryID = null; + var collection = false; try { - saveLocation = window.opener.ZoteroPane.getSelectedCollection(); + libraryID = window.opener.ZoteroPane.getSelectedLibraryID(); + collection = window.opener.ZoteroPane.getSelectedCollection(); } catch(e) {} translate.setHandler("itemDone", function(obj, item) { - if(saveLocation) saveLocation.addItem(item.getID()); + if(collection) collection.addItem(item.id); }); - translate.translate(); + translate.translate(libraryID); return false; } } diff --git a/chrome/content/zotero/overlay.js b/chrome/content/zotero/overlay.js index ae4a0ff239..f90dcd7a73 100644 --- a/chrome/content/zotero/overlay.js +++ b/chrome/content/zotero/overlay.js @@ -66,7 +66,6 @@ var ZoteroPane = new function() this.getSortedItems = getSortedItems; this.getSortField = getSortField; this.getSortDirection = getSortDirection; - this.buildCollectionContextMenu = buildCollectionContextMenu; this.buildItemContextMenu = buildItemContextMenu; this.onDoubleClick = onDoubleClick; this.loadURI = loadURI; @@ -74,11 +73,8 @@ var ZoteroPane = new function() this.clearItemsPaneMessage = clearItemsPaneMessage; this.contextPopupShowing = contextPopupShowing; this.openNoteWindow = openNoteWindow; - this.newNote = newNote; this.addTextToNote = addTextToNote; - this.addItemFromPage = addItemFromPage; this.addAttachmentFromDialog = addAttachmentFromDialog; - this.addAttachmentFromPage = addAttachmentFromPage; this.viewAttachment = viewAttachment; this.viewSelectedAttachment = viewSelectedAttachment; this.showAttachmentNotFoundDialog = showAttachmentNotFoundDialog; @@ -92,6 +88,9 @@ var ZoteroPane = new function() var self = this; var titlebarcolorState, toolbarCollapseState, titleState; + // Also needs to be changed in collectionTreeView.js + var _lastViewedFolderRE = /^(?:(C|S|G)([0-9]+)|L)$/; + /* * Called when the window is open */ @@ -136,7 +135,7 @@ var ZoteroPane = new function() Zotero.setFontSize(document.getElementById('zotero-pane')) if (Zotero.isMac) { - document.getElementById('zotero-tb-actions-zeroconf-update').setAttribute('hidden', false); + //document.getElementById('zotero-tb-actions-zeroconf-update').setAttribute('hidden', false); document.getElementById('zotero-pane').setAttribute('platform', 'mac'); } else if(Zotero.isWin) { document.getElementById('zotero-pane').setAttribute('platform', 'win'); @@ -180,11 +179,6 @@ var ZoteroPane = new function() Zotero.Keys.windowInit(document); - // If the database was initialized and Zotero hasn't been run before - // in this profile, display the Quick Start Guide -- this way the guide - // won't be displayed when they sync their DB to another profile or if - // they the DB is initialized erroneously (e.g. while switching data - // directory locations) if (Zotero.restoreFromServer) { Zotero.restoreFromServer = false; @@ -213,6 +207,11 @@ var ZoteroPane = new function() } }, 1000); } + // If the database was initialized and Zotero hasn't been run before + // in this profile, display the Quick Start Guide -- this way the guide + // won't be displayed when they sync their DB to another profile or if + // they the DB is initialized erroneously (e.g. while switching data + // directory locations) else if (Zotero.Schema.dbInitialized && Zotero.Prefs.get('firstRun')) { setTimeout(function () { gBrowser.selectedTab = gBrowser.addTab(ZOTERO_CONFIG.FIRST_RUN_URL); @@ -629,29 +628,42 @@ var ZoteroPane = new function() return false; } - var item = new Zotero.Item(false, typeID); - - for (var i in data) - { - item.setField(i, data[i]); + if (!this.canEdit()) { + this.displayCannotEditLibraryMessage(); + return; } - item.save(); + var item = new Zotero.Item(typeID); + item.libraryID = this.getSelectedLibraryID(); + for (var i in data) { + item.setField(i, data[i]); + } + var itemID = item.save(); if (this.itemsView && this.itemsView._itemGroup.isCollection()) { - this.itemsView._itemGroup.ref.addItem(item.id); + this.itemsView._itemGroup.ref.addItem(itemID); } //set to Info tab document.getElementById('zotero-view-item').selectedIndex = 0; - this.selectItem(item.id); + this.selectItem(itemID); - return item; + return Zotero.Items.get(itemID); } function newCollection(parent) { + if (!Zotero.stateCheck()) { + this.displayErrorMessage(true); + return false; + } + + if (!this.canEdit()) { + this.displayCannotEditLibraryMessage(); + return; + } + var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] .getService(Components.interfaces.nsIPromptService); @@ -674,13 +686,29 @@ var ZoteroPane = new function() } var collection = new Zotero.Collection; + collection.libraryID = this.getSelectedLibraryID(); collection.name = newName.value; collection.parent = parent; collection.save(); } + + this.newGroup = function () { + if (this.isFullScreen()) { + this.toggleDisplay(); + } + + window.loadURI(Zotero.Groups.addGroupURL); + } + + function newSearch() { + if (!Zotero.stateCheck()) { + this.displayErrorMessage(true); + return false; + } + var s = new Zotero.Search(); s.addCondition('title', 'contains', ''); @@ -692,6 +720,21 @@ var ZoteroPane = new function() } + this.openLookupWindow = function () { + if (!Zotero.stateCheck()) { + this.displayErrorMessage(true); + return false; + } + + if (!this.canEdit()) { + this.displayCannotEditLibraryMessage(); + return; + } + + window.openDialog('chrome://zotero/content/lookup.xul', 'zotero-lookup', 'chrome,modal'); + } + + function openAdvancedSearchWindow() { var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] .getService(Components.interfaces.nsIWindowMediator); @@ -825,13 +868,13 @@ var ZoteroPane = new function() * Passed to the items tree to trigger on changes */ function _setTagScope() { - var itemgroup = self.collectionsView. - _getItemAtRow(self.collectionsView.selection.currentIndex); + var itemGroup = self.collectionsView._getItemAtRow(self.collectionsView.selection.currentIndex); var tagSelector = document.getElementById('zotero-tag-selector'); if (!tagSelector.getAttribute('collapsed') || tagSelector.getAttribute('collapsed') == 'false') { Zotero.debug('Updating tag selector with current tags'); - tagSelector.scope = itemgroup.getChildTags(); + tagSelector.libraryID = itemGroup.ref.libraryID; + tagSelector.scope = itemGroup.getChildTags(); } } @@ -841,45 +884,59 @@ var ZoteroPane = new function() if (this.itemsView) { this.itemsView.unregister(); - document.getElementById('zotero-items-tree').removeEventListener( - 'keypress', this.itemsView.wrappedJSObject.listener, false - ); + if (this.itemsView.wrappedJSObject.listener) { + document.getElementById('zotero-items-tree').removeEventListener( + 'keypress', this.itemsView.wrappedJSObject.listener, false + ); + } this.itemsView.wrappedJSObject.listener = null; document.getElementById('zotero-items-tree').view = this.itemsView = null; } document.getElementById('zotero-tb-search').value = ""; - if (this.collectionsView.selection.count == 1 && this.collectionsView.selection.currentIndex != -1) { - var itemgroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); - itemgroup.setSearch(''); - itemgroup.setTags(getTagSelection()); - itemgroup.showDuplicates = false; - - try { - Zotero.UnresponsiveScriptIndicator.disable(); - this.itemsView = new Zotero.ItemTreeView(itemgroup); - this.itemsView.addCallback(_setTagScope); - document.getElementById('zotero-items-tree').view = this.itemsView; - this.itemsView.selection.clearSelection(); - } - finally { - Zotero.UnresponsiveScriptIndicator.enable(); - } - - if (itemgroup.isLibrary()) { - Zotero.Prefs.set('lastViewedFolder', 'L'); - } - if (itemgroup.isCollection()) { - Zotero.Prefs.set('lastViewedFolder', 'C' + itemgroup.ref.id); - } - else if (itemgroup.isSearch()) { - Zotero.Prefs.set('lastViewedFolder', 'S' + itemgroup.ref.id); - } - } - else - { + if (this.collectionsView.selection.count != 1) { document.getElementById('zotero-items-tree').view = this.itemsView = null; + return; + } + + // this.collectionsView.selection.currentIndex != -1 + + var itemgroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); + + /* + if (itemgroup.isSeparator()) { + document.getElementById('zotero-items-tree').view = this.itemsView = null; + return; + } + */ + + itemgroup.setSearch(''); + itemgroup.setTags(getTagSelection()); + itemgroup.showDuplicates = false; + + try { + Zotero.UnresponsiveScriptIndicator.disable(); + this.itemsView = new Zotero.ItemTreeView(itemgroup); + this.itemsView.addCallback(_setTagScope); + document.getElementById('zotero-items-tree').view = this.itemsView; + this.itemsView.selection.clearSelection(); + } + finally { + Zotero.UnresponsiveScriptIndicator.enable(); + } + + if (itemgroup.isLibrary()) { + Zotero.Prefs.set('lastViewedFolder', 'L'); + } + if (itemgroup.isCollection()) { + Zotero.Prefs.set('lastViewedFolder', 'C' + itemgroup.ref.id); + } + else if (itemgroup.isSearch()) { + Zotero.Prefs.set('lastViewedFolder', 'S' + itemgroup.ref.id); + } + else if (itemgroup.isGroup()) { + Zotero.Prefs.set('lastViewedFolder', 'G' + itemgroup.ref.id); } } @@ -928,7 +985,7 @@ var ZoteroPane = new function() tabs.hidden = true; var noteEditor = document.getElementById('zotero-note-editor'); - noteEditor.mode = this.itemsView.readOnly ? 'view' : 'edit'; + noteEditor.mode = this.collectionsView.editable ? 'edit' : 'view'; // If loading new or different note, disable undo while we repopulate the text field // so Undo doesn't end up clearing the field. This also ensures that Undo doesn't @@ -942,10 +999,7 @@ var ZoteroPane = new function() noteEditor.enableUndo(); var viewButton = document.getElementById('zotero-view-note-button'); - if (this.itemsView.readOnly) { - viewButton.hidden = true; - } - else { + if (this.collectionsView.editable) { viewButton.hidden = false; viewButton.setAttribute('noteID', item.ref.id); if (item.ref.getSource()) { @@ -955,6 +1009,9 @@ var ZoteroPane = new function() viewButton.removeAttribute('sourceID'); } } + else { + viewButton.hidden = true; + } document.getElementById('zotero-item-pane-content').selectedIndex = 2; } @@ -963,7 +1020,7 @@ var ZoteroPane = new function() tabs.hidden = true; var attachmentBox = document.getElementById('zotero-attachment-box'); - attachmentBox.mode = this.itemsView.readOnly ? 'view' : 'edit'; + attachmentBox.mode = this.collectionsView.editable ? 'edit' : 'view'; attachmentBox.item = item.ref; document.getElementById('zotero-item-pane-content').selectedIndex = 3; @@ -973,16 +1030,16 @@ var ZoteroPane = new function() else { document.getElementById('zotero-item-pane-content').selectedIndex = 1; - if (this.itemsView.readOnly) { - document.getElementById('zotero-view-item').selectedIndex = 0; - ZoteroItemPane.viewItem(item.ref, 'view'); - tabs.hidden = true; - } - else { + if (this.collectionsView.editable) { ZoteroItemPane.viewItem(item.ref); tabs.selectedIndex = document.getElementById('zotero-view-item').selectedIndex; tabs.hidden = false; } + else { + document.getElementById('zotero-view-item').selectedIndex = 0; + ZoteroItemPane.viewItem(item.ref, 'view'); + tabs.hidden = true; + } } } else @@ -1041,21 +1098,26 @@ var ZoteroPane = new function() function duplicateSelectedItem() { - var item = this.getSelectedItems()[0]; - if (item.getTags()) { - var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Components.interfaces.nsIPromptService); - ps.alert(null, "Error", "Duplication of tagged items is not available in this Zotero release."); + if (!this.canEdit()) { + this.displayCannotEditLibraryMessage(); return; } - var newItem = this.getSelectedItems()[0].clone(); - var newItemID = newItem.save() - var newItem = Zotero.Items.get(newItemID); + var item = this.getSelectedItems()[0]; + + // Create new unsaved clone item in target library + var newItem = new Zotero.Item(item.itemTypeID); + newItem.libraryID = item.libraryID; + // DEBUG: save here because clone() doesn't currently work on unsaved tagged items + var id = newItem.save(); + + var newItem = Zotero.Items.get(id); + item.clone(false, newItem); + newItem.save(); if (this.itemsView._itemGroup.isCollection()) { this.itemsView._itemGroup.ref.addItem(newItem.id); - this.selectItem(newItemID); + this.selectItem(newItem.id); } } @@ -1070,17 +1132,23 @@ var ZoteroPane = new function() */ this.deleteSelectedItems = function (force) { if (this.itemsView && this.itemsView.selection.count > 0) { + var itemGroup = this.itemsView._itemGroup; + + if (!itemGroup.isTrash() && !this.canEdit()) { + this.displayCannotEditLibraryMessage(); + return; + } + if (!force){ - if (this.itemsView._itemGroup.isCollection()) { + if (itemGroup.isCollection()) { var noPrompt = true; } // Do nothing in search and share views - else if (this.itemsView._itemGroup.isSearch() || - this.itemsView._itemGroup.isShare()) { + else if (itemGroup.isSearch() || itemGroup.isShare()) { return; } // Do nothing in trash view if any non-deleted items are selected - else if (this.itemsView._itemGroup.isTrash()) { + else if (itemGroup.isTrash()) { var start = {}; var end = {}; for (var i=0, len=this.itemsView.selection.getRangeCount(); i 0) { var row = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); @@ -1196,7 +1274,8 @@ var ZoteroPane = new function() } } else { - var s = new Zotero.Search(row.ref.id); + var s = new Zotero.Search(); + s.id = row.ref.id; var io = {dataIn: {search: s, name: row.getName()}, dataOut: null}; window.openDialog('chrome://zotero/content/searchDialog.xul','','chrome,modal',io); if (io.dataOut) { @@ -1310,24 +1389,55 @@ var ZoteroPane = new function() function selectItem(itemID, inLibrary, expand) { if (!itemID) { - return; + return false; } - if (this.itemsView) { - if (!this.itemsView._itemGroup.isLibrary() && inLibrary) { - this.collectionsView.selection.select(0); - } - - var selected = this.itemsView.selectItem(itemID, expand); - if (!selected) { - this.collectionsView.selection.select(0); - this.itemsView.selectItem(itemID, expand); - } + var item = Zotero.Items.get(itemID); + if (!item) { + return false; } + + if (!this.itemsView) { + Components.utils.reportError("Items view not set in ZoteroPane.selectItem()"); + return false; + } + + var currentLibraryID = this.getSelectedLibraryID(); + // If in a different library + if (item.libraryID != currentLibraryID) { + this.collectionsView.selectLibrary(item.libraryID); + } + // Force switch to library view + else if (!this.itemsView._itemGroup.isLibrary() && inLibrary) { + this.collectionsView.selectLibrary(item.libraryID); + } + + var selected = this.itemsView.selectItem(itemID, expand); + if (!selected) { + this.collectionsView.selectLibrary(item.libraryID); + this.itemsView.selectItem(itemID, expand); + } + + return true; } + + this.getSelectedLibraryID = function () { + var group = this.getSelectedGroup(); + if (group) { + return group.libraryID; + } + var collection = this.getSelectedCollection(); + if (collection) { + return collection.libraryID; + } + return null; + } + + function getSelectedCollection(asID) { - if (this.collectionsView.selection + if (this.collectionsView + && this.collectionsView.selection && this.collectionsView.selection.count > 0 && this.collectionsView.selection.currentIndex != -1) { var collection = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); @@ -1335,20 +1445,10 @@ var ZoteroPane = new function() return asID ? collection.ref.id : collection.ref; } } - // If the Zotero pane hasn't yet been opened, use the lastViewedFolder pref - else { - var lastViewedFolder = Zotero.Prefs.get('lastViewedFolder'); - var matches = lastViewedFolder.match(/^(?:(C|S)([0-9]+)|L)$/); - if (matches && matches[1] == 'C') { - var col = Zotero.Collections.get(matches[2]); - if (col) { - return asID ? col.id : col; - } - } - } return false; } + function getSelectedSavedSearch(asID) { if (this.collectionsView.selection.count > 0 && this.collectionsView.selection.currentIndex != -1) { @@ -1357,20 +1457,10 @@ var ZoteroPane = new function() return asID ? collection.ref.id : collection.ref; } } - // If the Zotero pane hasn't yet been opened, use the lastViewedFolder pref - else { - var lastViewedFolder = Zotero.Prefs.get('lastViewedFolder'); - var matches = lastViewedFolder.match(/^(?:(C|S)([0-9]+)|L)$/); - if (matches && matches[1] == 'S') { - var search = Zotero.Search.get(matches[2]); - if (search) { - return asID ? search.id : search; - } - } - } return false; } + /* * Return an array of Item objects for selected items * @@ -1386,6 +1476,19 @@ var ZoteroPane = new function() } + this.getSelectedGroup = function (asID) { + if (this.collectionsView.selection + && this.collectionsView.selection.count > 0 + && this.collectionsView.selection.currentIndex != -1) { + var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); + if (itemGroup && itemGroup.isGroup()) { + return asID ? itemGroup.ref.id : itemGroup.ref; + } + } + return false; + } + + /* * Returns an array of Zotero.Item objects of visible items in current sort order * @@ -1418,10 +1521,9 @@ var ZoteroPane = new function() } - function buildCollectionContextMenu() + this.buildCollectionContextMenu = function buildCollectionContextMenu() { var menu = document.getElementById('zotero-collectionmenu'); - var m = { newCollection: 0, newSavedSearch: 1, @@ -1437,22 +1539,32 @@ var ZoteroPane = new function() emptyTrash: 11 }; + var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); + + var enable = [], disable = [], show = []; + // Collection - if (this.collectionsView.selection.count == 1 && - this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex).isCollection()) - { - var show = [m.newSubcollection, m.sep1, m.editSelectedCollection, m.removeCollection, - m.sep2, m.exportCollection, m.createBibCollection, m.loadReport]; + if (itemGroup.isCollection()) { + show = [ + m.newSubcollection, + m.sep1, + m.editSelectedCollection, + m.removeCollection, + m.sep2, + m.exportCollection, + m.createBibCollection, + m.loadReport + ]; + var s = [m.exportCollection, m.createBibCollection, m.loadReport]; if (this.itemsView.rowCount>0) { - var enable = [m.exportCollection, m.createBibCollection, m.loadReport]; + enable = s; } else if (!this.collectionsView.isContainerEmpty(this.collectionsView.selection.currentIndex)) { - var enable = [m.exportCollection]; - var disable = [m.createBibCollection, m.loadReport]; + enable = [m.exportCollection]; + disable = [m.createBibCollection, m.loadReport]; } - else - { - var disable = [m.exportCollection, m.createBibCollection, m.loadReport]; + else { + disable = s; } // Adjust labels @@ -1463,17 +1575,22 @@ var ZoteroPane = new function() menu.childNodes[m.loadReport].setAttribute('label', Zotero.getString('pane.collections.menu.generateReport.collection')); } // Saved Search - else if (this.collectionsView.selection.count == 1 && - this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex).isSearch()) { - var show = [m.editSelectedCollection, m.removeCollection, m.sep2, m.exportCollection, - m.createBibCollection, m.loadReport]; + else if (itemGroup.isSearch()) { + show = [ + m.editSelectedCollection, + m.removeCollection, + m.sep2, + m.exportCollection, + m.createBibCollection, + m.loadReport + ]; + var s = [m.exportCollection, m.createBibCollection, m.loadReport]; if (this.itemsView.rowCount>0) { - var enable = [m.exportCollection, m.createBibCollection, m.loadReport]; + enable = s; } - else - { - var disable = [m.exportCollection, m.createBibCollection, m.loadReport]; + else { + disable = s; } // Adjust labels @@ -1484,14 +1601,30 @@ var ZoteroPane = new function() menu.childNodes[m.loadReport].setAttribute('label', Zotero.getString('pane.collections.menu.generateReport.savedSearch')); } // Trash - else if (this.collectionsView.selection.count == 1 && - this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex).isTrash()) { - var show = [m.emptyTrash]; + else if (itemGroup.isTrash()) { + show = [m.emptyTrash]; + } + // Header + else if (itemGroup.isHeader()) { + + } + // Group + else if (itemGroup.isGroup()) { + show = [m.newCollection]; } // Library else { - var show = [m.newCollection, m.newSavedSearch, m.sep1, m.exportFile]; + show = [m.newCollection, m.newSavedSearch, m.sep1, m.exportFile]; + } + + // Disable some actions if user doesn't have write access + var s = [m.editSelectedCollection, m.removeCollection, m.newCollection, m.newSavedSearch]; + if (itemGroup.isGroup() && !itemGroup.ref.editable) { + disable = disable.concat(s); + } + else { + enable = enable.concat(s); } for (var i in disable) @@ -1541,7 +1674,7 @@ var ZoteroPane = new function() var enable = [], disable = [], show = [], hide = [], multiple = ''; // TODO: implement menu for remote items - if (this.itemsView.readOnly) { + if (!this.collectionsView.editable) { for each(var pos in m) { disable.push(pos); } @@ -1725,13 +1858,23 @@ var ZoteroPane = new function() } if (tree.id == 'zotero-collections-tree') { - var s = this.getSelectedSavedSearch(); - if (s) { + var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); + if (itemGroup.isSearch()) { this.editSelectedCollection(); } + else if (itemGroup.isGroup()) { + var uri = Zotero.URI.getGroupURI(itemGroup.ref, true); + window.loadURI(uri); + } + else if (itemGroup.isHeader()) { + if (itemGroup.ref.id == 'group-libraries-header') { + var uri = Zotero.URI.getGroupsURL(); + window.loadURI(uri); + } + } } else if (tree.id == 'zotero-items-tree') { - if (this.itemsView.readOnly) { + if (!this.collectionsView.editable) { return; } @@ -1893,7 +2036,7 @@ var ZoteroPane = new function() } } - var menuitem = document.getElementById("zotero-context-save-link-as-snapshot"); + var menuitem = document.getElementById("zotero-context-save-link-as-item"); if (menuitem) { if (window.gContextMenu.onLink) { menuitem.hidden = false; @@ -1904,7 +2047,7 @@ var ZoteroPane = new function() } } - var menuitem = document.getElementById("zotero-context-save-image-as-snapshot"); + var menuitem = document.getElementById("zotero-context-save-image-as-item"); if (menuitem) { // Not using window.gContextMenu.hasBGImage -- if the user wants it, // they can use the Firefox option to view and then import from there @@ -1922,26 +2065,32 @@ var ZoteroPane = new function() } - function newNote(popup, parent, text) { + this.newNote = function (popup, parent, text) { if (!Zotero.stateCheck()) { this.displayErrorMessage(true); return; } + if (!this.canEdit()) { + this.displayCannotEditLibraryMessage(); + return; + } + if (!popup) { if (!text) { text = ''; } text = Zotero.Utilities.prototype.trim(text); - var item = new Zotero.Item(false, 'note'); + var item = new Zotero.Item('note'); + item.libraryID = this.getSelectedLibraryID(); item.setNote(text); if (parent) { item.setSource(parent); } var itemID = item.save(); - if (this.itemsView && this.itemsView._itemGroup.isCollection()) { + if (!parent && this.itemsView && this.itemsView._itemGroup.isCollection()) { this.itemsView._itemGroup.ref.addItem(itemID); } @@ -1964,6 +2113,11 @@ var ZoteroPane = new function() function addTextToNote(text) { + if (!this.canEdit()) { + this.displayCannotEditLibraryMessage(); + return; + } + try { // trim text = text.replace(/^[\xA0\r\n\s]*(.*)[\xA0\r\n\s]*$/m, "$1"); @@ -1996,6 +2150,11 @@ var ZoteroPane = new function() function openNoteWindow(itemID, col, parentItemID) { + if (!this.canEdit()) { + this.displayCannotEditLibraryMessage(); + return; + } + var name = null; if (itemID) { @@ -2026,6 +2185,20 @@ var ZoteroPane = new function() function addAttachmentFromDialog(link, id) { + if (!this.canEdit()) { + this.displayCannotEditLibraryMessage(); + return; + } + + // FIXME: temporarily disable file attachment options for groups + var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); + if (itemGroup.isWithinGroup()) { + var pr = Components.classes["@mozilla.org/network/default-prompt;1"] + .getService(Components.interfaces.nsIPrompt); + pr.alert("", "Files cannot currently be added to group libraries."); + return; + } + var nsIFilePicker = Components.interfaces.nsIFilePicker; var fp = Components.classes["@mozilla.org/filepicker;1"] .createInstance(nsIFilePicker); @@ -2055,80 +2228,120 @@ var ZoteroPane = new function() } - function addItemFromPage() { + this.addItemFromPage = function (itemType) { + return this.addItemFromDocument(window.content.document, itemType); + } + + + /** + * @param {Document} doc + * @param {String|Integer} [itemType='webpage'] Item type id or name + * @param {Boolean} [saveSnapshot] Force saving of a snapshot, + * regardless of automaticSnapshots pref + */ + this.addItemFromDocument = function (doc, itemType, saveSnapshot) { if (!Zotero.stateCheck()) { this.displayErrorMessage(true); return false; } + if (!this.canEdit()) { + this.displayCannotEditLibraryMessage(); + return; + } + var progressWin = new Zotero.ProgressWindow(); progressWin.changeHeadline(Zotero.getString('ingester.scraping')); var icon = 'chrome://zotero/skin/treeitem-webpage.png'; - progressWin.addLines(window.content.document.title, icon) + progressWin.addLines(doc.title, icon) progressWin.show(); progressWin.startCloseTimer(); var data = { - title: window.content.document.title, - url: window.content.document.location.href, + title: doc.title, + url: doc.location.href, accessDate: "CURRENT_TIMESTAMP" } - var item = this.newItem(Zotero.ItemTypes.getID('webpage'), data); + // Save web page item by default + if (!itemType) { + itemType = 'webpage'; + } + itemType = Zotero.ItemTypes.getID(itemType); + var item = this.newItem(itemType, data); - // Automatically save snapshot if pref set - if (item.id && Zotero.Prefs.get('automaticSnapshots')) - { - var f = function() { - // We set |noParent|, since child items don't belong to collections - ZoteroPane.addAttachmentFromPage(false, item.id, true); + var filesEditable = false; + if (item.libraryID) { + var group = Zotero.Groups.getByLibraryID(item.libraryID); + filesEditable = group.filesEditable; + } + + // Save snapshot if explicitly enabled or automatically pref is set and not explicitly disabled + if (saveSnapshot || (saveSnapshot !== false && Zotero.Prefs.get('automaticSnapshots'))) { + var link = false; + + if (link) { + Zotero.Attachments.linkFromDocument(doc, item.id); + } + else if (filesEditable) { + Zotero.Attachments.importFromDocument(doc, item.id); } - // Give progress window time to appear - setTimeout(f, 300); } return item.id; } + this.addItemFromURL = function (url, itemType) { + if (url == window.content.document.location.href) { + return this.addItemFromPage(itemType); + } + + var processor = function (doc) { + ZoteroPane.addItemFromDocument(doc, itemType); + }; + + var done = function () {} + + var exception = function (e) { + Zotero.debug(e); + } + + Zotero.Utilities.HTTP.processDocuments([url], processor, done, exception); + } + + /* * Create an attachment from the current page * - * |link| -- create web link instead of snapshot * |itemID| -- itemID of parent item - * |noParent| -- don't add to current collection + * |link| -- create web link instead of snapshot */ - function addAttachmentFromPage(link, itemID, noParent) + this.addAttachmentFromPage = function (link, itemID) { if (!Zotero.stateCheck()) { this.displayErrorMessage(true); return; } - if (!noParent) { - var progressWin = new Zotero.ProgressWindow(); - progressWin.changeHeadline(Zotero.getString('save.' + (link ? 'link' : 'attachment'))); - var type = link ? 'web-link' : 'snapshot'; - var icon = 'chrome://zotero/skin/treeitem-attachment-' + type + '.png'; - progressWin.addLines(window.content.document.title, icon) - progressWin.show(); - progressWin.startCloseTimer(); - - if (this.itemsView && this.itemsView._itemGroup.isCollection()) { - var parentCollectionID = this.itemsView._itemGroup.ref.id; - } + if (typeof itemID != 'number') { + throw ("itemID must be an integer in ZoteroPane.addAttachmentFromPage()"); } - var f = function() { - if (link) { - Zotero.Attachments.linkFromDocument(window.content.document, itemID, parentCollectionID); - } - else { - Zotero.Attachments.importFromDocument(window.content.document, itemID, false, parentCollectionID); - } + var progressWin = new Zotero.ProgressWindow(); + progressWin.changeHeadline(Zotero.getString('save.' + (link ? 'link' : 'attachment'))); + var type = link ? 'web-link' : 'snapshot'; + var icon = 'chrome://zotero/skin/treeitem-attachment-' + type + '.png'; + progressWin.addLines(window.content.document.title, icon) + progressWin.show(); + progressWin.startCloseTimer(); + + if (link) { + Zotero.Attachments.linkFromDocument(window.content.document, itemID); + } + else { + Zotero.Attachments.importFromDocument(window.content.document, itemID); } - // Give progress window time to appear - setTimeout(f, 100); } @@ -2225,6 +2438,28 @@ var ZoteroPane = new function() } + /** + * Test if the user can edit the currently selected library/collection, + * and display an error if not + * + * @return {Boolean} TRUE if user can edit, FALSE if not + */ + this.canEdit = function () { + var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); + return itemGroup.isEditable(); + } + + + this.displayCannotEditLibraryMessage = function () { + var pr = Components.classes["@mozilla.org/network/default-prompt;1"] + .getService(Components.interfaces.nsIPrompt); + pr.alert( + Zotero.getString('general.accessDenied'), + "You cannot make changes to the currently selected library." + ); + } + + function showAttachmentNotFoundDialog(itemID, noLocate) { var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]. createInstance(Components.interfaces.nsIPromptService); @@ -2254,6 +2489,11 @@ var ZoteroPane = new function() function relinkAttachment(itemID) { + if (!this.canEdit()) { + this.displayCannotEditLibraryMessage(); + return; + } + var item = Zotero.Items.get(itemID); if (!item) { throw('Item ' + itemID + ' not found in ZoteroPane.relinkAttachment()'); diff --git a/chrome/content/zotero/overlay.xul b/chrome/content/zotero/overlay.xul index a478712bc3..435f28e6bf 100644 --- a/chrome/content/zotero/overlay.xul +++ b/chrome/content/zotero/overlay.xul @@ -60,12 +60,13 @@