Return-Path: X-Original-To: apmail-couchdb-commits-archive@www.apache.org Delivered-To: apmail-couchdb-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 2878C18165 for ; Mon, 2 Nov 2015 21:32:18 +0000 (UTC) Received: (qmail 96080 invoked by uid 500); 2 Nov 2015 21:32:18 -0000 Delivered-To: apmail-couchdb-commits-archive@couchdb.apache.org Received: (qmail 96005 invoked by uid 500); 2 Nov 2015 21:32:17 -0000 Mailing-List: contact commits-help@couchdb.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@couchdb.apache.org Delivered-To: mailing list commits@couchdb.apache.org Received: (qmail 95996 invoked by uid 99); 2 Nov 2015 21:32:17 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 02 Nov 2015 21:32:17 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 5BC49E0998; Mon, 2 Nov 2015 21:32:17 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: michellep@apache.org To: commits@couchdb.apache.org Message-Id: X-Mailer: ASF-Git Admin Mailer Subject: fauxton commit: updated refs/heads/master to e5f834e Date: Mon, 2 Nov 2015 21:32:17 +0000 (UTC) Repository: couchdb-fauxton Updated Branches: refs/heads/master e6a351437 -> e5f834e8f don't allow deletion of docs without rev if we have no rev, bulk deletion is not possible, so we should not try to offer it. Signed-off-by: Michelle Phung Project: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/repo Commit: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/commit/e5f834e8 Tree: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/tree/e5f834e8 Diff: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/diff/e5f834e8 Branch: refs/heads/master Commit: e5f834e8f4eca0d3d0daa844f29dfcf9ea3f2537 Parents: e6a3514 Author: Robert Kowalski Authored: Fri Oct 30 13:39:47 2015 +0100 Committer: Michelle Phung Committed: Mon Nov 2 16:29:12 2015 -0500 ---------------------------------------------------------------------- .../index-results.components.react.jsx | 11 +- app/addons/documents/index-results/stores.js | 82 +++++++-------- .../index-results.componentsSpec.react.jsx | 100 ++++++++++++++++--- .../tests/index-results.storesSpec.js | 40 ++++++-- app/addons/documents/resources.js | 4 + app/addons/documents/shared-resources.js | 4 + .../documents/tests/document-test-helper.js | 45 +++++++++ 7 files changed, 219 insertions(+), 67 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/e5f834e8/app/addons/documents/index-results/index-results.components.react.jsx ---------------------------------------------------------------------- diff --git a/app/addons/documents/index-results/index-results.components.react.jsx b/app/addons/documents/index-results/index-results.components.react.jsx index bbcd196..92cdfdc 100644 --- a/app/addons/documents/index-results/index-results.components.react.jsx +++ b/app/addons/documents/index-results/index-results.components.react.jsx @@ -86,8 +86,8 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, Documents) { }, maybeGetCheckboxCell: function (i) { - if (!this.props.docId) { - return null; + if (!this.props.data.isDeletable) { + return ; } return ( @@ -123,6 +123,7 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, Documents) { return ( - + title="Select all docs that can be..." /> : null} {mainView} ); @@ -347,7 +348,7 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, Documents) { canSelectAll={this.state.canSelectAll} isSelected={this.isSelected} isEditable={this.state.isEditable} - isListDeletable={this.state.results.hasEditableAndDeletableDoc} + isListDeletable={this.state.results.hasDeletableDoc} docChecked={this.docChecked} isLoading={this.state.isLoading} results={this.state.results} http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/e5f834e8/app/addons/documents/index-results/stores.js ---------------------------------------------------------------------- diff --git a/app/addons/documents/index-results/stores.js b/app/addons/documents/index-results/stores.js index 18d2f06..5a4766e 100644 --- a/app/addons/documents/index-results/stores.js +++ b/app/addons/documents/index-results/stores.js @@ -104,7 +104,7 @@ function (app, FauxtonAPI, ActionTypes, HeaderActionTypes, Documents, MangoHelpe return false; } - return doc.isDeletable(); + return doc.isBulkDeletable(); }, getCollection: function () { @@ -179,29 +179,30 @@ function (app, FauxtonAPI, ActionTypes, HeaderActionTypes, Documents, MangoHelpe }, + filterOutGeneratedMangoDocs: function (doc) { + if (doc.get && typeof doc.get === 'function') { + return doc.get('language') !== 'query'; + } + + return doc.language !== 'query'; + }, + getResults: function () { - var hasEditableAndDeletableDoc; + var hasDeletableDoc; var res; var collection; - - function filterOutGeneratedMangoDocs (doc) { - if (doc.get && typeof doc.get === 'function') { - return doc.get('language') !== 'query'; - } - - return doc.language !== 'query'; - } + var filteredCollection = this._collection.filter(this.filterOutGeneratedMangoDocs); // Table sytle view if (this.getIsTableView()) { - collection = this._collection.toJSON().filter(filterOutGeneratedMangoDocs); + collection = this._collection.toJSON().filter(this.filterOutGeneratedMangoDocs); return this.getTableViewData(collection); } // JSON style views res = this._collection - .filter(filterOutGeneratedMangoDocs) + .filter(this.filterOutGeneratedMangoDocs) .map(function (doc, i) { if (doc.get('def') || this.isGhostDoc(doc)) { return this.getMangoDoc(doc, i); @@ -217,10 +218,10 @@ function (app, FauxtonAPI, ActionTypes, HeaderActionTypes, Documents, MangoHelpe }; }, this); - hasEditableAndDeletableDoc = this.getHasEditableAndDeletableDoc(res); + hasDeletableDoc = this.getHasDeletableDoc(res); return { - hasEditableAndDeletableDoc: hasEditableAndDeletableDoc, + hasDeletableDoc: hasDeletableDoc, results: res }; }, @@ -259,7 +260,7 @@ function (app, FauxtonAPI, ActionTypes, HeaderActionTypes, Documents, MangoHelpe return data; }, - getTableViewData: function (data, hasEditableAndDeletableDoc) { + getTableViewData: function (data) { var res; var schema; var database; @@ -268,42 +269,36 @@ function (app, FauxtonAPI, ActionTypes, HeaderActionTypes, Documents, MangoHelpe schema = this.getPseudoSchema(data); database = this.getDatabase().safeID(); - res = data.map(function (doc) { - var safeId = app.utils.getSafeIdForDoc(doc._id); - var url; - - if (safeId) { - url = FauxtonAPI.urls('document', 'app', database, safeId); - } - - return { - content: doc, - id: safeId, - header: '', - keylabel: '', - url: url, - isDeletable: !!safeId, - isEditable: !!safeId - }; - }); + res = this._collection + .filter(this.filterOutGeneratedMangoDocs) + .map(function (doc) { - hasEditableAndDeletableDoc = this.getHasEditableAndDeletableDoc(res); + return { + content: doc.toJSON(), + id: this.getDocId(doc), + header: '', + keylabel: '', + url: this.getDocId(doc) ? doc.url('app') : null, + isDeletable: this.isDeletable(doc), + isEditable: this.isEditable(doc) + }; + }, this); return { - hasEditableAndDeletableDoc: hasEditableAndDeletableDoc, + hasDeletableDoc: this.getHasDeletableDoc(res), schema: schema, results: res }; }, - getHasEditableAndDeletableDoc: function (data) { + getHasDeletableDoc: function (data) { var found = false; var length = data.length; var i; - // use a for loop here as we can end it once we found the first id for (i = 0; i < length; i++) { - if (data[i].id) { + + if (data[i].isDeletable) { found = true; break; } @@ -343,6 +338,9 @@ function (app, FauxtonAPI, ActionTypes, HeaderActionTypes, Documents, MangoHelpe selectAllDocuments: function () { this.clearSelectedItems(); this._collection.each(function (doc) { + if (!doc.isBulkDeletable()) { + return; + } this.selectDoc(doc.id); }, this); }, @@ -356,11 +354,17 @@ function (app, FauxtonAPI, ActionTypes, HeaderActionTypes, Documents, MangoHelpe }, areAllDocumentsSelected: function () { + var filtered; + if (this._collection.length === 0) { return false; } - return Object.keys(this._selectedItems).length === this._collection.length; + filtered = this._collection.filter(function (doc) { + return doc.isBulkDeletable(); + }); + + return Object.keys(this._selectedItems).length === filtered.length; }, getSelectedItemsLength: function () { http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/e5f834e8/app/addons/documents/index-results/tests/index-results.componentsSpec.react.jsx ---------------------------------------------------------------------- diff --git a/app/addons/documents/index-results/tests/index-results.componentsSpec.react.jsx b/app/addons/documents/index-results/tests/index-results.componentsSpec.react.jsx index ee4811c..0589c7a 100644 --- a/app/addons/documents/index-results/tests/index-results.componentsSpec.react.jsx +++ b/app/addons/documents/index-results/tests/index-results.componentsSpec.react.jsx @@ -16,9 +16,11 @@ define([ 'addons/documents/index-results/stores', 'addons/documents/resources', 'addons/databases/resources', + + 'addons/documents/tests/document-test-helper', 'testUtils', "react" -], function (FauxtonAPI, Views, IndexResultsActions, Stores, Documents, Databases, utils, React) { +], function (FauxtonAPI, Views, IndexResultsActions, Stores, Documents, Databases, documentTestHelper, utils, React) { FauxtonAPI.router = new FauxtonAPI.Router([]); var assert = utils.assert; @@ -82,17 +84,67 @@ define([ store.reset(); }); - function createDocColumn (docs) { - docs = docs.map(function (doc) { - return Documents.Doc.prototype.parse(doc); + var createDocColumn = documentTestHelper.createDocColumn; + var createMangoIndexDocColumn = documentTestHelper.createMangoIndexDocColumn; + + + it('does not render checkboxes for elements with just the special index (Mango Index List)', function () { + IndexResultsActions.sendMessageNewResultList({ + collection: createMangoIndexDocColumn([{foo: 'testId1', type: 'special'}]) }); - return new Documents.AllDocs(docs, opts); - } + store.enableTableView(); + + IndexResultsActions.resultsListReset(); + + instance = TestUtils.renderIntoDocument( + , + container + ); + + var $el = $(instance.getDOMNode()); + + assert.ok($el.find('.tableview-header-el-checkbox').length === 0); + }); - it('does not render checkboxes for elements with no id in a table', function () { + it('renders checkboxes for elements with more than just the the special index (Mango Index List)', function () { IndexResultsActions.sendMessageNewResultList({ - collection: createDocColumn([{foo: 'testId1'}, {bar: 'testId1'}]) + collection: createMangoIndexDocColumn([{ + ddoc: null, + name: 'biene', + type: 'json', + def: {fields: [{_id: 'desc'}]} + }, + { + ddoc: null, + name: 'biene', + type: 'special', + def: {fields: [{_id: 'desc'}]} + }]) + }); + + store.enableTableView(); + + IndexResultsActions.resultsListReset(); + + instance = TestUtils.renderIntoDocument( + , + container + ); + + var $el = $(instance.getDOMNode()); + + assert.ok($el.find('.tableview-header-el-checkbox').length > 0); + }); + + it('does not render checkboxes for elements with no id in a table (usual docs)', function () { + IndexResultsActions.sendMessageNewResultList({ + collection: createDocColumn([{ + ddoc: null, + name: 'biene', + type: 'special', + def: {fields: [{_id: 'desc'}]} + }]) }); store.enableTableView(); @@ -109,9 +161,28 @@ define([ assert.ok($el.find('.tableview-header-el-checkbox').length === 0); }); - it('renders checkboxes for elements with id in a table', function () { + it('does not render checkboxes for elements with no rev in a table (usual docs)', function () { IndexResultsActions.sendMessageNewResultList({ - collection: createDocColumn([{id: '1', foo: 'testId1'}, {bar: 'testId1'}]) + collection: createDocColumn([{id: '1', foo: 'testId1'}, {id: '1', bar: 'testId1'}]) + }); + + store.enableTableView(); + + IndexResultsActions.resultsListReset(); + + instance = TestUtils.renderIntoDocument( + , + container + ); + + var $el = $(instance.getDOMNode()); + + assert.ok($el.find('.tableview-header-el-checkbox').length === 0); + }); + + it('renders checkboxes for elements with an id and rev in a table (usual docs)', function () { + IndexResultsActions.sendMessageNewResultList({ + collection: createDocColumn([{id: '1', foo: 'testId1', rev: 'foo'}, {bar: 'testId1', rev: 'foo'}]) }); store.enableTableView(); @@ -128,9 +199,9 @@ define([ assert.ok($el.find('.tableview-checkbox-cell').length > 0); }); - it('renders checkboxes for elements with an id in a json view', function () { + it('renders checkboxes for elements with an id and rev in a json view (usual docs)', function () { IndexResultsActions.sendMessageNewResultList({ - collection: createDocColumn([{id: '1', foo: 'testId1'}, {bar: 'testId1'}]) + collection: createDocColumn([{id: '1', emma: 'testId1', rev: 'foo'}, {bar: 'testId1'}]) }); IndexResultsActions.resultsListReset(); @@ -143,13 +214,12 @@ define([ ); var $el = $(instance.getDOMNode()); - assert.ok($el.find('.js-row-select').length > 0); }); - it('does not render checkboxes for elements with no id in a json view', function () { + it('does not render checkboxes for elements with that are not deletable in a json view (usual docs)', function () { IndexResultsActions.sendMessageNewResultList({ - collection: createDocColumn([{foo: 'testId1'}, {bar: 'testId1'}]) + collection: createDocColumn([{foo: 'testId1', rev: 'foo'}, {bar: 'testId1'}]) }); IndexResultsActions.resultsListReset(); http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/e5f834e8/app/addons/documents/index-results/tests/index-results.storesSpec.js ---------------------------------------------------------------------- diff --git a/app/addons/documents/index-results/tests/index-results.storesSpec.js b/app/addons/documents/index-results/tests/index-results.storesSpec.js index 304cf87..dec5d50 100644 --- a/app/addons/documents/index-results/tests/index-results.storesSpec.js +++ b/app/addons/documents/index-results/tests/index-results.storesSpec.js @@ -15,13 +15,18 @@ define([ 'addons/documents/index-results/stores', 'addons/documents/index-results/actiontypes', 'addons/documents/shared-resources', + 'addons/documents/tests/document-test-helper', + 'testUtils' -], function (FauxtonAPI, Stores, ActionTypes, Documents, testUtils) { +], function (FauxtonAPI, Stores, ActionTypes, Documents, documentTestHelper, testUtils) { var assert = testUtils.assert; var dispatchToken; var store; var opts; + var createDocColumn = documentTestHelper.createDocColumn; + var createMangoIndexDocColumn = documentTestHelper.createMangoIndexDocColumn; + describe('Index Results Store', function () { beforeEach(function () { store = new Stores.IndexResultsStore(); @@ -120,20 +125,21 @@ define([ assert.deepEqual(doclist, res); }); - it('finds out if we have at least one editable/deleteable doc which needs an id', function () { + it('finds out if we have at least one editable/deleteable doc', function () { var doclist = [ - {id: 'testId2', foo: 'one'}, + {id: 'testId2', foo: 'one', isDeletable: true}, {id: 'testId3', foo: 'two'} ]; - assert.ok(store.getHasEditableAndDeletableDoc(doclist)); + assert.ok(store.getHasDeletableDoc(doclist)); doclist = [ {foo: 'one'}, {foo: 'two'} ]; - assert.notOk(store.getHasEditableAndDeletableDoc(doclist)); + assert.notOk(store.getHasDeletableDoc(doclist)); + }); it('if the collection is empty, no docs should be selected', function () { @@ -142,6 +148,17 @@ define([ assert.notOk(store.areAllDocumentsSelected()); }); + it('special mango docs are not selectable', function () { + store._collection = createMangoIndexDocColumn([ + {ddoc: 'testId1', type: 'special', def: {fields: [{_id: 'desc'}]}}, + {ddoc: 'testId2', blubb: 'ba', type: 'json', def: {fields: [{_id: 'desc'}]}} + ]); + + store.selectAllDocuments(); + + assert.ok(store.areAllDocumentsSelected()); + }); + }); describe('canSelectAll', function () { @@ -236,18 +253,25 @@ define([ describe('#selectAllDocuments', function () { it('selects all documents', function () { - store._collection = new Documents.AllDocs([{_id: 'testId1', 'value': 'one'}], opts); + store._collection = createDocColumn([{_id: 'testId1', _rev: '1', 'value': 'one'}]); store.selectAllDocuments(); assert.ok(store.getSelectedItems().testId1); }); + it('does not select all documents if rev is missing', function () { + store._collection = createDocColumn([{_id: 'testId1', 'value': 'one'}]); + + store.selectAllDocuments(); + assert.equal(store.getSelectedItemsLength(), 0); + }); + }); describe('toggleSelectAllDocuments', function () { it('deselects all documents', function () { - store._collection = new Documents.AllDocs([{_id: 'testId1', 'value': 'one'}], opts); + store._collection = new Documents.AllDocs([{_id: 'testId1', _rev: '1', 'value': 'one'}], opts); store.selectAllDocuments(); assert.ok(store.getSelectedItems().testId1); @@ -257,7 +281,7 @@ define([ it('deselects all documents', function () { store.reset(); - store._collection = new Documents.AllDocs([{_id: 'testId1', 'value': 'one'}], opts); + store._collection = new Documents.AllDocs([{_id: 'testId1', _rev: '1', 'value': 'one'}], opts); assert.equal(Object.keys(store.getSelectedItems()).length, 0); store.toggleSelectAllDocuments(); http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/e5f834e8/app/addons/documents/resources.js ---------------------------------------------------------------------- diff --git a/app/addons/documents/resources.js b/app/addons/documents/resources.js index c6cbb29..e8d291c 100644 --- a/app/addons/documents/resources.js +++ b/app/addons/documents/resources.js @@ -94,6 +94,10 @@ function (app, FauxtonAPI, Documents, PagingCollection) { return this.get('type') !== 'special'; }, + isBulkDeletable: function () { + return this.isDeletable(); + }, + isFromView: function () { return false; }, http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/e5f834e8/app/addons/documents/shared-resources.js ---------------------------------------------------------------------- diff --git a/app/addons/documents/shared-resources.js b/app/addons/documents/shared-resources.js index 7a635d9..570cef6 100644 --- a/app/addons/documents/shared-resources.js +++ b/app/addons/documents/shared-resources.js @@ -57,6 +57,10 @@ define([ return app.utils.getDocTypeFromId(this.id); }, + isBulkDeletable: function () { + return !!this.id && !!this.get('_rev'); + }, + isDeletable: function () { return !!this.id; }, http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/e5f834e8/app/addons/documents/tests/document-test-helper.js ---------------------------------------------------------------------- diff --git a/app/addons/documents/tests/document-test-helper.js b/app/addons/documents/tests/document-test-helper.js new file mode 100644 index 0000000..c6ecce3 --- /dev/null +++ b/app/addons/documents/tests/document-test-helper.js @@ -0,0 +1,45 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +define([ + 'api', + 'addons/documents/resources' +], function (FauxtonAPI, Documents) { + + var opts = { + params: {}, + database: { + safeID: function () { return '1';} + } + }; + + function createDocColumn (docs) { + docs = docs.map(function (doc) { + return Documents.Doc.prototype.parse(doc); + }); + + return new Documents.AllDocs(docs, opts); + } + + function createMangoIndexDocColumn (docs) { + docs = docs.map(function (doc) { + return Documents.MangoIndex.prototype.parse(doc); + }); + + return new Documents.MangoIndexCollection(docs, opts); + } + + return { + createDocColumn: createDocColumn, + createMangoIndexDocColumn: createMangoIndexDocColumn + }; + +});