diff --git a/chrome/content/zotero/overlay.js b/chrome/content/zotero/overlay.js index fcfe615eb1..72400e87d4 100644 --- a/chrome/content/zotero/overlay.js +++ b/chrome/content/zotero/overlay.js @@ -101,6 +101,9 @@ var ZoteroOverlay = new function() observerService.addObserver(zoteroObserver, "browser-delayed-startup-finished", false); + // Set a flag for hi-res displays + Zotero.hiDPI = window.devicePixelRatio > 1; + // Add a listener for toolbar change events window.addEventListener("customizationchange", onToolbarChange, false); diff --git a/chrome/content/zotero/xpcom/attachments.js b/chrome/content/zotero/xpcom/attachments.js index 4047e9eaec..415228d36c 100644 --- a/chrome/content/zotero/xpcom/attachments.js +++ b/chrome/content/zotero/xpcom/attachments.js @@ -1064,7 +1064,8 @@ Zotero.Attachments = new function(){ throw ("Attachment is already in library " + libraryID); } - var newAttachment = attachment.clone(libraryID); + attachment.loadItemData(); + var newAttachment = yield attachment.clone(libraryID); if (attachment.isImportedAttachment()) { // Attachment path isn't copied over by clone() if libraryID is different newAttachment.attachmentPath = attachment.attachmentPath; diff --git a/chrome/content/zotero/xpcom/collectionTreeView.js b/chrome/content/zotero/xpcom/collectionTreeView.js index 51ca7fb48a..68ddd339e9 100644 --- a/chrome/content/zotero/xpcom/collectionTreeView.js +++ b/chrome/content/zotero/xpcom/collectionTreeView.js @@ -42,8 +42,21 @@ Zotero.CollectionTreeView = function() this.hideSources = []; this._highlightedRows = {}; - this._unregisterID = Zotero.Notifier.registerObserver(this, ['collection', 'search', 'share', 'group', 'trash', 'bucket'], 'collectionTreeView'); + this._unregisterID = Zotero.Notifier.registerObserver( + this, + [ + 'collection', + 'search', + 'publications', + 'share', + 'group', + 'trash', + 'bucket' + ], + 'collectionTreeView' + ); this._containerState = {}; + this._publicationsRow; this._duplicateLibraries = []; this._unfiledLibraries = []; this._trashNotEmpty = {}; @@ -164,37 +177,34 @@ Zotero.CollectionTreeView.prototype.refresh = Zotero.Promise.coroutine(function* libraryID: Zotero.Libraries.userLibraryID }; - // treeRow, level, beforeRow, startOpen + // + // Add "My Library" + // + // addRow(treeRow, level, beforeRow, startOpen) this._addRow(newRows, new Zotero.CollectionTreeRow('library', library), 0, 1); yield this._expandRow(newRows, 0); + + // Add "My Publications" + this._addRow(newRows, new Zotero.CollectionTreeRow('separator', false)); + this._addRow(newRows, new Zotero.CollectionTreeRow('publications', { + libraryID: Zotero.Libraries.publicationsLibraryID + })); + this._publicationsRow = newRows.length - 1; + + // Add groups var groups = yield Zotero.Groups.getAll(); if (groups.length) { this._addRow(newRows, new Zotero.CollectionTreeRow('separator', false)); - var header = { + var row = this._addRow(newRows, new Zotero.CollectionTreeRow('header', { id: "group-libraries-header", label: Zotero.getString('pane.collections.groupLibraries'), - libraryID: -1, - expand: Zotero.Promise.coroutine(function* (rows, beforeRow, groups) { - if (!groups) { - groups = yield Zotero.Groups.getAll(); - } - var newRows = 0; - for (var i = 0, len = groups.length; i < len; i++) { - var row = self._addRow( - rows, - new Zotero.CollectionTreeRow('group', groups[i]), - 1, - beforeRow ? beforeRow + newRows : null - ); - newRows += 1 + ( yield self._expandRow(rows, row) ); - } - return newRows; - }) - } - var row = this._addRow(newRows, new Zotero.CollectionTreeRow('header', header)); - if (this._containerState.HG) { - newRows[row][1] = true; - yield header.expand(newRows, null, groups); + libraryID: -1 + }, 0)); + for (let i = 0, len = groups.length; i < len; i++) { + var row = this._addRow( + newRows, + new Zotero.CollectionTreeRow('group', groups[i]) + ); } } @@ -325,7 +335,8 @@ Zotero.CollectionTreeView.prototype.notify = Zotero.Promise.coroutine(function* this.rememberSelection(savedSelection); } else if (action == 'modify' || action == 'refresh') { - if (type != 'bucket') { + if (type != 'bucket' + && (type != 'publications' || this.selectedTreeRow.isPublications())) { yield this.reload(); } this.rememberSelection(savedSelection); @@ -392,10 +403,21 @@ Zotero.CollectionTreeView.prototype.setHighlightedRows = Zotero.Promise.coroutin this._highlightedRows = {}; this._treebox.invalidate(); - for each(var id in ids) { - yield this.expandToCollection(id); - this._highlightedRows[this._collectionRowMap[id]] = true; - this._treebox.invalidateRow(this._collectionRowMap[id]); + if (!ids) return; + for (let id of ids) { + var row = null; + if (id[0] == 'C') { + id = id.substr(1); + yield this.expandToCollection(id); + row = this._collectionRowMap[id]; + } + else if (id == 'P') { + row = this._publicationsRow; + } + if (row) { + this._highlightedRows[row] = true; + this._treebox.invalidateRow(row); + } } }); @@ -429,6 +451,8 @@ Zotero.CollectionTreeView.prototype.getCellText = function(row, column) Zotero.CollectionTreeView.prototype.getImageSrc = function(row, col) { + var suffix = Zotero.hiDPI ? "@2x" : ""; + var treeRow = this.getRow(row); var collectionType = treeRow.type; @@ -467,6 +491,9 @@ Zotero.CollectionTreeView.prototype.getImageSrc = function(row, col) case 'collection': case 'search': return "chrome://zotero-platform/content/treesource-" + collectionType + ".png"; + + case 'publications': + return "chrome://zotero/skin/treeitem-journalArticle" + suffix + ".png"; } return "chrome://zotero/skin/treesource-" + collectionType + ".png"; @@ -475,7 +502,7 @@ Zotero.CollectionTreeView.prototype.getImageSrc = function(row, col) Zotero.CollectionTreeView.prototype.isContainer = function(row) { var treeRow = this.getRow(row); - return treeRow.isLibrary(true) || treeRow.isCollection() || treeRow.isHeader() || treeRow.isBucket(); + return treeRow.isLibrary(true) || treeRow.isCollection() || treeRow.isPublications() || treeRow.isBucket(); } Zotero.CollectionTreeView.prototype.isContainerOpen = function(row) @@ -492,9 +519,6 @@ Zotero.CollectionTreeView.prototype.isContainerEmpty = function(row) if (treeRow.isLibrary()) { return false; } - if (treeRow.isHeader()) { - return false; - } if (treeRow.isBucket()) { return true; } @@ -559,10 +583,7 @@ Zotero.CollectionTreeView.prototype.toggleOpenState = Zotero.Promise.coroutine(f } else { var treeRow = this.getRow(row); - if (treeRow.type == 'header') { - count = yield treeRow.ref.expand(this._rows, row + 1); - } - else if (treeRow.isLibrary(true) || treeRow.isCollection()) { + if (treeRow.isLibrary(true) || treeRow.isCollection()) { count = yield this._expandRow(this._rows, row, true); } this.rowCount += count; @@ -581,8 +602,9 @@ Zotero.CollectionTreeView.prototype.toggleOpenState = Zotero.Promise.coroutine(f Zotero.CollectionTreeView.prototype.isSelectable = function (row, col) { var treeRow = this.getRow(row); switch (treeRow.type) { - case 'separator': - return false; + case 'separator': + case 'header': + return false; } return true; } @@ -741,14 +763,6 @@ Zotero.CollectionTreeView.prototype.selectLibrary = Zotero.Promise.coroutine(fun // Find library for (var i = 0; i < this.rowCount; i++) { var treeRow = this.getRow(i); - - // If group header is closed, open it - if (treeRow.isHeader() && treeRow.ref.id == 'group-libraries-header' - && !this.isContainerOpen(i)) { - yield this.toggleOpenState(i); - continue; - } - if (treeRow.ref && treeRow.ref.libraryID == libraryID) { this._treebox.ensureRowIsVisible(i); this.selection.select(i); @@ -900,6 +914,10 @@ Zotero.CollectionTreeView.prototype._expandRow = Zotero.Promise.coroutine(functi var isCollection = treeRow.isCollection(); var libraryID = treeRow.ref.libraryID; + if (treeRow.isPublications()) { + return false; + } + if (isGroup) { var group = yield Zotero.Groups.getByLibraryID(libraryID); var collections = yield group.getCollections(); @@ -1259,7 +1277,6 @@ Zotero.CollectionTreeView.prototype.canDropCheck = function (row, orient, dataTr var ids = data; var items = Zotero.Items.get(ids); var skip = true; - Zotero.debug(ids); for each(var item in items) { // Can only drag top-level items if (!item.isTopLevelItem()) { @@ -1281,6 +1298,15 @@ Zotero.CollectionTreeView.prototype.canDropCheck = function (row, orient, dataTr continue; } + if (treeRow.isPublications() && treeRow.ref.libraryID != item.libraryID) { + if (item.isAttachment() || item.isNote()) { + Zotero.debug("Standalone attachments and notes cannot be added to My Publications"); + return false; + } + skip = false; + continue; + } + // Cross-library drag if (treeRow.ref.libraryID != item.libraryID) { // Only allow cross-library drag to root library and collections @@ -1313,7 +1339,7 @@ Zotero.CollectionTreeView.prototype.canDropCheck = function (row, orient, dataTr return true; } else if (dataType == 'text/x-moz-url' || dataType == 'application/x-moz-file') { - if (treeRow.isSearch()) { + if (treeRow.isSearch() || treeRow.isPublications()) { return false; } if (dataType == 'application/x-moz-file') { @@ -1330,6 +1356,10 @@ Zotero.CollectionTreeView.prototype.canDropCheck = function (row, orient, dataTr return true; } else if (dataType == 'zotero/collection') { + if (treeRow.isPublications()) { + return false; + } + let draggedCollectionID = data[0]; let draggedCollection = Zotero.Collections.get(draggedCollectionID); @@ -1400,16 +1430,22 @@ Zotero.CollectionTreeView.prototype.canDropCheckAsync = Zotero.Promise.coroutine if (linkedItem && !linkedItem.deleted) { // For drag to root, skip if linked item exists if (treeRow.isLibrary(true)) { + Zotero.debug("Linked item " + linkedItem.key + " already exists " + + "in library " + treeRow.ref.libraryID); continue; } // For drag to collection else if (treeRow.isCollection()) { // skip if linked item is already in it if (treeRow.ref.hasItem(linkedItem.id)) { + Zotero.debug("Linked item " + linkedItem.key + " already exists " + + "in collection"); continue; } // or if linked item is a child item else if (!linkedItem.isTopLevelItem()) { + Zotero.debug("Linked item " + linkedItem.key + " already exists " + + "as child item"); continue; } } @@ -1420,8 +1456,7 @@ Zotero.CollectionTreeView.prototype.canDropCheckAsync = Zotero.Promise.coroutine // Intra-library drag - // Make sure there's at least one item that's not already - // in this collection + // Make sure there's at least one item that's not already in this destination if (treeRow.isCollection()) { if (treeRow.ref.hasItem(item.id)) { Zotero.debug("Item " + item.id + " already exists in collection"); @@ -1563,7 +1598,7 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r } // Create new clone item in target library - var newItem = item.clone(targetLibraryID, false, !Zotero.Prefs.get('groups.copyTags')); + var newItem = yield item.clone(targetLibraryID, false, !Zotero.Prefs.get('groups.copyTags')); var newItemID = yield newItem.save(); // Record link @@ -1577,10 +1612,11 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r // Child notes if (Zotero.Prefs.get('groups.copyChildNotes')) { + yield item.loadChildItems(); var noteIDs = item.getNotes(); var notes = yield Zotero.Items.getAsync(noteIDs); for each(var note in notes) { - let newNote = note.clone(targetLibraryID); + let newNote = yield note.clone(targetLibraryID); newNote.parentID = newItemID; yield newNote.save() @@ -1617,11 +1653,11 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r } } - Zotero.Attachments.copyAttachmentToLibrary(attachment, targetLibraryID, newItem.id); + Zotero.Attachments.copyAttachmentToLibrary(attachment, targetLibraryID, newItemID); } } - return newID; + return newItemID; }); var targetLibraryID = targetTreeRow.ref.libraryID; @@ -1791,9 +1827,7 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r // Add items to target collection if (targetCollectionID) { var collection = yield Zotero.Collections.getAsync(targetCollectionID); - Zotero.debug('adding'); yield collection.addItems(newIDs); - Zotero.debug('added'); } // If moving, remove items from source collection @@ -1902,24 +1936,34 @@ Zotero.CollectionTreeView.prototype.isSorted = function() { return false; Zotero.CollectionTreeView.prototype.getRowProperties = function(row, prop) { var props = []; - if (this._highlightedRows[row]) { - // <=Fx21 - if (prop) { - var aServ = Components.classes["@mozilla.org/atom-service;1"]. - getService(Components.interfaces.nsIAtomService); - prop.AppendElement(aServ.getAtom("highlighted")); - } - // Fx22+ - else { - props.push("highlighted"); - } + var treeRow = this.getRow(row); + if (treeRow.isHeader()) { + props.push("header"); + } + else if (this._highlightedRows[row]) { + props.push("highlighted"); } return props.join(" "); } -Zotero.CollectionTreeView.prototype.getColumnProperties = function(col, prop) { } -Zotero.CollectionTreeView.prototype.getCellProperties = function(row, col, prop) { } +Zotero.CollectionTreeView.prototype.getColumnProperties = function(col, prop) {} + +Zotero.CollectionTreeView.prototype.getCellProperties = function(row, col, prop) { + var props = []; + + var treeRow = this.getRow(row); + if (treeRow.isHeader()) { + props.push("header"); + props.push("notwisty"); + } + else if (treeRow.isPublications()) { + props.push("notwisty"); + } + + return props.join(" "); + +} Zotero.CollectionTreeView.prototype.isSeparator = function(index) { var source = this.getRow(index); return source.type == 'separator'; @@ -1965,6 +2009,9 @@ Zotero.CollectionTreeRow.prototype.__defineGetter__('id', function () { case 'search': return 'S' + this.ref.id; + case 'publications': + return 'P'; + case 'duplicates': return 'D' + this.ref.libraryID; @@ -1990,7 +2037,7 @@ Zotero.CollectionTreeRow.prototype.__defineGetter__('id', function () { Zotero.CollectionTreeRow.prototype.isLibrary = function (includeGlobal) { if (includeGlobal) { - return this.type == 'library' || this.type == 'group'; + return this.type == 'library' || this.type == 'publications' || this.type == 'group'; } return this.type == 'library'; } @@ -2022,6 +2069,10 @@ Zotero.CollectionTreeRow.prototype.isHeader = function () { return this.type == 'header'; } +Zotero.CollectionTreeRow.prototype.isPublications = function() { + return this.type == 'publications'; +} + Zotero.CollectionTreeRow.prototype.isGroup = function() { return this.type == 'group'; } @@ -2059,7 +2110,7 @@ Zotero.CollectionTreeRow.prototype.__defineGetter__('editable', function () { if (this.isTrash() || this.isShare() || this.isBucket()) { return false; } - if (!this.isWithinGroup()) { + if (this.isPublications || !this.isWithinGroup()) { return true; } var libraryID = this.ref.libraryID; @@ -2107,6 +2158,9 @@ Zotero.CollectionTreeRow.prototype.getName = function() case 'library': return Zotero.getString('pane.collections.library'); + case 'publications': + return Zotero.getString('pane.collections.publications'); + case 'trash': return Zotero.getString('pane.collections.trash'); @@ -2130,9 +2184,6 @@ Zotero.CollectionTreeRow.prototype.getItems = Zotero.Promise.coroutine(function* case 'bucket': return this.ref.getItems(); - - case 'header': - return []; } var ids = yield this.getSearchResults(); @@ -2220,7 +2271,7 @@ Zotero.CollectionTreeRow.prototype.getSearchObject = Zotero.Promise.coroutine(fu yield s.addCondition('deleted', 'true'); } else { - throw ('Invalid search mode in Zotero.CollectionTreeRow.getSearchObject()'); + throw new Error('Invalid search mode in Zotero.CollectionTreeRow.getSearchObject()'); } } @@ -2263,9 +2314,6 @@ Zotero.CollectionTreeRow.prototype.getChildTags = Zotero.Promise.method(function case 'bucket': return false; - - case 'header': - return false; } return Zotero.Tags.getAllWithinSearchResults(this.getSearchResults(true)); diff --git a/chrome/content/zotero/xpcom/data/collection.js b/chrome/content/zotero/xpcom/data/collection.js index ecff518350..725fff8ebe 100644 --- a/chrome/content/zotero/xpcom/data/collection.js +++ b/chrome/content/zotero/xpcom/data/collection.js @@ -606,7 +606,10 @@ Zotero.Collection.prototype.erase = function(deleteItems) { return Zotero.DB.executeTransaction(function* () { var descendents = yield this.getDescendents(false, null, true); var items = []; - notifierData[this.id] = { old: this.toJSON() }; + notifierData[this.id] = { + libraryID: this.libraryID, + key: this.key + }; var del = []; for(var i=0, len=descendents.length; i { + Zotero.debug(e); return this._eraseRecoverFromFailure(env); }); diff --git a/chrome/content/zotero/xpcom/data/item.js b/chrome/content/zotero/xpcom/data/item.js index 9480ad86cb..cb3eae3b3b 100644 --- a/chrome/content/zotero/xpcom/data/item.js +++ b/chrome/content/zotero/xpcom/data/item.js @@ -77,6 +77,7 @@ Zotero.Item = function(itemTypeOrID) { this._fileExists = null; this._deleted = null; + this._publication = null; this._hasNote = null; this._noteAccessTime = null; @@ -1037,28 +1038,35 @@ Zotero.Item.prototype.removeCreator = function(orderIndex, allowMissing) { return true; } -Zotero.defineProperty(Zotero.Item.prototype, 'deleted', { - get: function() { - if (!this.id) { - return false; + +// Define 'deleted' and 'publication' properties +for (let name of ['deleted', 'publication']) { + let prop = '_' + name; + + Zotero.defineProperty(Zotero.Item.prototype, name, { + get: function() { + if (!this.id) { + return false; + } + if (this[prop] !== null) { + return this[prop]; + } + this._requireData('primaryData'); + }, + set: function(val) { + val = !!val; + + if (this[prop] == val) { + Zotero.debug(Zotero.Utilities.capitalize(name) + + " state hasn't changed for item " + this.id); + return; + } + this._markFieldChange('publication', !!this[prop]); + this._changed[name] = true; + this[prop] = val; } - if (this._deleted !== null) { - return this._deleted; - } - this._requireData('primaryData'); - }, - set: function(val) { - var deleted = !!val; - - if (this._deleted == deleted) { - Zotero.debug("Deleted state hasn't changed for item " + this.id); - return; - } - this._markFieldChange('deleted', !!this._deleted); - this._changed.deleted = true; - this._deleted = deleted; - } -}); + }); +} Zotero.Item.prototype.addRelatedItem = Zotero.Promise.coroutine(function* (itemID) { @@ -1407,7 +1415,7 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) { // Trashed status if (this._changed.deleted) { - if (this.deleted) { + if (this._deleted) { sql = "REPLACE INTO deletedItems (itemID) VALUES (?)"; } else { @@ -3710,14 +3718,14 @@ Zotero.Item.prototype.multiDiff = Zotero.Promise.coroutine(function* (otherItems * @param {Number} [libraryID] - libraryID of the new item, or the same as original if omitted * @param {Boolean} [skipTags=false] - Skip tags */ -Zotero.Item.prototype.clone = function(libraryID, skipTags) { +Zotero.Item.prototype.clone = Zotero.Promise.coroutine(function* (libraryID, skipTags) { Zotero.debug('Cloning item ' + this.id); if (libraryID !== undefined && libraryID !== null && typeof libraryID !== 'number') { throw new Error("libraryID must be null or an integer"); } - this._requireData('primaryData'); + yield this.loadPrimaryData(); if (libraryID === undefined || libraryID === null) { libraryID = this.libraryID; @@ -3728,6 +3736,7 @@ Zotero.Item.prototype.clone = function(libraryID, skipTags) { newItem.libraryID = libraryID; newItem.setType(this.itemTypeID); + yield this.loadItemData(); var fieldIDs = this.getUsedFields(); for (let i = 0; i < fieldIDs.length; i++) { let fieldID = fieldIDs[i]; @@ -3736,9 +3745,11 @@ Zotero.Item.prototype.clone = function(libraryID, skipTags) { // Regular item if (this.isRegularItem()) { + yield this.loadCreators(); newItem.setCreators(newItem.getCreators()); } else { + yield this.loadNote(); newItem.setNote(this.getNote()); if (sameLibrary) { var parent = this.parentKey; @@ -3763,16 +3774,18 @@ Zotero.Item.prototype.clone = function(libraryID, skipTags) { } if (!skipTags) { + yield this.loadTags(); newItem.setTags(this.getTags()); } if (sameLibrary) { // DEBUG: this will add reverse-only relateds too + yield this.loadRelations(); newItem.setRelations(this.getRelations()); } return newItem; -} +}); /** @@ -3791,7 +3804,10 @@ Zotero.Item.prototype._eraseInit = Zotero.Promise.coroutine(function* (env) { if (!proceed) return false; env.deletedItemNotifierData = {}; - env.deletedItemNotifierData[this.id] = { old: this.toJSON() }; + env.deletedItemNotifierData[this.id] = { + libraryID: this.libraryID, + key: this.key + }; return true; }); @@ -4572,9 +4588,9 @@ Zotero.Item.prototype._getRelatedItems = function () { // Pull out object values from related-item relations, turn into items, and pull out keys var keys = []; for (let i=0; i'; content += escapeXML(relItem.getDisplayTitle()); diff --git a/chrome/content/zotero/xpcom/schema.js b/chrome/content/zotero/xpcom/schema.js index 81375c0284..5f1173f067 100644 --- a/chrome/content/zotero/xpcom/schema.js +++ b/chrome/content/zotero/xpcom/schema.js @@ -1420,6 +1420,7 @@ Zotero.Schema = new function(){ yield _updateDBVersion('compatibility', _maxCompatibility); yield Zotero.DB.queryAsync("INSERT INTO libraries (libraryID, libraryType) VALUES (?, 'user')", userLibraryID); + yield Zotero.DB.queryAsync("INSERT INTO libraries (libraryID, libraryType) VALUES (2, 'publications')"); if (!Zotero.Schema.skipDefaultData) { // Quick Start Guide web page item @@ -1775,6 +1776,7 @@ Zotero.Schema = new function(){ yield _updateDBVersion('compatibility', 1); yield Zotero.DB.queryAsync("INSERT INTO libraries VALUES (1, 'user')"); + yield Zotero.DB.queryAsync("INSERT INTO libraries VALUES (2, 'publications')"); let oldUserLibraryID = yield Zotero.DB.valueQueryAsync("SELECT value FROM settings WHERE setting='account' AND key='libraryID'"); diff --git a/chrome/content/zotero/xpcom/search.js b/chrome/content/zotero/xpcom/search.js index c2e4bd451e..f30c89c1b1 100644 --- a/chrome/content/zotero/xpcom/search.js +++ b/chrome/content/zotero/xpcom/search.js @@ -1007,7 +1007,6 @@ Zotero.Search.prototype._buildQuery = Zotero.Promise.coroutine(function* () { continue; case 'unfiled': - this._conditions[i]['operator'] var unfiled = this._conditions[i]['operator'] == 'true'; continue; @@ -1704,7 +1703,10 @@ Zotero.Searches = function() { search.id = id; yield search.loadPrimaryData(); yield search.loadConditions(); - notifierData[id] = { old: search.serialize() }; + notifierData[id] = { + libraryID: this.libraryID, + old: search.serialize() // TODO: replace with toJSON() + }; var sql = "DELETE FROM savedSearchConditions WHERE savedSearchID=?"; yield Zotero.DB.queryAsync(sql, id); @@ -1712,7 +1714,7 @@ Zotero.Searches = function() { var sql = "DELETE FROM savedSearches WHERE savedSearchID=?"; yield Zotero.DB.queryAsync(sql, id); } - }); + }.bind(this)); Zotero.Notifier.trigger('delete', 'search', ids, notifierData); }); @@ -1784,8 +1786,6 @@ Zotero.SearchConditions = new function(){ // // Special conditions // - - { name: 'deleted', operators: { diff --git a/chrome/content/zotero/xpcom/storage/queueManager.js b/chrome/content/zotero/xpcom/storage/queueManager.js index 24cc3f6e00..43eebcda3b 100644 --- a/chrome/content/zotero/xpcom/storage/queueManager.js +++ b/chrome/content/zotero/xpcom/storage/queueManager.js @@ -301,14 +301,14 @@ Zotero.Sync.Storage.QueueManager = new function () { } - function _reconcileConflicts(conflicts) { + var _reconcileConflicts = Zotero.Promise.coroutine(function* (conflicts) { var objectPairs = []; for each(var conflict in conflicts) { var item = Zotero.Sync.Storage.getItemFromRequestName(conflict.name); - var item1 = item.clone(false, false, true); + var item1 = yield item.clone(false, false, true); item1.setField('dateModified', Zotero.Date.dateToSQL(new Date(conflict.localData.modTime), true)); - var item2 = item.clone(false, false, true); + var item2 = yield item.clone(false, false, true); item2.setField('dateModified', Zotero.Date.dateToSQL(new Date(conflict.remoteData.modTime), true)); objectPairs.push([item1, item2]); @@ -342,7 +342,7 @@ Zotero.Sync.Storage.QueueManager = new function () { } return io.dataOut; - } + }); function _processMergeData(data) { diff --git a/chrome/content/zotero/xpcom/uri.js b/chrome/content/zotero/xpcom/uri.js index 29241fc35c..ed87f70fe0 100644 --- a/chrome/content/zotero/xpcom/uri.js +++ b/chrome/content/zotero/xpcom/uri.js @@ -86,10 +86,15 @@ Zotero.URI = new function () { switch (libraryType) { case 'user': + case 'publications': var id = Zotero.Users.getCurrentUserID(); if (!id) { throw new Exception("User id not available in Zotero.URI.getLibraryPath()"); } + + if (libraryType == 'publications') { + return "users/" + id + "/publications"; + } break; case 'group': @@ -171,49 +176,92 @@ Zotero.URI = new function () { * * @param {String} itemURI * @param {Zotero.Item|FALSE} + * @return {Promise} */ - this.getURIItem = function (itemURI) { + this.getURIItem = Zotero.Promise.method(function (itemURI) { + var {libraryID, key} = this._getURIObject(itemURI, 'item'); + if (!key) return false; + return Zotero.Items.getByLibraryAndKeyAsync(libraryID, key); + }); + + + /** + * @param {String} itemURI + * @return {Object|FALSE} - Object with 'libraryID' and 'key', or FALSE if item not found + */ + this.getURIItemLibraryKey = function (itemURI) { return this._getURIObject(itemURI, 'item'); } + /** + * @param {String} itemURI + * @return {Integer|FALSE} - itemID of matching item, or FALSE if none + */ + this.getURIItemID = function (itemURI) { + var {libraryID, key} = this._getURIObject(itemURI, 'item'); + if (!key) return false; + return Zotero.Items.getIDFromLibraryAndKey(libraryID, key); + } + + /** * Convert a collection URI into a collection * * @param {String} collectionURI * @param {Zotero.Collection|FALSE} + * @return {Promise} */ - this.getURICollection = function (collectionURI) { + this.getURICollection = Zotero.Promise.method(function (collectionURI) { + var {libraryID, key} = this._getURIObject(collectionURI, 'collection'); + if (!key) return false; + return Zotero.Collections.getByLibraryAndKeyAsync(libraryID, key); + }); + + + /** + * @param {String} collectionURI + * @return {Object|FALSE} - Object with 'libraryID' and 'key', or FALSE if item not found + */ + this.getURICollectionLibraryKey = function (collectionURI) { return this._getURIObject(collectionURI, 'collection'); } + /** + * @param {String} collectionURI + * @return {Integer|FALSE} - collectionID of matching collection, or FALSE if none + */ + this.getURICollectionID = function (collectionURI) { + var {libraryID, key} = this._getURIObject(collectionURI, 'item'); + if (!key) return false; + return Zotero.Collections.getIDFromLibraryAndKey(libraryID, key); + } + + /** * Convert a library URI into a libraryID * * @param {String} libraryURI - * @return {Zotero.Collection|FALSE} + * @return {Integer|FALSE} - libraryID, or FALSE if no matching library */ this.getURILibrary = function (libraryURI) { - return this._getURIObject(libraryURI, "library"); + var {libraryID} = this._getURIObject(libraryURI, "library"); + return libraryID !== undefined ? libraryID : false; } /** * Convert an object URI into an object (item, collection, etc.) * - * @param {String} objectURI - * @param {"item"|"collection"|"library"} [type] The type of object to return. If the object - * is valid but not available, returns "false". Note that if type is "library", this - * this function may return null for the default library, which is distinct from false. - * - * @return {Zotero.Item|Zotero.Collection|Integer|NULL|FALSE} + * @param {String} objectURI + * @param {'library'|'collection'|'item'} - The type of URI to expect + * @return {Object|FALSE} - An object containing 'libraryID' and, if applicable, 'key', + * or FALSE if library not found */ this._getURIObject = function (objectURI, type) { - var Types = type[0].toUpperCase() + type.substr(1) + 's'; - var types = Types.toLowerCase(); - - var libraryType = null; + var libraryType; + var libraryTypeID; // If this is a local URI, compare to the local user key if (objectURI.match(/\/users\/local\//)) { @@ -229,72 +277,82 @@ Zotero.URI = new function () { } } */ - var libraryType = 'user'; - var id = null; + libraryType = 'user'; + libraryTypeID = null; } // If not found, try global URI if (!libraryType) { - if (objectURI.indexOf(_baseURI) != 0) { - throw ("Invalid base URI '" + objectURI + "' in Zotero.URI._getURIObject()"); + if (!objectURI.startsWith(_baseURI)) { + throw new Error("Invalid base URI '" + objectURI + "'"); } objectURI = objectURI.substr(_baseURI.length); - var typeRE = /^(users|groups)\/([0-9]+)(?:\/|$)/; - var matches = objectURI.match(typeRE); + let typeRE = /^(users|groups)\/([0-9]+)(?:\/|$)/; + let matches = objectURI.match(typeRE); if (!matches) { - throw ("Invalid library URI '" + objectURI + "' in Zotero.URI._getURIObject()"); + throw new Error("Invalid library URI '" + objectURI + "'"); } - var libraryType = matches[1].substr(0, matches[1].length-1); - var id = matches[2]; + libraryType = matches[1].substr(0, matches[1].length-1); + libraryTypeID = matches[2]; objectURI = objectURI.replace(typeRE, ''); } - if (libraryType == 'group') { - if (!Zotero.Groups.get(id)) { + if (libraryType == 'user' && objectURI.startsWith('publications/')) { + libraryType = 'publications'; + } + + if (libraryType == 'user') { + var libraryID = Zotero.Libraries.userLibraryID; + } + else if (libraryType == 'group') { + if (!Zotero.Groups.exists(libraryTypeID)) { return false; } - var libraryID = Zotero.Groups.getLibraryIDFromGroupID(id); + var libraryID = Zotero.Groups.getLibraryIDFromGroupID(libraryTypeID); + } + else if (libraryType == 'publications') { + var libraryID = Zotero.Libraries.publicationsLibraryID; } if(type === 'library') { if (libraryType == 'user') { - if(id === null) { + if (libraryTypeID) { + if (libraryTypeID == Zotero.Users.getCurrentUserID()) { + return { + libraryID: libraryID + }; + } + } + else { var localUserURI = this.getLocalUserURI(); if (localUserURI) { localUserURI += "/"; - if (objectURI.indexOf(localUserURI) == 0) { - objectURI = objectURI.substr(localUserURI.length); - return null; + if (objectURI.startsWith(localUserURI)) { + return { + libraryID: Zotero.Libraries.userLibraryID + }; } } - } else { - if(id == Zotero.Users.getCurrentUserID()) { - return null; - } } - return false; } if (libraryType == 'group') { - return libraryID; + return { + libraryID: libraryID + }; } } else { - // TODO: objectID-based URI? - var re = new RegExp(types + "\/([A-Z0-9]{8})"); + var re = /(?:items|collections)\/([A-Z0-9]{8})/; var matches = objectURI.match(re); if (!matches) { throw ("Invalid object URI '" + objectURI + "' in Zotero.URI._getURIObject()"); } - var objectKey = matches[1]; - - if (libraryType == 'user') { - return Zotero[Types].getByLibraryAndKey(null, objectKey); - } - - if (libraryType == 'group') { - return Zotero[Types].getByLibraryAndKey(libraryID, objectKey); - } + let objectKey = matches[1]; + return { + libraryID: libraryID, + key: objectKey + }; } } } diff --git a/chrome/content/zotero/xpcom/utilities.js b/chrome/content/zotero/xpcom/utilities.js index a3b1571974..7e4c468522 100644 --- a/chrome/content/zotero/xpcom/utilities.js +++ b/chrome/content/zotero/xpcom/utilities.js @@ -847,6 +847,10 @@ Zotero.Utilities = { return newString; }, + "capitalize": function (str) { + return str[0].toUpperCase() + str.substr(1); + }, + /** * Replaces accented characters in a string with ASCII equivalents * diff --git a/chrome/content/zotero/xpcom/zotero.js b/chrome/content/zotero/xpcom/zotero.js index d3d8612e65..a91f9fc02e 100644 --- a/chrome/content/zotero/xpcom/zotero.js +++ b/chrome/content/zotero/xpcom/zotero.js @@ -2079,7 +2079,7 @@ Components.utils.import("resource://gre/modules/osfile.jsm"); //Zotero.Fulltext.purgeUnusedWords(); yield Zotero.Items.purge(); // DEBUG: this might not need to be permanent - Zotero.Relations.purge(); + yield Zotero.Relations.purge(); yield Zotero.CharacterSets.purge(); }); @@ -2679,7 +2679,7 @@ Zotero.DragDrop = { getDragSource: function (dataTransfer) { if (!dataTransfer) { - Zotero.debug("Drag data not available", 2); + //Zotero.debug("Drag data not available", 2); return false; } diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js index 437716f21b..158e5f9e3f 100644 --- a/chrome/content/zotero/zoteroPane.js +++ b/chrome/content/zotero/zoteroPane.js @@ -523,13 +523,17 @@ var ZoteroPane = new function() function setHighlightedRowsCallback() { var itemIDs = ZoteroPane_Local.getSelectedItems(true); if (itemIDs && itemIDs.length) { - Zotero.Collections.getCollectionsContainingItems(itemIDs, true) - .then(function (collectionIDs) { - if (collectionIDs) { - ZoteroPane_Local.collectionsView.setHighlightedRows(collectionIDs); + Zotero.Promise.coroutine(function* () { + var collectionIDs = yield Zotero.Collections.getCollectionsContainingItems(itemIDs, true); + var ids = collectionIDs.map(id => "C" + id); + Zotero.debug(Zotero.Items.get(itemIDs).some(item => !item.publication)); + if (!Zotero.Items.get(itemIDs).some(item => !item.publication)) { + ids.push("P"); } - }) - .done(); + if (ids.length) { + ZoteroPane_Local.collectionsView.setHighlightedRows(ids); + } + })(); } } @@ -1543,7 +1547,7 @@ var ZoteroPane = new function() var id = yield newItem.save(); var newItem = yield Zotero.Items.getAsync(id); - item.clone(false, newItem, false, !Zotero.Prefs.get('groups.copyTags')); + yield item.clone(false, newItem, false, !Zotero.Prefs.get('groups.copyTags')); yield newItem.save(); if (self.collectionsView.selectedTreeRow.isCollection() && newItem.isTopLevelItem()) { @@ -1591,7 +1595,10 @@ var ZoteroPane = new function() ) }; - if (collectionTreeRow.isLibrary(true)) { + if (collectionTreeRow.isPublications()) { + var prompt = toDelete; + } + else if (collectionTreeRow.isLibrary(true)) { // In library, don't prompt if meta key was pressed var prompt = (force && !fromMenu) ? false : toTrash; } diff --git a/chrome/locale/en-US/zotero/zotero.properties b/chrome/locale/en-US/zotero/zotero.properties index fbeb3c383d..a1c21bc289 100644 --- a/chrome/locale/en-US/zotero/zotero.properties +++ b/chrome/locale/en-US/zotero/zotero.properties @@ -163,6 +163,7 @@ pane.collections.newSavedSeach = New Saved Search pane.collections.savedSearchName = Enter a name for this saved search: pane.collections.rename = Rename collection: pane.collections.library = My Library +pane.collections.publications = My Publications pane.collections.groupLibraries = Group Libraries pane.collections.trash = Trash pane.collections.untitled = Untitled diff --git a/chrome/skin/default/zotero/overlay.css b/chrome/skin/default/zotero/overlay.css index 98622bb423..43fd8d9f85 100644 --- a/chrome/skin/default/zotero/overlay.css +++ b/chrome/skin/default/zotero/overlay.css @@ -10,6 +10,12 @@ overflow: hidden; } +/* Why is this necessary? */ +#zotero-collections-tree treechildren::-moz-tree-image, +#zotero-items-tree treechildren::-moz-tree-image { + margin-right: 5px; +} + #zotero-collections-pane { min-width: 150px; @@ -20,17 +26,17 @@ min-height: 5.2em; } -#zotero-collections-tree treechildren::-moz-tree-image(primary) -{ - margin-right: 5px; +#zotero-collections-tree treechildren::-moz-tree-row { + height: 1.7em; } -#zotero-collections-tree #zotero-collections-sync-status-column { - width: 35px; -} +/*#zotero-collections-tree treechildren::-moz-tree-separator { + border: none; +}*/ -#zotero-collections-tree[hidevscroll] #zotero-collections-sync-status-column { - width: 21px; +#zotero-collections-tree treechildren::-moz-tree-twisty(notwisty), +#zotero-collections-tree treechildren::-moz-tree-twisty(header) { + width: 0; } /* Set by setHighlightedRows() and getRowProperties() in collectionTreeView.js) */ @@ -39,11 +45,6 @@ background: #FFFF99 !important; } -/*#zotero-collections-tree treechildren::-moz-tree-row(separator) -{ - height: .25em; -}*/ - #zotero-pane splitter { border: 0; @@ -60,11 +61,6 @@ text-align: center; } -#zotero-items-tree treechildren::-moz-tree-image -{ - margin-right: 5px; -} - #zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie) { margin: 1px 0 0; diff --git a/chrome/skin/default/zotero/treeitem-journalArticle@2x.png b/chrome/skin/default/zotero/treeitem-journalArticle@2x.png new file mode 100644 index 0000000000..f5ff9572af Binary files /dev/null and b/chrome/skin/default/zotero/treeitem-journalArticle@2x.png differ