www

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

commit 2d46e3d59b5efa81ff582723a4530f5c556cb565
parent e7f568d56c7834f0aed0d873d93c93edd126eedc
Author: Aurimas Vinckevicius <aurimas.dev@gmail.com>
Date:   Tue,  3 Feb 2015 11:57:32 -0600

Various feeds changes

Diffstat:
Mchrome/content/zotero/xpcom/collectionTreeRow.js | 4++--
Mchrome/content/zotero/xpcom/collectionTreeView.js | 13+++++++++++--
Mchrome/content/zotero/xpcom/data/feed.js | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Mchrome/content/zotero/xpcom/data/feedItem.js | 33---------------------------------
Mchrome/content/zotero/xpcom/data/feedItems.js | 17++++++++++++++---
Mchrome/content/zotero/xpcom/data/feeds.js | 6+++---
Mchrome/content/zotero/xpcom/data/items.js | 8++++++--
Mchrome/content/zotero/xpcom/feedReader.js | 48+++++++++++++++++++++++-------------------------
Mchrome/content/zotero/xpcom/uri.js | 26+++++++++++++++++++++++++-
Mchrome/content/zotero/zoteroPane.js | 86++++++++++++++++++++++++++++++-------------------------------------------------
Achrome/skin/default/zotero/treesource-feed-error.png | 0
Achrome/skin/default/zotero/treesource-feed-updating.png | 0
12 files changed, 189 insertions(+), 160 deletions(-)

