From 316dff637fb22933c58dc98d88f5e97f4b1beb20 Mon Sep 17 00:00:00 2001 From: Giulio Cesare Solaroli Date: Tue, 26 Aug 2014 17:31:24 +0200 Subject: [PATCH] Implemented "Clone card" command. --- .../Clipperz/PM/DataModel/Record.Version.js | 3 +- .../delta/js/Clipperz/PM/DataModel/Record.js | 33 ++++++++-- .../delta/js/Clipperz/PM/DataModel/User.js | 23 ++++++- .../PM/UI/Components/Cards/CommandToolbar.js | 6 +- .../Clipperz/PM/UI/Components/Cards/Edit.js | 8 +-- .../PM/UI/Components/Cards/EditToolbar.js | 1 - .../Clipperz/PM/UI/Components/Cards/List.js | 26 +++++--- .../delta/js/Clipperz/PM/UI/MainController.js | 51 ++++++++++++--- .../tests/Clipperz/PM/DataModel/User.test.js | 65 ++++++++++++++++++- 9 files changed, 181 insertions(+), 35 deletions(-) diff --git a/frontend/delta/js/Clipperz/PM/DataModel/Record.Version.js b/frontend/delta/js/Clipperz/PM/DataModel/Record.Version.js index 338b336..ad1bce0 100644 --- a/frontend/delta/js/Clipperz/PM/DataModel/Record.Version.js +++ b/frontend/delta/js/Clipperz/PM/DataModel/Record.Version.js @@ -186,7 +186,8 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record.Version, Clipperz.PM.DataModel MochiKit.Base.bind(function () { this._fields[newField.reference()] = newField; }, this), MochiKit.Base.method(newField, 'setLabel', someParameters['label']), MochiKit.Base.method(newField, 'setValue', someParameters['value']), - MochiKit.Base.method(newField, 'setIsHidden', someParameters['isHidden']), +// MochiKit.Base.method(newField, 'setIsHidden', someParameters['isHidden']), + MochiKit.Base.method(newField, 'setIsHidden', someParameters['hidden']), MochiKit.Base.method(this, '_getObjectDataStore'), MochiKit.Base.methodcaller('values'), diff --git a/frontend/delta/js/Clipperz/PM/DataModel/Record.js b/frontend/delta/js/Clipperz/PM/DataModel/Record.js index 4abc5c5..765133f 100644 --- a/frontend/delta/js/Clipperz/PM/DataModel/Record.js +++ b/frontend/delta/js/Clipperz/PM/DataModel/Record.js @@ -207,7 +207,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt //......................................................................... - 'extractTags': function (aLabel) { + 'extractTagsFromFullLabel': function (aLabel) { var tagRegEx; var result; var match; @@ -226,7 +226,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt 'tags': function () { return Clipperz.Async.callbacks("Record.label", [ MochiKit.Base.method(this, 'fullLabel'), - MochiKit.Base.method(this, 'extractTags'), + MochiKit.Base.method(this, 'extractTagsFromFullLabel'), MochiKit.Base.keys ], {trace:false}); }, @@ -234,7 +234,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt 'addTag': function (aNewTag) { return Clipperz.Async.callbacks("Record.addTag", [ MochiKit.Base.method(this, 'fullLabel'), - MochiKit.Base.method(this, 'extractTags'), + MochiKit.Base.method(this, 'extractTagsFromFullLabel'), function (someTags) { someTags[aNewTag] = true; /* console.log("UPDATED TAGS", someTags); */ return someTags; }, MochiKit.Base.method(this, 'updateTags') ], {trace:false}); @@ -244,7 +244,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt //console.log("ADD TAG", aNewTag); return Clipperz.Async.callbacks("Record.removeTag", [ MochiKit.Base.method(this, 'fullLabel'), - MochiKit.Base.method(this, 'extractTags'), + MochiKit.Base.method(this, 'extractTagsFromFullLabel'), function (someTags) { delete someTags[aTag]; return someTags; }, MochiKit.Base.method(this, 'updateTags') ], {trace:false}); @@ -1008,8 +1008,29 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt //========================================================================= 'setUpWithRecord': function (aRecord) { - return Clipperz.Async.callbacks("Record.hasAnyCleanTextData", [ - MochiKit.Base.method(aRecord, 'full') + return Clipperz.Async.callbacks("Record.setUpWithRecord", [ + MochiKit.Base.method(aRecord, 'label'), + MochiKit.Base.bind(function (aLabel) { + return this.setLabel(aLabel + " - copy"); + }, this), + + MochiKit.Base.method(aRecord, 'fullLabel'), + MochiKit.Base.method(aRecord, 'extractTagsFromFullLabel'), + MochiKit.Base.method(this, 'updateTags'), + + MochiKit.Base.method(aRecord, 'notes'), + MochiKit.Base.method(this, 'setNotes'), + + MochiKit.Base.method(aRecord, 'getFieldsValues'), MochiKit.Base.values, + MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, 'addField')), + Clipperz.Async.collectAll, + +// MochiKit.Base.method(aRecord, 'directLogins'), MochiKit.Base.values, +// function (aValue) { console.log("-> DirectLogin Values", aValue); return aValue; }, +// MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, 'addField')), +// Clipperz.Async.collectAll, + + MochiKit.Base.bind(function () { return this; }, this) ], {trace:false}); }, diff --git a/frontend/delta/js/Clipperz/PM/DataModel/User.js b/frontend/delta/js/Clipperz/PM/DataModel/User.js index e1c459e..11f9674 100644 --- a/frontend/delta/js/Clipperz/PM/DataModel/User.js +++ b/frontend/delta/js/Clipperz/PM/DataModel/User.js @@ -660,10 +660,27 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, { }, 'cloneRecord': function (aRecord) { -console.log("USER.cloneRecord", aRecord); +//console.log("USER.cloneRecord", aRecord); + var result; + var user = this; + return Clipperz.Async.callbacks("User.cloneRecord", [ - MochiKit.Base.method(this, 'createNewRecord'), - MochiKit.Base.methodcaller('setUpWithRecord', aRecord) +// MochiKit.Base.method(this, 'createNewRecord'), +// MochiKit.Base.methodcaller('setUpWithRecord', aRecord), +// function (aValue) { result = aValue; return aValue; }, +// MochiKit.Base.method(this, 'saveChanges'), +// function () { return result; } + + MochiKit.Base.method(this, 'hasPendingChanges'), + Clipperz.Async.deferredIf("User has pending changes", [ + MochiKit.Async.fail + ], [ + MochiKit.Base.method(user, 'createNewRecord'), + MochiKit.Base.methodcaller('setUpWithRecord', aRecord), + function (aValue) { result = aValue; return aValue; }, + MochiKit.Base.method(user, 'saveChanges'), + function () { return result; } + ]) ], {trace:false}); }, diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/Cards/CommandToolbar.js b/frontend/delta/js/Clipperz/PM/UI/Components/Cards/CommandToolbar.js index f03d24f..41eccf5 100644 --- a/frontend/delta/js/Clipperz/PM/UI/Components/Cards/CommandToolbar.js +++ b/frontend/delta/js/Clipperz/PM/UI/Components/Cards/CommandToolbar.js @@ -55,6 +55,10 @@ Clipperz.PM.UI.Components.Cards.CommandToolbar = React.createClass({ // 'label': "share", // 'broadcastEvent': 'shareCard' // }, + 'clone': { + 'label': "clone", + 'broadcastEvent': 'cloneCard' + }, 'edit': { 'label': "edit", 'broadcastEvent': 'editCard' @@ -83,7 +87,7 @@ Clipperz.PM.UI.Components.Cards.CommandToolbar = React.createClass({ var commandHandler = this.selectCommandItem; return React.DOM.ul({}, MochiKit.Base.map(function (aCommand) { - return React.DOM.li({'onClick':commandHandler, 'data-broadcast-event':aCommand['broadcastEvent']}, [React.DOM.span({}, aCommand['label'])]); + return React.DOM.li({'className':aCommand['broadcastEvent'], 'onClick':commandHandler, 'data-broadcast-event':aCommand['broadcastEvent']}, [React.DOM.span({}, aCommand['label'])]); }, MochiKit.Base.values(this.commands()))); }, diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/Cards/Edit.js b/frontend/delta/js/Clipperz/PM/UI/Components/Cards/Edit.js index 14f04ab..0232c32 100644 --- a/frontend/delta/js/Clipperz/PM/UI/Components/Cards/Edit.js +++ b/frontend/delta/js/Clipperz/PM/UI/Components/Cards/Edit.js @@ -72,11 +72,11 @@ Clipperz.PM.UI.Components.Cards.Edit = React.createClass({ //============================================================================ renderLabel: function (aLabel) { - return React.DOM.input({'className':'cardLabel', 'onChange':this.handleChange(this.record(), 'setLabel'), 'defaultValue':aLabel}); + return React.DOM.input({'className':'cardLabel', 'onChange':this.handleChange(this.record(), 'setLabel'), 'defaultValue':aLabel, 'key':this.props['_reference'] + '_label'}); }, renderNotes: function (someNotes) { - return React.DOM.textarea({'className':'cardNotes', 'onChange':this.handleChange(this.record(), 'setNotes'), 'defaultValue':someNotes}); + return React.DOM.textarea({'className':'cardNotes', 'onChange':this.handleChange(this.record(), 'setNotes'), 'defaultValue':someNotes, 'key':this.props['_reference'] + '_notes'}); }, //............................................................................ @@ -130,7 +130,7 @@ Clipperz.PM.UI.Components.Cards.Edit = React.createClass({ //............................................................................ renderDirectLogin: function (aDirectLogin) { - return React.DOM.div({'className':'cardDirectLogin'}, [ + return React.DOM.div({'className':'cardDirectLogin', 'key':aDirectLogin['_reference']}, [ React.DOM.span({'className':'directLoginLabel'}, aDirectLogin['label']), React.DOM.div({'className':'directLoginAction action'}, 'DIRECT LOGIN') ]); @@ -147,7 +147,7 @@ Clipperz.PM.UI.Components.Cards.Edit = React.createClass({ 'edit': true } -console.log("RENDER CARD EDIT"); +//console.log("RENDER CARD EDIT"); return React.DOM.div({'className':React.addons.classSet(classes)},[ Clipperz.PM.UI.Components.Cards.EditToolbar(this.props), React.DOM.div({'className':'content'}, [ diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/Cards/EditToolbar.js b/frontend/delta/js/Clipperz/PM/UI/Components/Cards/EditToolbar.js index a735ad1..d990251 100644 --- a/frontend/delta/js/Clipperz/PM/UI/Components/Cards/EditToolbar.js +++ b/frontend/delta/js/Clipperz/PM/UI/Components/Cards/EditToolbar.js @@ -59,7 +59,6 @@ Clipperz.PM.UI.Components.Cards.EditToolbar = React.createClass({ }; classes[style] = true; -console.log("EDIT TOOLBAR", this.props); return React.DOM.div({'className':React.addons.classSet(classes)}, [ React.DOM.ul({}, [ React.DOM.li({'onClick':this.cancel, 'className':'cancel'}, [React.DOM.span({}, "cancel")]), diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/Cards/List.js b/frontend/delta/js/Clipperz/PM/UI/Components/Cards/List.js index 2fdd2ca..b83e227 100644 --- a/frontend/delta/js/Clipperz/PM/UI/Components/Cards/List.js +++ b/frontend/delta/js/Clipperz/PM/UI/Components/Cards/List.js @@ -38,16 +38,24 @@ Clipperz.PM.UI.Components.Cards.List = React.createClass({ }, renderItem: function (anItem) { - var classes = { - 'selected': this.props['selectedCard'] ? this.props['selectedCard']['_reference'] == anItem['_reference'] : false, - 'archived': anItem['_isArchived'] - }; + var result; - return React.DOM.li({'className':React.addons.classSet(classes), 'onClick': this.handleClick, 'key':anItem['_reference'], 'data-reference':anItem['_reference'], 'data-label':anItem['label']}, [ - React.DOM.span({'className':'favicon'}, [ React.DOM.img({src:anItem['favicon']})]), - React.DOM.span({'className':'label'}, anItem['label']), -// React.DOM.span({'className':'action'}, 'show detail') - ]); + if (anItem['_isBrandNew'] == true) { + result = null; + } else { + var classes = { + 'selected': this.props['selectedCard'] ? this.props['selectedCard']['_reference'] == anItem['_reference'] : false, + 'archived': anItem['_isArchived'] + }; + + result = React.DOM.li({'className':React.addons.classSet(classes), 'onClick': this.handleClick, 'key':anItem['_reference'], 'data-reference':anItem['_reference'], 'data-label':anItem['label']}, [ + React.DOM.span({'className':'favicon'}, [ React.DOM.img({src:anItem['favicon']})]), + React.DOM.span({'className':'label'}, anItem['label']), + // React.DOM.span({'className':'action'}, 'show detail') + ]); + } + + return result; }, render: function () { diff --git a/frontend/delta/js/Clipperz/PM/UI/MainController.js b/frontend/delta/js/Clipperz/PM/UI/MainController.js index 9067c13..96f196c 100644 --- a/frontend/delta/js/Clipperz/PM/UI/MainController.js +++ b/frontend/delta/js/Clipperz/PM/UI/MainController.js @@ -82,6 +82,7 @@ Clipperz.PM.UI.MainController = function() { 'addCardClick', 'deleteCard', 'archiveCard', + 'cloneCard', 'editCard', 'showArchivedCards', @@ -429,7 +430,7 @@ console.log("SET USER", aUser); collectRecordInfo: function (aRecord) { var deferredResult; - + deferredResult = new Clipperz.Async.Deferred('MainController.collectRecordInfo', {trace:false}); deferredResult.setValue('_record'); deferredResult.addMethod(aRecord, 'reference'); @@ -444,8 +445,6 @@ console.log("SET USER", aUser); deferredResult.setValue('notes'); deferredResult.addMethod(aRecord, 'tags'); deferredResult.setValue('tags'); -// deferredResult.addMethod(aRecord, 'isArchived'); -// deferredResult.setValue('isArchived'); deferredResult.addMethod(aRecord, 'fields'); deferredResult.addCallback(MochiKit.Base.values); @@ -971,15 +970,21 @@ console.log("SET USER", aUser); // MochiKit.Base.method(self, 'updateSelectedCard', {'reference':aRecordReference}, false), // MochiKit.Base.method(self, 'refreshUI'), MochiKit.Base.method(this, 'refreshUI', aRecordReference) - ], {trace:true}); + ], {trace:false}); }, cancelCardEdits_handler: function (aRecordReference) { var currentPage = this.pages()[this.currentPage()]; var self = this; - + var wasBrandNew; + return Clipperz.Async.callbacks("MainController.cancelCardEdits_handler", [ + MochiKit.Base.method(this.user(), 'getRecord', aRecordReference), + MochiKit.Base.methodcaller('isBrandNew'), + function (aValue) { wasBrandNew = aValue }, + MochiKit.Base.method(this.user(), 'hasPendingChanges'), +//function (aValue) { console.log("2- USER.hasPendingChanges()", aValue); return aValue; }, Clipperz.Async.deferredIf('HasPendingChanges',[ MochiKit.Base.method(self, 'ask', { 'question': "Lose pending changes?", @@ -993,8 +998,18 @@ console.log("SET USER", aUser); ]), MochiKit.Base.method(currentPage, 'setProps', {'mode':'view'}), MochiKit.Base.method(this.user(), 'revertChanges'), - MochiKit.Base.method(self, 'updateSelectedCard', {'reference':aRecordReference}, false), - ], {trace:true}); + + MochiKit.Base.bind(function () { + var info; + if (wasBrandNew == true) { + info = null; + } else { + info = {'reference': aRecordReference}; + } + + this.updateSelectedCard(info, false); + }, this) + ], {trace:false}); }, //---------------------------------------------------------------------------- @@ -1027,7 +1042,14 @@ console.log("SET USER", aUser); //---------------------------------------------------------------------------- addCardClick_handler: function () { -console.log("ADD CARD CLICK"); + return Clipperz.Async.callbacks("MainController.addCardClick_handler", [ + MochiKit.Base.method(this.user(), 'createNewRecord'), + MochiKit.Base.methodcaller('reference'), + MochiKit.Base.method(this, 'refreshUI'), + MochiKit.Base.bind(function () { + this.pages()[this.currentPage()].setProps({'mode': 'edit'}); + }, this), + ], {trace:false}); }, deleteCard_handler: function (anEvent) { @@ -1057,6 +1079,19 @@ console.log("ADD CARD CLICK"); ], {trace:false}); }, + cloneCard_handler: function (anEvent) { +//console.log("CLONE CARD", anEvent['reference']); + return Clipperz.Async.callbacks("MainController.cloneCard_handler", [ + MochiKit.Base.method(this.user(), 'getRecord', anEvent['reference']), + MochiKit.Base.method(this.user(), 'cloneRecord'), + MochiKit.Base.methodcaller('reference'), + MochiKit.Base.method(this, 'refreshUI'), +// MochiKit.Base.bind(function () { +// this.pages()[this.currentPage()].setProps({'mode': 'edit'}); +// }, this), + ], {trace:false}); + }, + editCard_handler: function (anEvent) { //console.log("EDIT CARD", anEvent['reference']); this.pages()[this.currentPage()].setProps({'mode': 'edit'}); diff --git a/frontend/delta/tests/tests/Clipperz/PM/DataModel/User.test.js b/frontend/delta/tests/tests/Clipperz/PM/DataModel/User.test.js index b41bedf..73b1da3 100644 --- a/frontend/delta/tests/tests/Clipperz/PM/DataModel/User.test.js +++ b/frontend/delta/tests/tests/Clipperz/PM/DataModel/User.test.js @@ -1978,7 +1978,7 @@ console.log("PROXY", proxy); return deferredResult; }, - //------------------------------------------------------------------------- + //------------------------------------------------------------------------- 'changePassphrase_test': function (someTestArgs) { var deferredResult; @@ -2014,7 +2014,68 @@ console.log("PROXY", proxy); return deferredResult; }, - //------------------------------------------------------------------------- + //------------------------------------------------------------------------- + + 'cloneRecord_test': function (someTestArgs) { + var deferredResult; + var proxy; + var user; + var user2; + var recordID; + var clonedRecordID; + + proxy = new Clipperz.PM.Proxy.Test({shouldPayTolls:true, isDefault:true, readOnly:false}); + user = new Clipperz.PM.DataModel.User({username:'joe', getPassphraseFunction:function () { return 'clipperz';}}); + user2 = new Clipperz.PM.DataModel.User({username:'joe', getPassphraseFunction:function () { return 'clipperz';}}); + + recordID = '13a5e52976337ab210903cd04872588e1b21fb72bc183e91aa25c494b8138551'; + + deferredResult = new Clipperz.Async.Deferred("cloneRecord_test", someTestArgs); + deferredResult.addMethod(proxy.dataStore(), 'setupWithEncryptedData', testData['joe_clipperz_offline_copy_data']); + deferredResult.addMethod(user, 'login'); + + deferredResult.addMethod(user, 'getRecords'); + deferredResult.addCallback(MochiKit.Base.itemgetter('length')); + deferredResult.addTest(20, "This account has 20 cards"); + + deferredResult.addMethod(user, 'getRecord', recordID); + deferredResult.addMethodcaller('label'); + deferredResult.addTest("Amazon.com", "This is the card we are going to duplicate."); + + deferredResult.addMethod(user, 'getRecord', recordID); + deferredResult.addMethodcaller('fields'); + deferredResult.addCallback(MochiKit.Base.keys); + deferredResult.addCallback(MochiKit.Base.itemgetter('length')); + deferredResult.addTest(2, "The selected record has 2 fields"); + + deferredResult.addMethod(user, 'getRecord', recordID); + deferredResult.addMethod(user, 'cloneRecord'); + deferredResult.addMethodcaller('reference'); + deferredResult.addCallback(function (aReference) { clonedRecordID = aReference; return aReference; }); + + deferredResult.addMethod(user, 'getRecords'); + deferredResult.addCallback(MochiKit.Base.itemgetter('length')); + deferredResult.addTest(20 + 1, "The account now has one more record"); + + deferredResult.addCallback(function () { return clonedRecordID; }) + deferredResult.addMethod(user, 'getRecord'); + deferredResult.addMethodcaller('label'); + deferredResult.addTest("Amazon.com - copy", "This is the label of the cloned card."); + + deferredResult.addCallback(function () { return clonedRecordID; }) + deferredResult.addMethod(user, 'getRecord'); + deferredResult.addMethodcaller('fields'); + deferredResult.addCallback(MochiKit.Base.keys); + deferredResult.addCallback(MochiKit.Base.itemgetter('length')); + deferredResult.addTest(2, "The cloned record has 2 fields too"); + + + deferredResult.callback(); + + return deferredResult; + }, + + //------------------------------------------------------------------------- /* 'rearrangeRecordFieldOrderAndSave_test': function (someTestArgs) { var deferredResult;