diff --git a/chrome/content/zotero/xpcom/collectionTreeRow.js b/chrome/content/zotero/xpcom/collectionTreeRow.js @@ -149,14 +149,14 @@ Zotero.CollectionTreeRow.prototype.isWithinEditableGroup = function () { } Zotero.CollectionTreeRow.prototype.__defineGetter__('editable', function () { - if (this.isTrash() || this.isShare() || this.isBucket() || this.isFeed()) { + if (this.isTrash() || this.isShare() || this.isBucket()) { return false; } if (!this.isWithinGroup() || this.isPublications()) { return true; } var libraryID = this.ref.libraryID; - if (this.isGroup()) { + if (this.isGroup() || this.isFeed()) { return this.ref.editable; } if (this.isCollection() || this.isSearch() || this.isDuplicates() || this.isUnfiled()) { diff --git a/chrome/content/zotero/xpcom/collectionTreeView.js b/chrome/content/zotero/xpcom/collectionTreeView.js @@ -48,6 +48,7 @@ Zotero.CollectionTreeView = function() 'collection', 'search', 'publications', + 'feed', 'share', 'group', 'feedItem', @@ -315,7 +316,7 @@ Zotero.CollectionTreeView.prototype.selectWait = Zotero.Promise.method(function * Called by Zotero.Notifier on any changes to collections in the data layer */ Zotero.CollectionTreeView.prototype.notify = Zotero.Promise.coroutine(function* (action, type, ids, extraData) { - if (type == 'feed' && action == 'unreadCountUpdated') { + if (type == 'feed' && (action == 'unreadCountUpdated' || action == 'statusChanged')) { for (let i=0; i<ids.length; i++) { this._treebox.invalidateRow(this._rowMap['L' + ids[i]]); } @@ -492,7 +493,7 @@ Zotero.CollectionTreeView.prototype.notify = Zotero.Promise.coroutine(function* case 'feed': case 'group': yield this.reload(); - yield this.selectByID(currentTreeRow.id); + yield this.selectByID("L" + id); break; } } @@ -719,6 +720,11 @@ Zotero.CollectionTreeView.prototype.getImageSrc = function(row, col) switch (collectionType) { case 'library': case 'feed': + if (treeRow.ref.updating) { + collectionType += '-updating'; + } else if (treeRow.ref.lastCheckError) { + collectionType += '-error'; + } break; case 'trash': @@ -731,6 +737,9 @@ Zotero.CollectionTreeView.prototype.getImageSrc = function(row, col) if (treeRow.ref.id == 'group-libraries-header') { collectionType = 'groups'; } + else if (treeRow.ref.id == 'feed-libraries-header') { + collectionType = 'feedLibrary'; + } else if (treeRow.ref.id == 'commons-header') { collectionType = 'commons'; } diff --git a/chrome/content/zotero/xpcom/data/feed.js b/chrome/content/zotero/xpcom/data/feed.js @@ -29,13 +29,14 @@ Zotero.Feed = function(params = {}) { this._feedCleanupAfter = null; this._feedRefreshInterval = null; - - // Feeds are not editable/filesEditable by the user. Remove the setter - this.editable = false; + + // Feeds are editable by the user. Remove the setter + this.editable = true; Zotero.defineProperty(this, 'editable', { get: function() this._get('_libraryEditable') }); - + + // Feeds are not filesEditable by the user. Remove the setter this.filesEditable = false; Zotero.defineProperty(this, 'filesEditable', { get: function() this._get('_libraryFilesEditable') @@ -53,22 +54,31 @@ Zotero.Feed = function(params = {}) { return obj[prop]; } }); + this._feedUnreadCount = null; + + this._updating = false; +} + +Zotero.Feed._colToProp = function(c) { + return "_feed" + Zotero.Utilities.capitalize(c); } +Zotero.defineProperty(Zotero.Feed, '_unreadCountSQL', { + value: "(SELECT COUNT(*) FROM items I JOIN feedItems FeI USING (itemID)" + + " WHERE I.libraryID=F.libraryID AND FeI.readTime IS NULL) AS _feedUnreadCount" +}); + Zotero.defineProperty(Zotero.Feed, '_dbColumns', { value: Object.freeze(['name', 'url', 'lastUpdate', 'lastCheck', 'lastCheckError', 'cleanupAfter', 'refreshInterval']) }); -Zotero.Feed._colToProp = function(c) { - return "_feed" + Zotero.Utilities.capitalize(c); -} +Zotero.defineProperty(Zotero.Feed, '_primaryDataSQLParts'); Zotero.defineProperty(Zotero.Feed, '_rowSQLSelect', { value: Zotero.Library._rowSQLSelect + ", " + Zotero.Feed._dbColumns.map(c => "F." + c + " AS " + Zotero.Feed._colToProp(c)).join(", ") - + ", (SELECT COUNT(*) FROM items I JOIN feedItems FeI USING (itemID)" - + " WHERE I.libraryID=F.libraryID AND FeI.readTime IS NULL) AS feedUnreadCount" + + ", " + Zotero.Feed._unreadCountSQL }); Zotero.defineProperty(Zotero.Feed, '_rowSQL', { @@ -89,6 +99,17 @@ Zotero.defineProperty(Zotero.Feed.prototype, 'isFeed', { Zotero.defineProperty(Zotero.Feed.prototype, 'libraryTypes', { value: Object.freeze(Zotero.Feed._super.prototype.libraryTypes.concat(['feed'])) }); +Zotero.defineProperty(Zotero.Feed.prototype, 'unreadCount', { + get: function() this._feedUnreadCount +}); +Zotero.defineProperty(Zotero.Feed.prototype, 'updating', { + get: function() this._updating, + set: function(v) { + if (!v == !this._updating) return; // Unchanged + this._updating = !!v; + Zotero.Notifier.trigger('statusChanged', 'feed', this.id); + } +}); (function() { // Create accessors @@ -185,7 +206,7 @@ Zotero.Feed.prototype._loadDataFromRow = function(row) { this._feedLastUpdate = row._feedLastUpdate || null; this._feedCleanupAfter = parseInt(row._feedCleanupAfter) || null; this._feedRefreshInterval = parseInt(row._feedRefreshInterval) || null; - this._feedUnreadCount = parseInt(row.feedUnreadCount); + this._feedUnreadCount = parseInt(row._feedUnreadCount); } Zotero.Feed.prototype._reloadFromDB = Zotero.Promise.coroutine(function* () { @@ -218,7 +239,7 @@ Zotero.Feed.prototype._initSave = Zotero.Promise.coroutine(function* (env) { Zotero.Feed.prototype._saveData = Zotero.Promise.coroutine(function* (env) { yield Zotero.Feed._super.prototype._saveData.apply(this, arguments); - Zotero.debug("Saving feed data for collection " + this.id); + Zotero.debug("Saving feed data for library " + this.id); let changedCols = [], params = []; for (let i=0; i<Zotero.Feed._dbColumns.length; i++) { @@ -268,21 +289,17 @@ Zotero.Feed.prototype._finalizeSave = Zotero.Promise.coroutine(function* (env) { } }); -Zotero.Feed.prototype._finalizeErase = Zotero.Promise.method(function(env) { - Zotero.Feeds.unregister(this.libraryID); - return Zotero.Feed._super.prototype._finalizeErase.apply(this, arguments); -}); - Zotero.Feed.prototype.getExpiredFeedItemIDs = Zotero.Promise.coroutine(function* () { let sql = "SELECT itemID AS id FROM feedItems " + + "LEFT JOIN libraryItems LI USING (itemID)" + + "WHERE LI.libraryID=?" + "WHERE readTime IS NOT NULL " + "AND (julianday(readTime, 'utc') + (?) - julianday('now', 'utc')) > 0"; - let expiredIDs = yield Zotero.DB.queryAsync(sql, [{int: this.cleanupAfter}]); + let expiredIDs = yield Zotero.DB.queryAsync(sql, [this.id, {int: this.cleanupAfter}]); return expiredIDs.map(row => row.id); }); -Zotero.Feed.prototype._updateFeed = Zotero.Promise.coroutine(function* () { - let errorMessage = ''; +Zotero.Feed.prototype.clearExpiredItems = Zotero.Promise.coroutine(function* () { try { // Clear expired items if (this.cleanupAfter) { @@ -299,10 +316,16 @@ Zotero.Feed.prototype._updateFeed = Zotero.Promise.coroutine(function* () { Zotero.debug("Error clearing expired feed items."); Zotero.debug(e); } +}); + +Zotero.Feed.prototype._updateFeed = Zotero.Promise.coroutine(function* () { + this.updating = true; + this.lastCheckError = null; + yield this.clearExpiredItems(); try { let fr = new Zotero.FeedReader(this.url); - let itemIterator = fr.itemIterator; + let itemIterator = new fr.ItemIterator(); let item, toAdd = [], processedGUIDs = []; while (item = yield itemIterator.next().value) { if (item.dateModified && this.lastUpdate @@ -331,6 +354,7 @@ Zotero.Feed.prototype._updateFeed = Zotero.Promise.coroutine(function* () { feedItem.libraryID = this.id; } else { Zotero.debug("Feed item " + item.guid + " already in library."); + if (item.dateModified && feedItem.dateModified && feedItem.dateModified == item.dateModified ) { @@ -353,19 +377,23 @@ Zotero.Feed.prototype._updateFeed = Zotero.Promise.coroutine(function* () { // Save in reverse order let savePromises = new Array(toAdd.length); for (let i=toAdd.length-1; i>=0; i--) { - yield toAdd[i].save({skipEditCheck: true, setDateModified: true}); + // Saving currently has to happen sequentially so as not to violate the + // unique constraints in dataValues (FIXME) + yield toAdd[i].save({skipEditCheck: true}); } this.lastUpdate = Zotero.Date.dateToSQL(new Date(), true); - } catch(e) { - Zotero.debug("Error processing feed from " + this.url); - Zotero.debug(e); - errorMessage = e.message || 'Error processing feed'; } - + catch (e) { + if (e.message) { + Zotero.debug("Error processing feed from " + this.url); + Zotero.debug(e); + } + this.lastCheckError = e.message || 'Error processing feed'; + } this.lastCheck = Zotero.Date.dateToSQL(new Date(), true); - this.lastCheckError = errorMessage || null; yield this.saveTx({skipEditCheck: true}); + this.updating = false; }); Zotero.Feed.prototype.updateFeed = function() { @@ -375,17 +403,27 @@ Zotero.Feed.prototype.updateFeed = function() { }); } -Zotero.Feed.prototype.erase = Zotero.Promise.coroutine(function* () { - yield this.loadChildItems(); - let childItemIDs = this.getChildItems(true, true); +Zotero.Feed.prototype._finalizeErase = Zotero.Promise.coroutine(function* (){ + let notifierData = {}; + notifierData[this.libraryID] = { + libraryID: this.libraryID + }; + Zotero.Notifier.trigger('delete', 'feed', this.id, notifierData); + Zotero.Feeds.unregister(this.libraryID); + return Zotero.Feed._super.prototype._finalizeErase.call(this); +}); + +Zotero.Feed.prototype.erase = Zotero.Promise.coroutine(function* (deleteItems) { + let childItemIDs = yield Zotero.FeedItems.getAll(this.id, false, false, true); yield Zotero.FeedItems.erase(childItemIDs); - return Zotero.Feed._super.prototype.erase.call(this); // Don't tell it to delete child items. They're already gone -}) + + yield Zotero.Feed._super.prototype.erase.call(this); +}); Zotero.Feed.prototype.updateUnreadCount = Zotero.Promise.coroutine(function* () { - let sql = "SELECT " + this._ObjectsClass._primaryDataSQLParts.feedUnreadCount - + this._ObjectsClass.primaryDataSQLFrom - + " AND O.libraryID=?"; + let sql = "SELECT " + Zotero.Feed._unreadCountSQL + + " FROM feeds F JOIN libraries L USING (libraryID)" + + " WHERE L.libraryID=?"; let newCount = yield Zotero.DB.valueQueryAsync(sql, [this.id]); if (newCount != this._feedUnreadCount) { diff --git a/chrome/content/zotero/xpcom/data/feedItem.js b/chrome/content/zotero/xpcom/data/feedItem.js @@ -132,38 +132,6 @@ Zotero.FeedItem.prototype._saveData = Zotero.Promise.coroutine(function* (env) { yield Zotero.DB.queryAsync(sql, [env.id, this.guid, this._feedItemReadTime]); this._clearChanged('feedItemData'); - -/* let itemID; - if (env.isNew) { - // For new items, run this first so we get an item ID - yield Zotero.FeedItem._super.prototype._saveData.apply(this, arguments); - itemID = env.id; - } else { - itemID = this.id; - } - - } - - if (!env.isNew) { - if (this.hasChanged()) { - yield Zotero.FeedItem._super.prototype._saveData.apply(this, arguments); - } else { - env.skipPrimaryDataReload = true; - } - Zotero.Notifier.trigger('modify', 'feedItem', itemID); - } else { - Zotero.Notifier.trigger('add', 'feedItem', itemID); - } - - if (env.collectionsAdded || env.collectionsRemoved) { - let affectedCollections = (env.collectionsAdded || []) - .concat(env.collectionsRemoved || []); - if (affectedCollections.length) { - let feeds = yield Zotero.Feeds.getAsync(affectedCollections); - for (let i=0; i<feeds.length; i++) { - feeds[i].updateUnreadCount(); - } - }*/ } }); @@ -174,7 +142,6 @@ Zotero.FeedItem.prototype.toggleRead = Zotero.Promise.coroutine(function* (state if (changed) { yield this.save({skipEditCheck: true, skipDateModifiedUpdate: true}); - yield this.loadCollections(); let feed = Zotero.Feeds.get(this.libraryID); feed.updateUnreadCount(); } diff --git a/chrome/content/zotero/xpcom/data/feedItems.js b/chrome/content/zotero/xpcom/data/feedItems.js @@ -94,14 +94,25 @@ Zotero.FeedItems = new Proxy(function() { return this.getAsync(id); }); - this.toggleReadById = Zotero.Promise.coroutine(function* (ids, state) { + this.toggleReadByID = Zotero.Promise.coroutine(function* (ids, state) { if (!Array.isArray(ids)) { - if (typeof ids != 'string') throw new Error('ids must be a string or array in Zotero.FeedItems.toggleReadById'); + if (typeof ids != 'string') throw new Error('ids must be a string or array in Zotero.FeedItems.toggleReadByID'); ids = [ids]; } - let items = yield this.getAsync(ids); + + if (state == undefined) { + // If state undefined, toggle read if at least one unread + state = true; + for (let item of items) { + if (item.isRead) { + state = false; + break; + } + } + } + for (let i=0; i<items.length; i++) { items[i].toggleRead(state); } diff --git a/chrome/content/zotero/xpcom/data/feeds.js b/chrome/content/zotero/xpcom/data/feeds.js @@ -105,7 +105,7 @@ Zotero.Feeds = new function() { .map(id => Zotero.Libraries.get(id)); } - this.get = Zotero.Libraries.get; + this.get = Zotero.Libraries.get.bind(Zotero.Libraries); this.haveFeeds = function() { if (!this._cache) throw new Error("Zotero.Feeds cache is not initialized"); @@ -154,8 +154,8 @@ Zotero.Feeds = new function() { let sql = "SELECT libraryID AS id FROM feeds " + "WHERE refreshInterval IS NOT NULL " + "AND ( lastCheck IS NULL " - + "OR (julianday(lastCheck, 'utc') + (refreshInterval/1440) - julianday('now', 'utc')) <= 0 )"; - let needUpdate = yield Zotero.DB.queryAsync(sql).map(row => row.id); + + "OR (julianday(lastCheck, 'utc') + (refreshInterval/1440.0) - julianday('now', 'utc')) <= 0 )"; + let needUpdate = (yield Zotero.DB.queryAsync(sql)).map(row => row.id); Zotero.debug("Running update for feeds: " + needUpdate.join(', ')); let feeds = Zotero.Libraries.get(needUpdate); let updatePromises = []; diff --git a/chrome/content/zotero/xpcom/data/items.js b/chrome/content/zotero/xpcom/data/items.js @@ -133,9 +133,10 @@ Zotero.Items = function() { * @param {Integer} libraryID * @param {Boolean} [onlyTopLevel=false] If true, don't include child items * @param {Boolean} [includeDeleted=false] If true, include deleted items - * @return {Promise<Array<Zotero.Item>>} + * @param {Boolean} [onlyIDs=false] If true, resolves only with IDs + * @return {Promise<Array<Zotero.Item|Integer>>} */ - this.getAll = Zotero.Promise.coroutine(function* (libraryID, onlyTopLevel, includeDeleted) { + this.getAll = Zotero.Promise.coroutine(function* (libraryID, onlyTopLevel, includeDeleted, onlyIDs=false) { var sql = 'SELECT A.itemID FROM items A'; if (onlyTopLevel) { sql += ' LEFT JOIN itemNotes B USING (itemID) ' @@ -150,6 +151,9 @@ Zotero.Items = function() { } sql += " AND libraryID=?"; var ids = yield Zotero.DB.columnQueryAsync(sql, libraryID); + if (onlyIDs) { + return ids; + } return this.getAsync(ids); }); diff --git a/chrome/content/zotero/xpcom/feedReader.js b/chrome/content/zotero/xpcom/feedReader.js @@ -45,7 +45,7 @@ * * @property {Zotero.Promise<Object>} feedProperties An object * representing feed properties - * @property {Zotero.Promise<FeedItem>*} itemIterator Returns an iterator + * @property {Zotero.Promise<FeedItem>*} ItemIterator Returns an iterator * for feed items. The iterator returns FeedItem promises that have to be * resolved before requesting the next promise. When all items are exhausted. * the promise resolves to null. @@ -170,17 +170,10 @@ Zotero.FeedReader = new function() { } /* - * Format JS date as SQL date + time zone offset + * Format JS date as SQL date */ function formatDate(date) { - let offset = (date.getTimezoneOffset() / 60) * -1; - let absOffset = Math.abs(offset); - offset = offset - ? ' ' + (offset < 0 ? '-' : '+') - + Zotero.Utilities.lpad(Math.floor(absOffset), '0', 2) - + ('' + ( (absOffset - Math.floor(absOffset)) || '' )).substr(1) // Get ".5" fraction or "" otherwise - : ''; - return Zotero.Date.dateToSQL(date, false) + offset; + return Zotero.Date.dateToSQL(date, true); } /* @@ -268,7 +261,6 @@ Zotero.FeedReader = new function() { if (!item.dateModified) { // When there's no reliable modification date, we can assume that item doesn't get updated Zotero.debug("FeedReader: Feed item missing a modification date (" + item.guid + ")"); - item.dateModified = null; } if (!item.date && item.dateModified) { @@ -470,7 +462,9 @@ Zotero.FeedReader = new function() { Zotero.debug("FeedReader: Fetching feed from " + feedUrl.spec); - this._channel = ios.newChannelFromURI(feedUrl); + this._channel = ios.newChannelFromURI2(feedUrl, null, + Services.scriptSecurityManager.getSystemPrincipal(), null, + Ci.nsILoadInfo.SEC_NORMAL, Ci.nsIContentPolicy.TYPE_OTHER); this._channel.asyncOpen(feedProcessor, null); // Sends an HTTP request } @@ -486,21 +480,25 @@ Zotero.FeedReader = new function() { * is terminated ahead of time, in which case it will be rejected with the reason * for termination. */ - Zotero.defineProperty(FeedReader.prototype, 'itemIterator', { + Zotero.defineProperty(FeedReader.prototype, 'ItemIterator', { get: function() { let items = this._feedItems; - return new function() { - let i = 0; - this.next = function() { - let item = items[i++]; - return { - value: item ? item.promise : null, - done: i >= items.length - }; + + let iterator = function() { + this.index = 0; + }; + + iterator.prototype.next = function() { + let item = items[this.index++]; + return { + value: item ? item.promise : null, + done: this.index >= items.length }; - } + }; + + return iterator; } - }); + }, {lazy: true}); /* * Terminate feed processing at any given time @@ -521,8 +519,8 @@ Zotero.FeedReader = new function() { } // Close feed connection - if (channel.isPending) { - channel.cancel(Components.results.NS_BINDING_ABORTED); + if (this._channel.isPending) { + this._channel.cancel(Components.results.NS_BINDING_ABORTED); } }; diff --git a/chrome/content/zotero/xpcom/uri.js b/chrome/content/zotero/xpcom/uri.js @@ -133,6 +133,14 @@ Zotero.URI = new function () { } + this.getFeedItemURI = function(feedItem) { + return this.getItemURI(feedItem); + } + + this.getFeedItemPath = function(feedItem) { + return this.getItemPath(feedItem); + } + /** * Return URI of collection, which might be a local URI if user hasn't synced */ @@ -148,6 +156,14 @@ Zotero.URI = new function () { return this._getObjectPath(collection); } + this.getFeedURI = function(feed) { + return this.getLibraryURI(feed); + } + + this.getFeedPath = function(feed) { + return this.getLibraryPath(feed); + } + this.getGroupsURL = function () { return ZOTERO_CONFIG.WWW_BASE_URL + "groups"; @@ -172,7 +188,7 @@ Zotero.URI = new function () { return path; } - if (obj instanceof Zotero.Item) { + if (obj instanceof Zotero.Item || obj instanceof Zotero.Feed) { return path + '/items/' + obj.key; } @@ -208,6 +224,9 @@ Zotero.URI = new function () { return this._getURIObject(itemURI, 'item'); } + this.getURIFeedItem = function (feedItemURI) { + return this._getURIObject(feedItemURI, 'feedItem'); + } /** * @param {String} itemURI @@ -264,6 +283,11 @@ Zotero.URI = new function () { let library = this._getURIObjectLibrary(libraryURI); return library ? library.id : false; } + + + this.getURIFeed = function (feedURI) { + return this._getURIObjectLibrary(feedURI, 'feed'); + } /** diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js @@ -522,22 +522,7 @@ var ZoteroPane = new function() } let itemIDs = this.getSelectedItems(true); - Zotero.FeedItems.getAsync(itemIDs) - .then(function(feedItems) { - // Determine what most items are set to; - let allUnread = true; - for (let item of feedItems) { - if (item.isRead) { - allUnread = false; - break; - } - } - - // If something is unread, toggle all read by default - for (let i=0; i<feedItems.length; i++) { - feedItems[i].toggleRead(!allUnread); - } - }); + Zotero.FeedItems.toggleReadByID(itemIDs); } } } @@ -866,7 +851,7 @@ var ZoteroPane = new function() return collection.saveTx(); }); - this.newFeed = Zotero.Promise.coroutine(function() { + this.newFeed = Zotero.Promise.coroutine(function* () { let data = {}; window.openDialog('chrome://zotero/content/feedSettings.xul', null, 'centerscreen, modal', data); @@ -877,7 +862,7 @@ var ZoteroPane = new function() feed.refreshInterval = data.ttl; feed.cleanupAfter = data.cleanAfter; yield feed.save({skipEditCheck: true}); - Zotero.Feeds.scheduleNextFeedCheck(); + yield feed.updateFeed(); } }); @@ -1774,48 +1759,40 @@ var ZoteroPane = new function() buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING + ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL; if (this.collectionsView.selection.count == 1) { - if (collectionTreeRow.isCollection()) - { + var title, message; + // Work out the required title and message + if (collectionTreeRow.isCollection()) { if (deleteItems) { - var index = ps.confirmEx( - null, - Zotero.getString('pane.collections.deleteWithItems.title'), - Zotero.getString('pane.collections.deleteWithItems'), - buttonFlags, - Zotero.getString('pane.collections.deleteWithItems.title'), - "", "", "", {} - ); + title = Zotero.getString('pane.collections.deleteWithItems.title'); + message = Zotero.getString('pane.collections.deleteWithItems'); } else { - var index = ps.confirmEx( - null, - Zotero.getString('pane.collections.delete.title'), - Zotero.getString('pane.collections.delete') + title = Zotero.getString('pane.collections.delete.title'); + message = Zotero.getString('pane.collections.delete') + "\n\n" - + Zotero.getString('pane.collections.delete.keepItems'), - buttonFlags, - Zotero.getString('pane.collections.delete.title'), - "", "", "", {} - ); - } - if (index == 0) { - this.collectionsView.deleteSelection(deleteItems); + + Zotero.getString('pane.collections.delete.keepItems'); } } - else if (collectionTreeRow.isSearch()) - { - - var index = ps.confirmEx( - null, - Zotero.getString('pane.collections.deleteSearch.title'), - Zotero.getString('pane.collections.deleteSearch'), - buttonFlags, - Zotero.getString('pane.collections.deleteSearch.title'), - "", "", "", {} - ); - if (index == 0) { - this.collectionsView.deleteSelection(); - } + else if (collectionTreeRow.isFeed()) { + title = Zotero.getString('pane.feed.deleteWithItems.title'); + message = Zotero.getString('pane.feed.deleteWithItems'); + } + else if (collectionTreeRow.isSearch()) { + title = Zotero.getString('pane.collections.deleteSearch.title'); + message = Zotero.getString('pane.collections.deleteSearch'); + } + + // Display prompt + var index = ps.confirmEx( + null, + title, + message, + buttonFlags, + title, + "", "", "", {} + ); + if (index == 0) { + this.collectionsView.deleteSelection(deleteItems); } } } @@ -2230,6 +2207,7 @@ var ZoteroPane = new function() "newCollection", "newSavedSearch", "newSubcollection", + "newFeed", "refreshFeed", "sep1", "showDuplicates", diff --git a/chrome/skin/default/zotero/treesource-feed-error.png b/chrome/skin/default/zotero/treesource-feed-error.png Binary files differ. diff --git a/chrome/skin/default/zotero/treesource-feed-updating.png b/chrome/skin/default/zotero/treesource-feed-updating.png Binary files differ.