summaryrefslogtreecommitdiff
path: root/frontend/delta/js/Clipperz/PM/DataModel
Side-by-side diff
Diffstat (limited to 'frontend/delta/js/Clipperz/PM/DataModel') (more/less context) (ignore whitespace changes)
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/DirectLogin.js1086
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/DirectLoginBinding.js120
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/DirectLoginFormValue.js101
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/DirectLoginInput.js192
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/EncryptedRemoteObject.js542
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/OneTimePassword.js350
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/Record.Version.Field.js186
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/Record.Version.js328
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/Record.js891
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/User.Header.Legacy.js182
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/User.Header.OneTimePasswords.js117
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/User.Header.Preferences.js48
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/User.Header.RecordIndex.js685
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/User.Subscription.js53
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/User.js827
15 files changed, 5708 insertions, 0 deletions
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/DirectLogin.js b/frontend/delta/js/Clipperz/PM/DataModel/DirectLogin.js
new file mode 100644
index 0000000..8db90de
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/DirectLogin.js
@@ -0,0 +1,1086 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+Clipperz.Base.module('Clipperz.PM.DataModel');
+
+Clipperz.PM.DataModel.DirectLogin = function(args) {
+ args = args || {};
+
+ Clipperz.PM.DataModel.DirectLogin.superclass.constructor.apply(this, arguments);
+
+ this._reference = args.reference
+ || Clipperz.PM.Crypto.randomKey();
+ this._record = args.record
+ || Clipperz.Base.exception.raise('MandatoryParameter');
+
+ this._retrieveIndexDataFunction = args.retrieveIndexDataFunction
+ || this.record().retrieveDirectLoginIndexDataFunction()
+ || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._setIndexDataFunction = args.setIndexDataFunction
+ || this.record().setDirectLoginIndexDataFunction()
+ || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._removeIndexDataFunction = args.removeIndexDataFunction
+ || this.record().removeDirectLoginIndexDataFunction()
+ || Clipperz.Base.exception.raise('MandatoryParameter');
+
+ this._inputs = null;
+ this._bindings = null;
+ this._formValues = null;
+
+// this._inputsDeferredLock = new MochiKit.Async.DeferredLock();
+// this._bindingsDeferredLock = new MochiKit.Async.DeferredLock();
+// this._formValuesDeferredLock = new MochiKit.Async.DeferredLock();
+
+ this._transientState = null;
+
+ this._isBrandNew = MochiKit.Base.isUndefinedOrNull(args.reference);
+
+ this.record().addDirectLogin(this);
+
+ return this;
+}
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.DirectLogin, Object, {
+
+ 'toString': function() {
+ return "DirectLogin (" + this.reference() + ")";
+ },
+
+ //=========================================================================
+
+ 'reference': function () {
+ return this._reference;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'record': function () {
+ return this._record;
+ },
+
+ //=========================================================================
+
+ 'isBrandNew': function () {
+ return this._isBrandNew;
+ },
+
+ //=========================================================================
+
+ 'removeIndexDataFunction': function () {
+ return this._removeIndexDataFunction;
+ },
+
+ 'remove': function () {
+ return Clipperz.Async.callbacks("DirectLogin.remove", [
+ MochiKit.Base.partial(this.removeIndexDataFunction(), this.reference()),
+ MochiKit.Base.method(this.record(), 'removeDirectLogin', this)
+ ], {trace:false});
+ },
+
+ //=========================================================================
+/*
+ 'inputsDeferredLock': function () {
+ return this._inputsDeferredLock;
+ },
+
+ 'bindingsDeferredLock': function () {
+ return this._bindingsDeferredLock;
+ },
+
+ 'formValuesDeferredLock': function () {
+ return this._formValuesDeferredLock;
+ },
+*/
+ //=========================================================================
+
+ 'label': function () {
+ return this.getIndexDataForKey('label');
+ },
+
+ 'setLabelKeepingBackwardCompatibilityWithBeta': function (aValue) {
+ return Clipperz.Async.callbacks("DirectLogin.setLabelKeepingBackwardCompatibilityWithBeta", [
+ MochiKit.Base.method(this, 'setIndexDataForKey', 'label', aValue),
+ MochiKit.Base.method(this, 'setValue', 'label', aValue)
+ ], {trace:false});
+ },
+
+ 'setLabel': function (aValue) {
+ return this.setLabelKeepingBackwardCompatibilityWithBeta(aValue);
+// return this.setIndexDataForKey('label', aValue);
+ },
+
+ //=========================================================================
+
+ 'favicon': function () {
+ return this.getIndexDataForKey('favicon');
+ },
+
+ 'setFavicon': function (aValue) {
+ return this.setIndexDataForKey('favicon', aValue);
+ },
+
+ 'faviconUrlWithBookmarkletConfiguration': function (aBookmarkletConfiguration) {
+ var result;
+
+ if (! MochiKit.Base.isUndefinedOrNull(aBookmarkletConfiguration['page']['favicon'])) {
+ result = aBookmarkletConfiguration['page']['favicon'];
+ } else if (! MochiKit.Base.isUndefinedOrNull(aBookmarkletConfiguration['form']['attributes']['action'])) {
+ var actionUrl;
+ var hostname;
+
+ actionUrl = aBookmarkletConfiguration['form']['attributes']['action'];
+ hostname = actionUrl.replace(/^https?:\/\/([^\/]*)\/.*/, '$1');
+ result = "http://" + hostname + "/favicon.ico";
+ } else {
+ result = null;
+ }
+
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'faviconData': function () {
+ var regexp = new RegExp('^data\:\/\/.*', 'i');
+
+ return Clipperz.Async.callbacks("DirectLogin.favicon", [
+ MochiKit.Base.method(this, 'getIndexDataForKey', 'favicon'),
+ MochiKit.Base.method(regexp, 'test'),
+ Clipperz.Async.deferredIf("is data URL", [
+ MochiKit.Base.method(this, 'getIndexDataForKey', 'favicon')
+ ], [
+ MochiKit.Base.method(this, 'transientState'),
+ MochiKit.Base.itemgetter('faviconData'),
+ Clipperz.Async.deferredIf('has a chaced value for the favicon data', [
+ MochiKit.Base.operator.identity
+ ], [
+ MochiKit.Base.method(this, 'getIndexDataForKey', 'favicon'),
+ MochiKit.Base.method(this, 'loadFaviconDataFromURL')
+ ])
+
+ ])
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'loadFaviconDataFromURL': function (anURL) {
+ var deferredResult;
+ var image;
+
+ deferredResult = new Clipperz.Async.Deferred("DirectLogin.loadFaviconDataFromURL", {trace:false});
+ deferredResult.addCallback(function (anEvent) {
+ var image = anEvent.src();
+ var canvas = document.createElement("canvas");
+ var result;
+
+ canvas.width = image.width;
+ canvas.height = image.height;
+
+ var ctx = canvas.getContext("2d");
+ ctx.drawImage(image, 0, 0);
+
+ result = canvas.toDataURL(/*"image/png"* /);
+
+ return result;
+ });
+ deferredResult.addErrback(MochiKit.Async.succeed, Clipperz.PM.Strings.getValue('defaultFaviconUrl'));
+ deferredResult.addBoth(MochiKit.Base.bind(function (aDataUrl) {
+ this.transientState()['faviconData'] = aDataUrl;
+
+ return aDataUrl;
+ }, this));
+
+ image = new Image();
+ MochiKit.Signal.connect(image, 'onload', MochiKit.Base.method(deferredResult, 'callback'));
+ MochiKit.Signal.connect(image, 'onerror', MochiKit.Base.method(deferredResult, 'errback'));
+ MochiKit.Signal.connect(image, 'onabort', MochiKit.Base.method(deferredResult, 'errback'));
+
+ image.src = anURL;
+
+ return deferredResult;
+ },
+*/
+
+ //=========================================================================
+
+ 'type': function () {
+ return this.getValue('formData.attributes.type')
+ },
+
+ //=========================================================================
+
+ 'serializedData': function () {
+ return Clipperz.Async.collectResults("DirectLogin.serializedData", {
+ 'bookmarkletVersion': MochiKit.Base.method(this, 'getValue', 'bookmarkletVersion'),
+ 'formData': MochiKit.Base.method(this, 'getValue', 'formData'),
+ 'formValues': MochiKit.Base.method(this, 'getValue', 'formValues'),
+ 'bindingData': [
+ MochiKit.Base.method(this, 'bindings'),
+ function (someBindings) {
+ var result;
+ var bindingKey;
+
+ result = {}
+ for (bindingKey in someBindings) {
+ result[bindingKey] = someBindings[bindingKey].serializedData();
+ }
+
+ return result;
+ }
+ ]
+ }, {trace:false})()
+ },
+
+ //=========================================================================
+/*
+ 'fixFormDataFromBookmarkletVersion_0_1': function(aValue) {
+//{"type":"radio", "name":"action", "value":"new-user", "checked":false }, { "type":"radio", "name":"action", "value":"sign-in", "checked":true }
+// ||
+// \ /
+// \/
+//{"name":"dominio", "type":"radio", "options":[{"value":"@alice.it", "checked":true}, {"value":"@tin.it", "checked":false}, {"value":"@virgilio.it", "checked":false}, {"value":"@tim.it", "checked":false}]}
+ var result;
+ var inputs;
+ var updatedInputs;
+ var radios;
+
+ result = aValue;
+ inputs = aValue['inputs'];
+
+ updatedInputs = MochiKit.Base.filter(function(anInput) {
+ var result;
+ var type;
+
+ type = anInput['type'] || 'text';
+ result = type.toLowerCase() != 'radio';
+
+ return result;
+ }, inputs);
+ radios = MochiKit.Base.filter(function(anInput) {
+ var result;
+ var type;
+
+ type = anInput['type'] || 'text';
+ result = type.toLowerCase() == 'radio';
+
+ return result;
+ }, inputs);
+
+ if (radios.length > 0) {
+ var updatedRadios;
+
+ updatedRadios = {};
+ MochiKit.Iter.forEach(radios, MochiKit.Base.bind(function(aRadio) {
+ var radioConfiguration;
+
+ radioConfiguration = updatedRadios[aRadio['name']];
+ if (radioConfiguration == null) {
+ radioConfiguration = {type:'radio', name:aRadio['name'], options:[]};
+ updatedRadios[aRadio['name']] = radioConfiguration;
+ }
+
+// TODO: remove the value: field and replace it with element.dom.value = <some value>
+ radioConfiguration.options.push({value:aRadio['value'], checked:aRadio['checked']});
+
+// TODO: shoud remove the 'formValues' call, as it is now deferred
+// if ((aRadio['checked'] == true) && (this.formValues()[aRadio['name']] == null)) {
+// this.formValues()[aRadio['name']] = aRadio['value'];
+// }
+ }, this))
+
+ updatedInputs = MochiKit.Base.concat(updatedInputs, MochiKit.Base.values(updatedRadios));
+ }
+
+ delete result.inputs;
+ result.inputs = updatedInputs;
+
+ return result;
+ },
+
+ '_fixConfiguration': function (aConfiguration) {
+ var fixedConfiguration;
+// var inputs;
+// var bindings;
+// var i,c;
+
+ fixedConfiguration = Clipperz.Base.deepClone(aConfiguration);
+
+//Clipperz.log("PROCESS CONFIGURATION", aConfiguration);
+ switch (aConfiguration['bookmarkletVersion']) {
+ case '0.1':
+ fixedConfiguration['formData'] = this.fixFormDataFromBookmarkletVersion_0_1(aConfiguration['formData']);
+ break;
+ case '0.2':
+ fixedConfiguration['formData'] = aConfiguration['formData'];
+ break;
+ }
+
+/ *
+ aConfiguration['_inputs'] = [];
+ c = formData['inputs'].length;
+ for (i=0; i<c; i++) {
+ aConfiguration['_inputs'].push(new Clipperz.PM.DataModel.DirectLoginInput(formData['inputs'][i]));
+ }
+* /
+/ *
+ aConfiguration['_bindings'] = {};
+ if (aConfiguration['legacyBindingData'] == null) {
+ if (aConfiguration['bindingData'] != null) {
+ var bindingKey;
+
+ for (bindingKey in aConfiguration['bindingData']) {
+ var newBinding;
+
+ newBinding = new Clipperz.PM.DataModel.DirectLoginBinding(bindingKey, {fieldKey:aConfiguration['bindingData'][bindingKey]});
+ aConfiguration['_bindings'][newBinding.key()] = newBinding;
+ }
+ } else {
+ var editableFields;
+
+ editableFields = MochiKit.Base.filter(function(aField) {
+ var result;
+ var type;
+
+ type = aField['type'].toLowerCase();
+ result = ((type != 'hidden') && (type != 'submit') && (type != 'checkbox') && (type != 'radio') && (type != 'select'));
+
+ return result;
+ }, aConfiguration['_inputs']);
+
+ MochiKit.Iter.forEach(editableFields, MochiKit.Base.bind(function(anEditableField) {
+ var newBinding;
+
+ newBinding = new Clipperz.PM.DataModel.DirectLoginBinding(anEditableField['name']);
+ aConfiguration['_bindings'][newBinding.key()] = newBinding;
+ }, this));
+ }
+
+ } else {
+ var bindingKey;
+
+ for (bindingKey in aConfiguration['legacyBindingData']) {
+ var newBinding;
+
+ newBinding = new Clipperz.PM.DataModel.DirectLoginBinding(bindingKey, {fieldName:aConfiguration['legacyBindingData'][bindingKey]});
+ aConfiguration['_bindings'][newBinding.key()] = newBinding;
+ }
+ }
+* /
+
+ return fixedConfiguration;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getObjectDataStore': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("DirectLogin.getObjectDataStore", {trace:false});
+ deferredResult.acquireLock(this.objectDataStoreDeferredLock());
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ var innerDeferredResult;
+
+ if (this._objectDataStore == null) {
+ this._objectDataStore = new Clipperz.KeyValueObjectStore();
+
+ innerDeferredResult = new Clipperz.Async.Deferred("DirectLogin.getObjectDataStore <inner deferred>", {trace:false});
+// innerDeferredResult.addMethod(this.record(), 'getValue', 'directLogins' + '.' + this.reference());
+ innerDeferredResult.addMethod(this, 'getValue', ''),
+ innerDeferredResult.addMethod(this, 'setOriginalState');
+ innerDeferredResult.addMethod(this, '_fixConfiguration');
+ innerDeferredResult.addMethod(this._objectDataStore, 'initWithValues');
+// innerDeferredResult.addMethod(this._objectDataStore, 'setValues');
+ innerDeferredResult.callback();
+ } else {
+ innerDeferredResult = MochiKit.Async.succeed(this._objectDataStore);
+ }
+
+ return innerDeferredResult;
+ }, this));
+ deferredResult.releaseLock(this.objectDataStoreDeferredLock());
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'hasInitiatedObjectDataStore': function () {
+ return (this._objectDataStore != null);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'resetObjectDataStore': function () {
+ this._objectDataStore.removeAllData();
+ this._objectDataStore = null;
+ },
+*/
+ //=========================================================================
+
+ 'bookmarkletConfiguration': function () {
+ return Clipperz.Async.callbacks("DirectLogin.bookmarkletConfiguration", [
+ Clipperz.Async.collectResults("DirectLogin.bookmarkletConfiguration <inner results>", {
+ 'label': MochiKit.Base.method(this, 'label'),
+ 'configuration': MochiKit.Base.method(this, 'getValue', '')
+ }, {trace:false}),
+ function (someValues) {
+ var result;
+
+ if (someValues['configuration'] != null) {
+ var configuration;
+
+ configuration = {
+ 'page': {
+ 'title': someValues['label']
+ // 'favicon'
+ // 'url'
+ },
+ 'form': someValues['configuration']['formData'],
+ 'version': someValues['configuration']['bookmarkletVersion']
+ }
+
+ result = Clipperz.Base.formatJSON(configuration);
+ } else {
+ result = '';
+ }
+
+ return result;
+ }
+ ], {trace:false});
+
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'setBookmarkletConfiguration': function (aValue) {
+ var bookmarkletConfiguration;
+
+ bookmarkletConfiguration = Clipperz.PM.DataModel.DirectLogin.checkBookmarkletConfiguration(aValue);
+
+ return Clipperz.Async.callbacks("DirectLogin.setBookmarkletConfiguration", [
+ MochiKit.Base.method(this, 'setValue', 'formData', bookmarkletConfiguration['form']),
+ MochiKit.Base.method(this, 'setValue', 'bookmarkletVersion', bookmarkletConfiguration['version']),
+
+ MochiKit.Base.method(this, 'favicon'),
+ Clipperz.Async.deferredIf("the favicon is not set", [
+ ], [
+ MochiKit.Base.method(this, 'faviconUrlWithBookmarkletConfiguration', bookmarkletConfiguration),
+ MochiKit.Base.method(this, 'setFavicon')
+ ]),
+
+ MochiKit.Base.method(this, 'updateInputsAfterChangingBookmarkletConfiguration'),
+ MochiKit.Base.method(this, 'updateFormValuesAfterChangingBookmarkletConfiguration'),
+ MochiKit.Base.method(this, 'updateBindingsAfterChangingBookmarkletConfiguration'),
+
+ MochiKit.Base.noop
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'formAttributes': function () {
+ return this.getValue('formData.attributes');
+ },
+
+ //=========================================================================
+
+ 'inputs': function () {
+ return Clipperz.Async.callbacks("DirectLogin.inputs", [
+ Clipperz.Async.deferredIf("this._inputs is defined", [
+ ], [
+ MochiKit.Base.method(this, 'updateInputsAfterChangingBookmarkletConfiguration')
+ ])
+ ], {trace:false}, this._inputs);
+ },
+
+ 'setInputWithFormDataConfiguration': function (aFormDataConfiguration) {
+ this._inputs = {};
+
+ if (aFormDataConfiguration != null) {
+ MochiKit.Iter.forEach(aFormDataConfiguration['inputs'], MochiKit.Base.bind(function (anInputData) {
+ var newInput;
+
+ newInput = new Clipperz.PM.DataModel.DirectLoginInput(anInputData);
+ this._inputs[newInput.name()] = newInput;
+ }, this));
+ }
+
+ return this._inputs;
+ },
+
+ 'updateInputsAfterChangingBookmarkletConfiguration': function () {
+ return Clipperz.Async.callbacks("DirectLogin.updateInputsAfterChangingBookmarkletConfiguration", [
+ MochiKit.Base.method(this, 'getValue', 'formData'),
+ MochiKit.Base.method(this, 'setInputWithFormDataConfiguration')
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'inputValues': function () {
+ return Clipperz.Async.callbacks("DirectLogin.inputValues", [
+ MochiKit.Base.method(this, 'inputs'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.partial(MochiKit.Base.method(this, 'inputValue'))),
+ Clipperz.Async.collectAll,
+ Clipperz.Base.mergeItems
+ ], {trace:false});
+ },
+
+ 'inputValue': function (anInput) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("DirectLogin.inputValue", {trace:false});
+
+ if (anInput.needsFormValue()) {
+ deferredResult.addMethod(this, 'formValues');
+ deferredResult.addCallback(MochiKit.Base.itemgetter(anInput.name()));
+ deferredResult.addMethodcaller('value');
+ } else if (anInput.needsBinding()) {
+ deferredResult.addMethod(this, 'bindings');
+ deferredResult.addCallback(MochiKit.Base.itemgetter(anInput.name()));
+ deferredResult.addMethodcaller('field');
+ deferredResult.addMethodcaller('value');
+ } else {
+ deferredResult.addCallback(MochiKit.Async.succeed, anInput.value());
+ }
+ deferredResult.addCallback(function (anActualValue) {
+ return [anInput.name(), anActualValue];
+ });
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'bindings': function () {
+ return Clipperz.Async.callbacks("DirectLogin.bindings", [
+ Clipperz.Async.deferredIf("this._bindings is defined", [
+ ], [
+ MochiKit.Base.method(this, 'updateBindingsAfterChangingBookmarkletConfiguration'),
+ MochiKit.Base.bind(function () { return this._bindings;}, this)
+ ])
+ ], {trace:false}, this._bindings);
+ },
+
+ 'bindFormFieldWithLabelToRecordFieldWithLabel': function (aFormFieldLabel, aRecordFieldLabel) {
+ return Clipperz.Async.callbacks("DirectLogin.bindFormFieldWithLabelToCardFieldWithLabel", [
+ Clipperz.Async.collectResults("DirectLogin.bindFormFieldWithLabelToCardFieldWithLabel - collect results", {
+ 'binding': [
+ MochiKit.Base.method(this, 'bindings'),
+ MochiKit.Base.itemgetter(aFormFieldLabel)
+ ],
+ 'field': [
+ MochiKit.Base.method(this.record(), 'fieldWithLabel', aRecordFieldLabel)
+ ]
+ }),
+ function (someValues) {
+ someValues['binding'].setField(someValues['field'])
+ }
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'bindingValues': function () {
+ return Clipperz.Async.callbacks("DirectLogin.bindingValues", [
+ Clipperz.Async.collectResults("DirectLogin.bindingValues [collectResults]", {
+ 'fieldValues': [
+ MochiKit.Base.method(this, 'record'),
+ MochiKit.Base.methodcaller('getFieldsValues')
+ ],
+ 'bindings': MochiKit.Base.method(this, 'bindings')
+ }, {trace:false}),
+ function (someData) {
+ var result;
+ var bindingKey;
+
+ result = {};
+ for (bindingKey in someData['bindings']) {
+ result[bindingKey] = someData['fieldValues'][someData['bindings'][bindingKey].fieldKey()]['value'];
+ }
+
+ return result;
+ }
+ ], {trace:false});
+ },
+*/
+ //-------------------------------------------------------------------------
+
+ 'updateBindingsAfterChangingBookmarkletConfiguration': function () {
+ return Clipperz.Async.callbacks("DirectLogin.updateBindingsAfterChangingBookmarkletConfiguration", [
+ Clipperz.Async.collectResults("DirectLogin.updateBindingsAfterChangingBookmarkletConfiguration<collect results>", {
+ 'currentValues': MochiKit.Base.method(this, 'getValue', ''),
+ 'originalValues': MochiKit.Base.method(this, 'originalConfiguration'),
+ 'inputs': MochiKit.Base.method(this, 'inputs')
+ }, {trace:false}),
+ MochiKit.Base.bind(function (someValues) {
+ var availableBindingValues;
+ var inputRequiringBindingValues;
+ var newBindingValues;
+
+ if (MochiKit.Base.isUndefinedOrNull(someValues['originalValues']) || MochiKit.Base.isUndefinedOrNull(someValues['originalValues']['bindingData'])) {
+ availableBindingValues = {};
+ } else {
+ availableBindingValues = Clipperz.Base.deepClone(someValues['originalValues']['bindingData'])
+ }
+
+ if (someValues['currentValues'] != null) {
+ MochiKit.Base.update(availableBindingValues, someValues['currentValues']['bindingData']);
+ }
+
+ this._bindings = {};
+ newBindingValues = {}
+ MochiKit.Iter.forEach(MochiKit.Base.filter(MochiKit.Base.methodcaller('needsBinding'), MochiKit.Base.values(someValues['inputs'])), MochiKit.Base.bind(function (anInput) {
+ var newBinding;
+
+ newBindingValues[anInput.name()] = availableBindingValues[anInput.name()];
+ newBinding = new Clipperz.PM.DataModel.DirectLoginBinding(this, {
+ 'key': anInput.name(),
+ 'field': availableBindingValues[anInput.name()]
+ });
+
+ this._bindings[anInput.name()] = newBinding;
+ }, this))
+
+ return newBindingValues;
+
+/*
+ this._bindings = {};
+
+ if (someValues['currentValues'] != null) {
+ if (someValues['currentValues']['bindingData'] != null) {
+ var bindingKey;
+
+ for (bindingKey in someValues['currentValues']['bindingData']) {
+ var newBinding;
+
+ newBinding = new Clipperz.PM.DataModel.DirectLoginBinding(this, {
+ 'key': bindingKey,
+ 'field': someValues['currentValues']['bindingData'][bindingKey]
+ });
+ this._bindings[newBinding.key()] = newBinding;
+ }
+ } else if (someValues['currentValues']['legacyBindingData'] == null) {
+ var bindingKey;
+
+ for (bindingKey in someValues['currentValues']['legacyBindingData']) {
+ var newBinding;
+
+ newBinding = new Clipperz.PM.DataModel.DirectLoginBinding(this, {
+ 'key': bindingKey,
+ 'field': someValues['currentValues']['legacyBindingData'][bindingKey]
+ });
+ this._bindings[newBinding.key()] = newBinding;
+ }
+ } else {
+ WTF = TODO;
+ }
+ }
+
+ return this._bindings;
+*/
+ }, this),
+ MochiKit.Base.method(this, 'setValue', 'bindingData')
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'formValues': function () {
+ return Clipperz.Async.callbacks("DirectLogin.formValues", [
+ Clipperz.Async.deferredIf("this._formValues is defined", [
+ ], [
+ MochiKit.Base.method(this, 'updateFormValuesAfterChangingBookmarkletConfiguration'),
+ MochiKit.Base.bind(function () { return this._formValues;}, this)
+ ])
+ ], {trace:false}, this._formValues);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'updateFormValuesAfterChangingBookmarkletConfiguration': function () {
+ return Clipperz.Async.callbacks("DirectLogin.updateFormValuesAfterChangingBookmarkletConfiguration", [
+ Clipperz.Async.collectResults("DirectLogin.updateFormValuesAfterChangingBookmarkletConfiguration <collect results>", {
+ 'currentValues': MochiKit.Base.method(this, 'getValue', ''),
+ 'originalValues': MochiKit.Base.method(this, 'originalConfiguration'),
+ 'inputs': MochiKit.Base.method(this, 'inputs')
+ }, {trace:false}),
+ MochiKit.Base.bind(function (someValues) {
+ var availableFormValues;
+ var inputRequiringFormValues;
+ var newFormValues;
+
+ if (MochiKit.Base.isUndefinedOrNull(someValues['originalValues']) || MochiKit.Base.isUndefinedOrNull(someValues['originalValues']['formValues'])) {
+ availableFormValues = {};
+ } else {
+ availableFormValues = Clipperz.Base.deepClone(someValues['originalValues']['formValues'])
+ }
+
+ MochiKit.Base.update(availableFormValues, someValues['currentValues']['formValues']);
+
+ this._formValues = {};
+ newFormValues = {};
+ MochiKit.Iter.forEach(MochiKit.Base.filter(MochiKit.Base.methodcaller('needsFormValue'), MochiKit.Base.values(someValues['inputs'])), MochiKit.Base.bind(function (anInput) {
+ var newFormValue;
+ var fieldOptions;
+
+ fieldOptions = {
+ 'type': anInput.type(),
+ 'options': anInput.options()
+ };
+
+ newFormValues[anInput.name()] = availableFormValues[anInput.name()]
+ newFormValue = new Clipperz.PM.DataModel.DirectLoginFormValue(this, {
+ 'key': anInput.name(),
+ 'fieldOptions': fieldOptions,
+ 'value': availableFormValues[anInput.name()]
+ });
+
+ this._formValues[anInput.name()] = newFormValue;
+ }, this))
+
+ return newFormValues;
+ }, this),
+ MochiKit.Base.method(this, 'setValue', 'formValues')
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'retrieveIndexDataFunction': function () {
+ return this._retrieveIndexDataFunction;
+ },
+
+ 'getIndexDataForKey': function (aKey) {
+ return Clipperz.Async.callbacks("DirectLogin.getIndexDataForKey", [
+ MochiKit.Base.partial(this.retrieveIndexDataFunction(), this.reference()),
+ Clipperz.Async.deferredIf("DirectLogin.getIndexDataForKey - index data not null", [
+ MochiKit.Base.itemgetter(aKey)
+ ],[
+ MochiKit.Async.succeed
+ ])
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'setIndexDataForKey': function (aKey, aValue) {
+ return Clipperz.Async.callbacks("DirectLogin.setValueForKey", [
+ MochiKit.Base.method(this, 'getIndexDataForKey', aKey),
+ MochiKit.Base.bind(function (anActualValue) {
+ var transientStateKey;
+
+ transientStateKey = 'original_' + aKey;
+ if (MochiKit.Base.isUndefinedOrNull(this.transientState()[transientStateKey])) {
+ if (anActualValue != aValue) {
+ this.transientState()[transientStateKey] = anActualValue;
+ }
+ } else if (this.transientState()[transientStateKey] == aValue) {
+ this.transientState()[transientStateKey] = null;
+ }
+ }, this),
+ MochiKit.Base.partial(this._setIndexDataFunction, this.reference(), aKey, aValue)
+ ], {trace:false})
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'setValueForKey': function (aKey, aValue) {
+ return Clipperz.Async.callbacks("DirectLogin.setValueForKey", [
+ MochiKit.Base.method(this, 'getIndexDataForKey', aKey),
+ MochiKit.Base.bind(function (anActualValue) {
+ var transientStateKey;
+
+ transientStateKey = 'original_' + aKey;
+ if (MochiKit.Base.isUndefinedOrNull(this.transientState()[transientStateKey])) {
+ if (anActualValue != aValue) {
+ this.transientState()[transientStateKey] = anActualValue;
+ }
+ } else if (this.transientState()[transientStateKey] == aValue) {
+ this.transientState()[transientStateKey] = null;
+ }
+ }, this),
+ MochiKit.Base.method(this, 'setIndexDataForKey', aKey, aValue)
+ ], {trace:false})
+ },
+*/
+ //=========================================================================
+/*
+ 'storedConfiguration': function () {
+ return this.record().getValue('directLogins' + '.' + this.reference());
+ },
+
+// 'setStoredConfiguration': function (aValue) {
+// return this.record().setValue('directLogins' + '.' + this.reference(), aValue);
+// },
+*/
+ //=========================================================================
+
+ 'hasPendingChanges': function () {
+ var result;
+ var deferredResult;
+
+ result = false;
+ result = result || this.isBrandNew();
+ result = result || (! MochiKit.Base.isUndefinedOrNull(this.transientState()['original_label']));
+ result = result || (! MochiKit.Base.isUndefinedOrNull(this.transientState()['original_favicon']));
+
+ if ((result == false) && (this.originalConfiguration() != null)) {
+ deferredResult = Clipperz.Async.callbacks("DirectLogin.hasPendingChanges", [
+ MochiKit.Base.method(this, 'serializedData'),
+ MochiKit.Base.bind(function (aCurrentConfiguration) {
+ var originalConfiguration;
+ var currentConfiguration;
+ var result;
+
+ originalConfiguration = this.originalConfiguration();
+ currentConfiguration = aCurrentConfiguration;
+
+ result = false;
+ result = result || (MochiKit.Base.compare(originalConfiguration['bookmarkletVersion'], currentConfiguration['bookmarkletVersion']) != 0);
+ result = result || (MochiKit.Base.compare(originalConfiguration['formData'], currentConfiguration['formData']) != 0);
+ result = result || (MochiKit.Base.compare(originalConfiguration['formValues'], currentConfiguration['formValues']) != 0);
+ result = result || (MochiKit.Base.compare(originalConfiguration['bindingData'], currentConfiguration['bindingData']) != 0);
+
+ return result;
+ }, this)
+ ], {trace:false});
+ } else {
+ deferredResult = MochiKit.Async.succeed(result);
+ }
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'revertChanges': function () {
+ var deferredResult;
+
+ if (this.transientState()['original_label'] != null) {
+ this.setLabel(this.transientState()['original_label']);
+ }
+
+ if (this.transientState()['original_favicon'] != null) {
+ this.setFavicon(this.transientState()['original_favicon']);
+ }
+
+ if (this.originalConfiguration() != null) {
+ deferredResult = this.setValue('', this.originalConfiguration());
+ } else {
+ deferredResult = MochiKit.Async.succeed();
+ }
+
+ this._inputs = null;
+ this._bindings = null;
+ this._formValues = null;
+
+ this.resetTransientState(false);
+
+/*
+ if (this.hasInitiatedObjectDataStore()) {
+ deferredResult = Clipperz.Async.callbacks("DirectLogin.revertChanges", [
+// MochiKit.Base.method(this.record(), 'setValue', 'directLogins' + '.' + this.reference(), this.originalState()),
+ MochiKit.Base.method(this, 'setValue', '', this.originalState()),
+ MochiKit.Base.method(this, 'resetObjectDataStore')
+ ], {trace:false})
+ } else {
+ deferredResult = MochiKit.Async.succeed();
+ }
+*/
+ return deferredResult;
+ },
+
+
+ //=========================================================================
+
+ 'transientState': function () {
+ if (this._transientState == null) {
+ this._transientState = {}
+ }
+
+ return this._transientState;
+ },
+
+ 'resetTransientState': function (isCommitting) {
+ this._transientState = null;
+ },
+
+ 'commitTransientState': function (isCommitting) {
+ this._transientState = null;
+ this._isBrandNew = false;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'originalConfiguration': function () {
+ return this.transientState()['original_configuration'];
+ },
+
+ 'setOriginalConfiguration': function (aConfiguration) {
+ this.transientState()['original_configuration'] = Clipperz.Base.deepClone(aConfiguration);
+ },
+
+ //=========================================================================
+
+ 'actualKey': function (aValueKey) {
+ var actualKey;
+
+ actualKey = 'directLogins' + '.' + this.reference();
+ if (aValueKey != '') {
+ actualKey = actualKey + '.' + aValueKey;
+ }
+
+ return actualKey;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getValue': function (aValueKey) {
+ return this.record().getValue(this.actualKey(aValueKey));
+ },
+
+ 'setValue': function (aValueKey, aValue) {
+// return this.record().setValue(this.actualKey(aValueKey), aValue);
+
+ return Clipperz.Async.callbacks("DirectLogin.setValue", [
+ MochiKit.Base.method(this, 'getValue', ''),
+ MochiKit.Base.bind(function (aValue) {
+ if (this.originalConfiguration() == null) {
+ this.setOriginalConfiguration(aValue);
+ }
+ }, this),
+// MochiKit.Base.method(this, 'originalConfiguration'),
+// Clipperz.Async.deferredIf("originalConfiguration has been set", [
+// ], [
+// MochiKit.Base.method(this, 'getValue', ''),
+// MochiKit.Base.method(this, 'setOriginalConfiguration')
+// ]),
+ MochiKit.Base.method(this.record(), 'setValue', this.actualKey(aValueKey), aValue)
+ ], {trace:false});
+ },
+
+ 'removeValue': function (aValueKey) {
+// return this.record().removeValue(this.actualKey(aValueKey));
+
+ return Clipperz.Async.callbacks("DirectLogin.setValue", [
+ MochiKit.Base.method(this, 'originalConfiguration'),
+ Clipperz.Async.deferredIf("originalConfiguration has been set", [
+ ], [
+ MochiKit.Base.method(this, 'getValue', ''),
+ MochiKit.Base.method(this, 'setOriginalConfiguration')
+ ]),
+ MochiKit.Base.method(this.record(), 'removeValue', this.actualKey(aValueKey))
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'content': function () {
+// return this.serializedData();
+// return MochiKit.Async.succeed(this);
+
+ var deferredResult;
+ var fieldValues;
+
+ fieldValues = {};
+ deferredResult = new Clipperz.Async.Deferred("DirectLogin.content", {trace:false});
+ deferredResult.addMethod(this, 'reference');
+ deferredResult.addCallback(function (aValue) { fieldValues['reference'] = aValue; });
+ deferredResult.addMethod(this, 'label');
+ deferredResult.addCallback(function (aValue) { fieldValues['label'] = aValue; });
+ deferredResult.addMethod(this, 'favicon');
+ deferredResult.addCallback(function (aValue) { fieldValues['favicon'] = aValue; });
+ deferredResult.addCallback(function () { return fieldValues; });
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'deleteAllCleanTextData': function () {
+ this._inputs = null;
+ this._bindings = null;
+ this._formValues = null;
+
+ this.resetTransientState();
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'hasAnyCleanTextData': function () {
+ var result;
+
+ result = false;
+
+ result = result || (this._inputs != null);
+ result = result || (this._bindings != null);
+ result = result || (this._formValues != null);
+ result = result || (MochiKit.Base.keys(this.transientState()).length != 0);
+
+ return MochiKit.Async.succeed(result);
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
+//#############################################################################
+
+Clipperz.PM.DataModel.DirectLogin.exception = {
+ 'WrongBookmarkletConfiguration': new MochiKit.Base.NamedError("Clipperz.PM.DataModel.DirectLogin.exception.WrongBookmarkletConfiguration")
+};
+
+Clipperz.PM.DataModel.DirectLogin.checkBookmarkletConfiguration = function(aConfiguration) {
+ var configuration;
+
+ try {
+ configuration = Clipperz.Base.evalJSON(aConfiguration);
+// configuration = Clipperz.PM.BookmarkletProcessor.sanitizeBookmarkletConfiguration(configuration);
+
+ if (MochiKit.Base.isUndefinedOrNull(configuration['page']['title'])
+ || MochiKit.Base.isUndefinedOrNull(configuration['form']['attributes']['action'])
+// || MochiKit.Base.isUndefinedOrNull(configuration['form']['attributes']['method'])
+ || MochiKit.Base.isUndefinedOrNull(configuration['form']['inputs'])
+ || MochiKit.Base.isUndefinedOrNull(configuration['version'])
+ ) {
+ throw Clipperz.PM.DataModel.DirectLogin.exception.WrongBookmarkletConfiguration;
+ }
+
+// if (MochiKit.Base.isUndefinedOrNull(configuration['favicon'])) {
+// throw Clipperz.PM.DataModel.DirectLogin.exception.WrongBookmarkletConfiguration;
+// }
+
+ } catch (exception) {
+ throw exception;
+ }
+
+ return configuration;
+};
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/DirectLoginBinding.js b/frontend/delta/js/Clipperz/PM/DataModel/DirectLoginBinding.js
new file mode 100644
index 0000000..a8ebb97
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/DirectLoginBinding.js
@@ -0,0 +1,120 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; }
+
+
+//#############################################################################
+
+Clipperz.PM.DataModel.DirectLoginBinding = function(aDirectLogin, args) {
+ args = args || {};
+
+ this._directLogin = aDirectLogin|| Clipperz.Base.exception.raise('MandatoryParameter');
+
+ this._key = args.key || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._fieldKey = args.field || /* this.directLogin().fieldWithName(args.fieldName).reference() || */ null;
+
+ return this;
+}
+
+Clipperz.PM.DataModel.DirectLoginBinding.prototype = MochiKit.Base.update(null, {
+
+ 'toString': function() {
+ return "DirectLoginBinding (" + this.key() + ", " + this.fieldKey() + ")";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'directLogin': function () {
+ return this._directLogin;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'key': function() {
+ return this._key;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'fieldKey': function() {
+ return this._fieldKey;
+ },
+
+ 'setFieldKey': function(aValue) {
+ this._fieldKey = aValue;
+
+ return this.directLogin().setValue('bindingData' + '.' + this.key(), aValue);
+ },
+
+// 'fieldName': function() {
+// return this._fieldName;
+// },
+
+ //-------------------------------------------------------------------------
+
+ 'field': function() {
+ var deferredResult;
+
+ if (this.fieldKey() != null) {
+ deferredResult = Clipperz.Async.callbacks("DirectLoginBinding.field [1]", [
+ MochiKit.Base.method(this.directLogin().record(), 'fields'),
+ MochiKit.Base.itemgetter(this.fieldKey())
+ ], {trace:false});
+// } else if (this.fieldName() != null) {
+// WTF = TODO;
+// result = this.directLogin().record().fieldWithName(this.fieldName());
+//
+// this.setFieldKey(result.key());
+ } else {
+ deferredResult = MochiKit.Async.succeed(null);
+ }
+
+ return deferredResult;
+ },
+
+ 'setField': function (aField) {
+ this.setFieldKey(aField.reference());
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'fieldValue': function () {
+ return Clipperz.Async.callbacks("DirectLoginBinding.fieldValue", [
+ MochiKit.Base.method('field'),
+ MochiKit.Base.methodcaller('value')
+ ], {trace:false});
+ },
+*/
+ //-------------------------------------------------------------------------
+
+ 'serializedData': function() {
+ return this.fieldKey();
+ },
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+});
+
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/DirectLoginFormValue.js b/frontend/delta/js/Clipperz/PM/DataModel/DirectLoginFormValue.js
new file mode 100644
index 0000000..2429f88
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/DirectLoginFormValue.js
@@ -0,0 +1,101 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; }
+
+
+//#############################################################################
+
+Clipperz.PM.DataModel.DirectLoginFormValue = function(aDirectLogin, args) {
+ args = args || {};
+
+ this._directLogin = aDirectLogin|| Clipperz.Base.exception.raise('MandatoryParameter');
+
+ this._key = args.key || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._fieldOptions = args.fieldOptions || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._value = args.value || null;
+
+ return this;
+}
+
+Clipperz.PM.DataModel.DirectLoginFormValue.prototype = MochiKit.Base.update(null, {
+
+ 'toString': function() {
+ return "DirectLoginFormValue (" + this.key() + ", " + this.value() + ")";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'directLogin': function () {
+ return this._directLogin;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'key': function() {
+ return this._key;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'fieldOptions': function() {
+ return this._fieldOptions;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'type': function () {
+ return this.fieldOptions()['type'];
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'value': function() {
+ var result;
+
+ result = this._value;
+
+// if ((result == null) && (this.type() == 'checkbox')) {
+// result = false;
+// };
+
+ return result;
+ },
+
+ 'setValue': function (aValue) {
+ this._value = aValue;
+ return this.directLogin().setValue('formValues' + '.' + this.key(), aValue);
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'serializedData': function() {
+ return this.value();
+ },
+*/
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+});
+
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/DirectLoginInput.js b/frontend/delta/js/Clipperz/PM/DataModel/DirectLoginInput.js
new file mode 100644
index 0000000..d9995fc
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/DirectLoginInput.js
@@ -0,0 +1,192 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; }
+
+//#############################################################################
+
+Clipperz.PM.DataModel.DirectLoginInput = function(args) {
+ this._args = args;
+
+ return this;
+}
+
+Clipperz.PM.DataModel.DirectLoginInput.prototype = MochiKit.Base.update(null, {
+
+ 'args': function() {
+ return this._args;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'name': function() {
+ return this.args()['name'];
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'type': function() {
+ var result;
+
+ result = this.args()['type'];
+
+ if (result != null) {
+ result = result.toLowerCase();
+ }
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'options': function() {
+ return this.args()['options'];
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'value': function() {
+ return this.args()['value'];
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'formConfiguration': function(someFormValues, someBindings, someFields) {
+ var result;
+
+ if (this.shouldSetValue()) {
+ switch (this.type()) {
+ case 'select':
+ var currentValue;
+ var options;
+
+// currentValue = this.directLogin()._configuration['formValues'][this.name()];
+ currentValue = someFormValues[this.name()];
+ options = this.args()['options'];
+
+ result = MochiKit.DOM.SELECT({name:this.name()},
+ MochiKit.Base.map(function(anOption) {
+ var options;
+
+ options = {value:anOption['value']};
+ if (currentValue == anOption['value']) {
+ options.selected = true;
+ }
+
+ return MochiKit.DOM.OPTION(options, anOption['label'])
+ }, options)
+ )
+ break;
+ case 'checkbox':
+ var options;
+
+ options = {type:'checkbox', name: this.name()};
+// if (this.directLogin()._configuration['formValues'][this.name()] == true) {
+ if (someFormValues[this.name()] == true) {
+ options['checked'] = true;
+ };
+
+ result = MochiKit.DOM.INPUT(options, null);
+ break;
+ case 'radio':
+ var currentName;
+ var currentValue;
+ var options;
+
+ currentName = this.name();
+// currentValue = this.directLogin()._configuration['formValues'][this.name()];
+ currentValue = someFormValues[this.name()];
+ options = this.args()['options'];
+
+ result = MochiKit.DOM.DIV(null,
+ MochiKit.Base.map(function(anOption) {
+ var options;
+ var isChecked;
+ var inputNode;
+ var divNode;
+
+ options = {type:'radio', name:currentName, value:anOption['value']}
+ isChecked = (currentValue == anOption['value']);
+ if (isChecked) {
+ options.checked = true;
+ }
+
+ if (Clipperz_IEisBroken == true) {
+ var checkedValue;
+
+ checkedValue = (isChecked ? " CHECKED" : "");
+ inputNode = MochiKit.DOM.currentDocument().createElement("<INPUT TYPE='RADIO' NAME='" + currentName + "' VALUE='" + anOption['value'] + "'" + checkedValue + ">");
+ } else {
+ inputNode = MochiKit.DOM.INPUT(options, anOption['value']);
+ }
+ divNode = MochiKit.DOM.DIV(null, inputNode);
+
+ return divNode;
+ }, options)
+ );
+ break;
+ }
+ } else {
+ var binding;
+// binding = this.directLogin().bindings()[this.name()];
+ binding = someBindings[this.name()];
+
+ result = MochiKit.DOM.INPUT({
+ type:((this.type() != 'password') ? this.type() : 'text'),
+ name:this.name(),
+// value:((binding != null)? binding.field().value() : this.value())
+ value:((binding != null)? someFields[binding.fieldKey()]['value'] : this.value())
+// value:((binding != null)? someFields[binding.fieldKey()].value() : this.value())
+ }, null);
+ }
+
+ return result;
+ },
+*/
+ //-------------------------------------------------------------------------
+
+ 'needsFormValue': function() {
+ var type;
+ var result;
+
+ type = this.type();
+ result = ((type == 'checkbox') || (type == 'radio') || (type == 'select'));
+
+ return result;
+ },
+
+ 'needsBinding': function() {
+ var type;
+ var result;
+
+ type = this.type();
+ result = ((type == 'text') || (type == 'password'));
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+});
+
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/EncryptedRemoteObject.js b/frontend/delta/js/Clipperz/PM/DataModel/EncryptedRemoteObject.js
new file mode 100644
index 0000000..1aa7a52
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/EncryptedRemoteObject.js
@@ -0,0 +1,542 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.KeyValueObjectStore) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.DataModel.EncryptedRemoteObject depends on Clipperz.KeyValueObjectStore!";
+}
+
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; }
+
+Clipperz.PM.DataModel.EncryptedRemoteObject = function(args) {
+ args = args || {};
+
+ this._name = args.name || null;
+ this._reference = args.reference || Clipperz.PM.Crypto.randomKey();
+ this._isBrandNew = ((args.reference == null) && (args.remoteData == null));
+
+ if ((this._isBrandNew == false) && (args['retrieveKeyFunction'] == null)) {
+ Clipperz.Base.exception.raise('MandatoryParameter');
+ } else {
+ this._retrieveKeyFunction = args['retrieveKeyFunction'];
+ }
+
+ this._retrieveRemoteDataFunction = args.retrieveRemoteDataFunction || null;
+ this._remoteData = args.remoteData || null;
+// this._remoteData = args.remoteData ? Clipperz.Base.deepClone(args.remoteData) : null;
+ if ((!this._isBrandNew) && ((this._retrieveRemoteDataFunction == null) && (this._remoteData == null))) {
+ Clipperz.Base.exception.raise('MandatoryParameter');
+ }
+
+
+ this._encryptedDataKeypath = args.encryptedDataKeypath || 'data'; //Clipperz.Base.exception.raise('MandatoryParameter');
+ this._encryptedVersionKeypath = args.encryptedVersionKeypath || 'version'; //Clipperz.Base.exception.raise('MandatoryParameter');
+
+
+ this._transientState = null;
+ this._deferredLocks = {};
+
+ if (this._isBrandNew == true) {
+ this._objectDataStore = new Clipperz.KeyValueObjectStore(/*{'name':'EncryptedRemoteObject.objectDataStore [1]'}*/);
+ } else {
+ this._objectDataStore = null;
+ }
+
+ return this;
+}
+
+//
+// Basic data workflow
+// =======================
+//
+// getRemoteData
+// unpackRemoteData
+// getDecryptData [encryptedDataKeypath, encryptedVersionKeypath]
+// unpackData
+//
+//
+// ?? packData
+// ?? encryptDataWithKey
+// ?? packRemoteData [encryptedDataKeypath (?), encryptedVersionKeypath (?)]
+//
+
+Clipperz.PM.DataModel.EncryptedRemoteObject.prototype = MochiKit.Base.update(null, {
+
+ 'toString': function () {
+ return "Clipperz.PM.DataModel.EncryptedRemoteObject" + (this.name() != null ? " - " + this.name() : "");
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'name': function () {
+ return this._name;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'reference': function () {
+ return this._reference;
+ },
+
+ 'setReference': function (aValue) {
+ this._reference = aValue;
+
+ return this._reference;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'transientState': function () {
+ if (this._transientState == null) {
+ this._transientState = new Clipperz.KeyValueObjectStore(/*{'name':'EncryptedRemoteObject.transientState [2]'}*/);
+ }
+
+ return this._transientState;
+ },
+
+ 'resetTransientState': function (isCommitting) {
+ if (this._transientState != null) {
+ this._transientState.removeAllData();
+ }
+
+ this._transientState = null;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'isBrandNew': function () {
+ return this._isBrandNew;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getKey': function () {
+ var deferredResult;
+ var deferredLock;
+
+ deferredLock = this.getDeferredLockForKey('key');
+
+ deferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObject.getKey", {trace:false});
+ deferredResult.acquireLock(deferredLock);
+ deferredResult.addMethod(
+ this.decryptedDataStore(),
+ 'deferredGetOrSet',
+ 'decryptionKey',
+ MochiKit.Base.partial(this.retrieveKeyFunction(), this.reference())
+ );
+ deferredResult.releaseLock(deferredLock);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+ 'retrieveKeyFunction': function () {
+ return this._retrieveKeyFunction;
+ },
+
+ 'setRetrieveKeyFunction': function (aFunction) {
+ this._retrieveKeyFunction = aFunction;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'hasLoadedRemoteData': function () {
+ return (this._remoteData != null);
+ },
+
+ 'getRemoteData': function () {
+ var deferredResult;
+ var deferredLock;
+
+ deferredLock = this.getDeferredLockForKey('remoteData');
+
+ deferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObjects.getRemoteData", {trace:false});
+ deferredResult.acquireLock(deferredLock);
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ var innerDeferredResult;
+
+ if (this._remoteData != null) {
+ innerDeferredResult = MochiKit.Async.succeed(this._remoteData);
+ } else {
+ innerDeferredResult = Clipperz.Async.callbacks("EncryptedRemoteObjects.getRemoteData <inner deferred>", [
+ MochiKit.Base.partial(this.retrieveRemoteDataFunction(), this.reference()),
+ MochiKit.Base.method(this, 'unpackRemoteData'),
+ MochiKit.Base.bind(function (someData) {
+ this._remoteData = someData;
+ return this._remoteData;
+ }, this)
+ ], {trace:false});
+ }
+
+ return innerDeferredResult;
+ }, this))
+ deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+ deferredResult.releaseLock(deferredLock);
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'unpackRemoteData': function (someData) {
+ return MochiKit.Async.succeed(someData);
+ },
+
+ //.........................................................................
+
+ 'packRemoteData': function (someData) {
+ var result;
+
+ result = {
+ 'reference': this.reference(),
+ 'data': someData,
+ 'version': Clipperz.PM.Crypto.encryptingFunctions.currentVersion
+ };
+
+ return MochiKit.Async.succeed(result);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'retrieveRemoteDataFunction': function () {
+ return this._retrieveRemoteDataFunction;
+ },
+
+ 'setRetrieveRemoteDataFunction': function (aFunction) {
+ this._retrieveRemoteDataFunction = aFunction;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'decryptedDataStore': function () {
+ if (this._decryptedDataStore == null) {
+ this._decryptedDataStore = new Clipperz.KeyValueObjectStore(/*{'name':'EncryptedRemoteObject.decryptedDataStore [3]'}*/);
+ };
+
+ return this._decryptedDataStore;
+ },
+
+ //.........................................................................
+
+ 'getDecryptedData': function () {
+ var deferredResult;
+ var deferredLock;
+
+ deferredLock = this.getDeferredLockForKey('decryptedData');
+
+ deferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObject.getDecryptedData", {trace:false});
+ deferredResult.acquireLock(deferredLock);
+ deferredResult.addMethod(this, 'decryptedDataStore');
+ deferredResult.addCallback(MochiKit.Base.methodcaller('deferredGetOrSet', 'decryptedData', MochiKit.Base.bind(function () {
+ var innerDeferredResult;
+
+ innerDeferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObject.getDecryptedData <inner deferred>", {trace:false});
+
+ innerDeferredResult.addMethod(this, 'getRemoteData');
+ innerDeferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+ innerDeferredResult.collectResults({
+ 'key': MochiKit.Base.method(this, 'getKey'),
+ 'value': MochiKit.Base.itemgetter(this._encryptedDataKeypath),
+ 'version': MochiKit.Base.itemgetter(this._encryptedVersionKeypath)
+ });
+
+ innerDeferredResult.addCallback(Clipperz.PM.Crypto.deferredDecrypt);
+ innerDeferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+ innerDeferredResult.addMethod(this, 'unpackData');
+ innerDeferredResult.callback();
+
+ return innerDeferredResult;
+ }, this)));
+ deferredResult.releaseLock(deferredLock);
+ deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'setValue': function(aKey, aValue) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObject.setValue", {trace:false});
+ deferredResult.addMethod(this, '_getObjectDataStore');
+ deferredResult.addCallback(MochiKit.Base.methodcaller('setValue', aKey, aValue));
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //.........................................................................
+
+ 'getValue': function (aKey) {
+ return Clipperz.Async.callbacks("EncryptedRemoteObject.getValue", [
+ MochiKit.Base.method(this, '_getObjectDataStore'),
+ MochiKit.Base.methodcaller('getValue', aKey)
+ ], {trace:false});
+ },
+
+ //.........................................................................
+
+ 'removeValue': function (aKey) {
+ return Clipperz.Async.callbacks("EncryptedRemoteObject.removeValue", [
+ MochiKit.Base.method(this, '_getObjectDataStore'),
+ MochiKit.Base.methodcaller('removeValue', aKey)
+ ], {trace:false});
+ },
+
+ //.........................................................................
+
+ 'values': function () {
+ return Clipperz.Async.callbacks("EncryptedRemoteObject.values", [
+ MochiKit.Base.method(this, '_getObjectDataStore'),
+ MochiKit.Base.methodcaller('values')
+ ], {trace:false});
+ },
+
+ 'setValues': function (someValues) {
+ return Clipperz.Async.callbacks("EncryptedRemoteObject.values", [
+ MochiKit.Base.method(this, '_getObjectDataStore'),
+ MochiKit.Base.methodcaller('setValues', someValues)
+ ], {trace:false});
+ },
+
+ //.........................................................................
+
+ '_getObjectDataStore': function () {
+ var deferredResult;
+ var deferredLock;
+
+ deferredLock = this.getDeferredLockForKey('objectDataStore');
+
+ deferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObject._getObjectDataStore", {trace:false});
+ deferredResult.acquireLock(deferredLock);
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ var innerDeferredResult;
+
+ if (this._objectDataStore == null) {
+ this._objectDataStore = new Clipperz.KeyValueObjectStore(/*{'name':'EncryptedRemoteObject.objectDataStore [4]'}*/);
+
+ innerDeferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObject._getObjectDataStore <inner deferred>", {trace:false});
+ innerDeferredResult.addMethod(this, 'getDecryptedData');
+ innerDeferredResult.addMethod(this._objectDataStore, 'initWithValues');
+ innerDeferredResult.callback();
+ } else {
+ innerDeferredResult = MochiKit.Async.succeed(this._objectDataStore);
+ }
+
+ return innerDeferredResult;
+ }, this));
+ deferredResult.releaseLock(deferredLock);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ 'hasInitiatedObjectDataStore': function () {
+ return (this._objectDataStore != null);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getDeferredLockForKey': function (aKey) {
+ var result;
+
+ result = this._deferredLocks[aKey];
+
+ if (typeof(result) == 'undefined') {
+ result = new MochiKit.Async.DeferredLock();
+ this._deferredLocks[aKey] = result;
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'unpackData': function (someData) { // ++
+ return someData;
+ },
+
+ 'packData': function (someData) { // ++
+ return someData;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'hasPendingChanges': function () {
+ var deferredResult;
+ var tempObj = this;
+
+ if (this.isBrandNew()) {
+// deferredResult = MochiKit.Async.succeed(true);
+ deferredResult = this.hasPendingChangesWhenBrandNew();
+ } else if (this.hasInitiatedObjectDataStore()) {
+ deferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObject.hasPendingChanges", {trace:false});
+ deferredResult.collectResults({
+ 'decryptedData': [
+ MochiKit.Base.method(this, 'getDecryptedData'),
+ Clipperz.Base.serializeJSON
+ ],
+ 'objectData': [
+ MochiKit.Base.method(this, '_getObjectDataStore'),
+ MochiKit.Base.methodcaller('values'),
+ Clipperz.Base.serializeJSON
+ ]
+ });
+ deferredResult.addCallback(function (someValues) {
+ return (someValues['decryptedData'] != someValues['objectData']);
+ });
+ deferredResult.callback();
+ } else {
+ deferredResult = MochiKit.Async.succeed(false);
+ }
+
+ return deferredResult;
+ },
+
+ 'hasPendingChangesWhenBrandNew': function () {
+ return MochiKit.Async.succeed(true);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'commitTransientState': function () {
+ var deferredResult;
+
+// if (this.transientState().getValue('__prepareRemoteData') == true) {
+ if (this.transientState().getValue('packedRemoteData') != null) {
+ deferredResult = Clipperz.Async.callbacks("EncryptedRemoteObject.commitTransientState - prepareRemoteData", [
+ MochiKit.Base.bind(function (someData) {
+ this._remoteData = this.transientState().getValue('packedRemoteData');
+ }, this),
+
+ MochiKit.Base.method(this, '_getObjectDataStore'),
+ MochiKit.Base.methodcaller('values'),
+ Clipperz.Base.deepClone,
+ MochiKit.Base.method(this.decryptedDataStore(), 'setValue', 'decryptedData'),
+
+ MochiKit.Base.method(this, 'resetTransientState', true)
+ ], {trace:false});
+
+ } else {
+ deferredResult = Clipperz.Async.callbacks("EncryptedRemoteObject.commitTransientState - NO prepareRemoteData", [
+ MochiKit.Base.method(this, 'resetTransientState', true)
+ ], {trace:false});
+ }
+
+ this._isBrandNew = false;
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'revertChanges': function () {
+ if (this.hasInitiatedObjectDataStore()) {
+ this._objectDataStore.removeAllData();
+ this._objectDataStore = null;
+ }
+ this.resetTransientState(false);
+
+ return MochiKit.Async.succeed();
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'deleteAllCleanTextData': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObject.deleteAllCleanTextData", {trace:false});
+
+ deferredResult.addMethod(this, 'resetTransientState', false);
+
+ deferredResult.acquireLock(this.getDeferredLockForKey('decryptedData'));
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ if (this._decryptedDataStore != null) {
+ this._decryptedDataStore.removeAllData();
+ }
+ }, this));
+ deferredResult.releaseLock(this.getDeferredLockForKey('decryptedData'));
+
+ deferredResult.acquireLock(this.getDeferredLockForKey('objectDataStore'));
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ if (this._objectDataStore != null) {
+ this._objectDataStore.removeAllData();
+ this._objectDataStore = null;
+ }
+ }, this));
+ deferredResult.releaseLock(this.getDeferredLockForKey('objectDataStore'));
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //.........................................................................
+
+ 'hasAnyCleanTextData': function () {
+ var result;
+
+ result = false;
+
+ result = result || (! this.decryptedDataStore().isEmpty());
+ result = result || (! this.transientState().isEmpty());
+ if (this.hasInitiatedObjectDataStore()) {
+ result = result || (! this._objectDataStore.isEmpty());
+ }
+
+ return MochiKit.Async.succeed(result);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'prepareRemoteDataWithKey': function (aKey) {
+ return Clipperz.Async.callbacks("EncryptedRemoteObject.prepareRemoteDataWithKey", [
+// MochiKit.Base.method(this.transientState(), 'setValue', '__prepareRemoteData', true),
+ MochiKit.Base.method(this, '_getObjectDataStore'),
+ MochiKit.Base.methodcaller('values'),
+ MochiKit.Base.method(this, 'packData'),
+ function (someData) {
+ return Clipperz.PM.Crypto.deferredEncrypt({
+ 'key': aKey,
+ 'value': someData,
+ 'version': Clipperz.PM.Crypto.encryptingFunctions.currentVersion
+ })
+ },
+ MochiKit.Base.method(this, 'packRemoteData'),
+ MochiKit.Base.method(this.transientState(), 'setValue', 'packedRemoteData'),
+ function (someData) {
+ MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'advanceProgress');
+ return someData;
+ }
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+});
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/OneTimePassword.js b/frontend/delta/js/Clipperz/PM/DataModel/OneTimePassword.js
new file mode 100644
index 0000000..fbca1ff
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/OneTimePassword.js
@@ -0,0 +1,350 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; }
+
+
+//#############################################################################
+
+Clipperz.PM.DataModel.OneTimePassword = function(args) {
+ args = args || {};
+
+// this._user = args['user'];
+ this._reference = args['reference'] || Clipperz.PM.Crypto.randomKey();
+ this._password = args['password'];
+ this._passwordValue = Clipperz.PM.DataModel.OneTimePassword.normalizedOneTimePassword(args['password']);
+ this._creationDate = args['created'] ? Clipperz.PM.Date.parseDateWithUTCFormat(args['created']) : new Date();
+ this._usageDate = args['used'] ? Clipperz.PM.Date.parseDateWithUTCFormat(args['used']) : null;
+
+ this._status = args['status'] || 'ACTIVE'; // 'REQUESTED', 'USED', 'DISABLED'
+ this._connectionInfo= null;
+
+ this._key = null;
+ this._keyChecksum = null;
+
+ return this;
+}
+
+Clipperz.PM.DataModel.OneTimePassword.prototype = MochiKit.Base.update(null, {
+
+ 'toString': function() {
+ return "Clipperz.PM.DataModel.OneTimePassword";
+ },
+/*
+ //-------------------------------------------------------------------------
+
+ 'user': function() {
+ return this._user;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'password': function() {
+ return this._password;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'passwordValue': function() {
+ return this._passwordValue;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'creationDate': function() {
+ return this._creationDate;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'reference': function() {
+ return this._reference;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'key': function() {
+ if (this._key == null) {
+ this._key = Clipperz.PM.DataModel.OneTimePassword.computeKeyWithUsernameAndPassword(this.user().username(), this.passwordValue());
+ }
+
+ return this._key;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'keyChecksum': function() {
+ if (this._keyChecksum == null) {
+ this._keyChecksum = Clipperz.PM.DataModel.OneTimePassword.computeKeyChecksumWithUsernameAndPassword(this.user().username(), this.passwordValue());
+ }
+
+ return this._keyChecksum;
+ },
+*/
+ //-------------------------------------------------------------------------
+
+ 'status': function() {
+ return this._status;
+ },
+
+ 'setStatus': function(aValue) {
+ this._status = aValue;
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'serializedData': function() {
+ var result;
+
+ result = {
+ 'password': this.password(),
+ 'created': this.creationDate() ? Clipperz.PM.Date.formatDateWithUTCFormat(this.creationDate()) : null,
+ 'used': this.usageDate() ? Clipperz.PM.Date.formatDateWithUTCFormat(this.usageDate()) : null,
+ 'status': this.status()
+ };
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'packedPassphrase': function() {
+ var result;
+ var packedPassphrase;
+ var encodedPassphrase;
+ var prefixPadding;
+ var suffixPadding;
+ var getRandomBytes;
+
+ getRandomBytes = MochiKit.Base.method(Clipperz.Crypto.PRNG.defaultRandomGenerator(), 'getRandomBytes');
+
+ encodedPassphrase = new Clipperz.ByteArray(this.user().passphrase()).toBase64String();
+//Clipperz.logDebug("--- encodedPassphrase.length: " + encodedPassphrase.length);
+ prefixPadding = getRandomBytes(getRandomBytes(1).byteAtIndex(0)).toBase64String();
+//Clipperz.logDebug("--- prefixPadding.length: " + prefixPadding.length);
+ suffixPadding = getRandomBytes((500 - prefixPadding.length - encodedPassphrase.length) * 6 / 8).toBase64String();
+//Clipperz.logDebug("--- suffixPadding.length: " + suffixPadding.length);
+//Clipperz.logDebug("--- total.length: " + (prefixPadding.length + encodedPassphrase.length + suffixPadding.length));
+
+ packedPassphrase = {
+ 'prefix': prefixPadding,
+ 'passphrase': encodedPassphrase,
+ 'suffix': suffixPadding
+ };
+
+// result = Clipperz.Base.serializeJSON(packedPassphrase);
+ result = packedPassphrase;
+//Clipperz.logDebug("===== OTP packedPassprase: [" + result.length + "]" + result);
+//Clipperz.logDebug("<<< OneTimePassword.packedPassphrase");
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'encryptedPackedPassphrase': function() {
+ return Clipperz.PM.Crypto.deferredEncryptWithCurrentVersion(this.passwordValue(), this.packedPassphrase())
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'encryptedData': function() {
+ var deferredResult;
+ var result;
+
+//Clipperz.logDebug(">>> OneTimePassword.encryptedData");
+//Clipperz.logDebug("--- OneTimePassword.encryptedData - id: " + this.reference());
+ result = {
+ 'reference': this.reference(),
+ 'key': this.key(),
+ 'keyChecksum': this.keyChecksum(),
+ 'data': "",
+ 'version': Clipperz.PM.Crypto.encryptingFunctions.currentVersion
+ }
+//Clipperz.logDebug("--- OneTimePassword.encryptedData - 2: " + Clipperz.Base.serializeJSON(result));
+ deferredResult = new MochiKit.Async.Deferred();
+//Clipperz.logDebug("--- OneTimePassword.encryptedData - 3");
+//deferredResult.addBoth(function(res) {Clipperz.logDebug("OneTimePassword.encryptedData - 1: " + res); return res;});
+//# deferredResult.addCallback(Clipperz.PM.Crypto.deferredEncryptWithCurrentVersion, this.passwordValue(), this.packedPassphrase());
+ deferredResult.addCallback(MochiKit.Base.method(this, 'encryptedPackedPassphrase'));
+//Clipperz.logDebug("--- OneTimePassword.encryptedData - 4");
+//deferredResult.addBoth(function(res) {Clipperz.logDebug("OneTimePassword.encryptedData - 2: [" + res.length + "]" + res); return res;});
+ deferredResult.addCallback(function(aResult, res) {
+ aResult['data'] = res;
+ return aResult;
+ }, result);
+//Clipperz.logDebug("--- OneTimePassword.encryptedData - 5");
+//deferredResult.addBoth(function(res) {Clipperz.logDebug("OneTimePassword.encryptedData - 3: " + Clipperz.Base.serializeJSON(res)); return res;});
+ deferredResult.callback();
+//Clipperz.logDebug("--- OneTimePassword.encryptedData - 6");
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'saveChanges': function() {
+ var deferredResult;
+ var result;
+
+//Clipperz.logDebug(">>> OneTimePassword.saveChanges");
+ result = {};
+ deferredResult = new MochiKit.Async.Deferred();
+
+ deferredResult.addCallback(Clipperz.NotificationCenter.deferredNotification, this, 'updatedProgressState', 'saveOTP_encryptUserData');
+ deferredResult.addCallback(MochiKit.Base.method(this.user(), 'encryptedData'));
+ deferredResult.addCallback(function(aResult, res) {
+ aResult['user'] = res;
+ return aResult;
+ }, result);
+
+ deferredResult.addCallback(Clipperz.NotificationCenter.deferredNotification, this, 'updatedProgressState', 'saveOTP_encryptOTPData');
+ deferredResult.addCallback(MochiKit.Base.method(this, 'encryptedData'));
+ deferredResult.addCallback(function(aResult, res) {
+ aResult['oneTimePassword'] = res;
+ return aResult;
+ }, result);
+
+ deferredResult.addCallback(Clipperz.NotificationCenter.deferredNotification, this, 'updatedProgressState', 'saveOTP_sendingData');
+//deferredResult.addBoth(function(res) {Clipperz.logDebug("OneTimePassword.saveChanges - 1: " + Clipperz.Base.serializeJSON(res)); return res;});
+ deferredResult.addCallback(MochiKit.Base.method(this.user().connection(), 'message'), 'addNewOneTimePassword');
+
+ deferredResult.addCallback(Clipperz.NotificationCenter.deferredNotification, this, 'updatedProgressState', 'saveOTP_updatingInterface');
+//deferredResult.addBoth(function(res) {Clipperz.logDebug("OneTimePassword.saveChanges - 2: " + res); return res;});
+ deferredResult.addCallback(Clipperz.NotificationCenter.deferredNotification, this, 'notify', 'OTPUpdated');
+ deferredResult.addCallback(Clipperz.NotificationCenter.deferredNotification, this, 'oneTimePassword_saveChanges_done', null);
+//deferredResult.addBoth(function(res) {Clipperz.logDebug("OneTimePassword.saveChanges - 2: " + res); return res;});
+ deferredResult.callback();
+//Clipperz.logDebug("<<< OneTimePassword.saveChanges");
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'usageDate': function() {
+ return this._usageDate;
+ },
+
+ 'setUsageDate': function(aValue) {
+ this._usageDate = aValue;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'connectionInfo': function() {
+ return this._connectionInfo;
+ },
+
+ 'setConnectionInfo': function(aValue) {
+ this._connectionInfo = aValue;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'isExpired': function() {
+ return (this.usageDate() != null);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'updateStatusWithValues': function(someValues) {
+ var result;
+
+ result = false;
+
+ if (someValues['status'] != this.status()) {
+ result = true;
+ }
+
+ this.setStatus(someValues['status']);
+ this.setUsageDate(Clipperz.PM.Date.parseDateWithUTCFormat(someValues['requestDate']));
+ this.setConnectionInfo(someValues['connection']);
+
+ return result;
+ },
+*/
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+});
+
+//#############################################################################
+
+Clipperz.PM.DataModel.OneTimePassword.computeKeyWithUsernameAndPassword = function(anUsername, aPassword) {
+ return Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aPassword)).toHexString().substring(2);
+}
+
+Clipperz.PM.DataModel.OneTimePassword.computeKeyChecksumWithUsernameAndPassword = function(anUsername, aPassword) {
+ return Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(anUsername + aPassword)).toHexString().substring(2);
+}
+
+//=============================================================================
+
+Clipperz.PM.DataModel.OneTimePassword.isValidOneTimePasswordValue = function(aPassword) {
+ var result;
+
+// "yaxx k7ww - f8y6 tqz5 - 58b6 th44 - 9cwv q0fg"
+ if (aPassword.replace(/[\s\-]/g, '').length == 32) {
+ try {
+ var passwordByteArray;
+
+ passwordByteArray = new Clipperz.ByteArray();
+ passwordByteArray.appendBase32String(aPassword);
+
+ result = true;
+ } catch(exception) {
+ result = false;
+ }
+ } else {
+ result = false;
+ }
+
+ return result;
+}
+
+//=============================================================================
+
+Clipperz.PM.DataModel.OneTimePassword.normalizedOneTimePassword = function(aPassword) {
+ var result;
+
+ if (aPassword.replace(/[\s\-]/g, '').length == 32) {
+ try {
+ var passwordByteArray;
+
+ passwordByteArray = new Clipperz.ByteArray();
+ passwordByteArray.appendBase32String(aPassword);
+
+ result = passwordByteArray.toBase64String();
+ } catch(exception) {
+ result = aPassword;
+ }
+ } else {
+ result = aPassword;
+ }
+
+ return result;
+}
+
+//#############################################################################
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/Record.Version.Field.js b/frontend/delta/js/Clipperz/PM/DataModel/Record.Version.Field.js
new file mode 100644
index 0000000..01e7196
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/Record.Version.Field.js
@@ -0,0 +1,186 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.PM.DataModel.Record.Version) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.DataModel.Record.Version.Field depends on Clipperz.PM.DataModel.Record.Version!";
+}
+
+Clipperz.PM.DataModel.Record.Version.Field = function(args) {
+ Clipperz.PM.DataModel.Record.Version.Field.superclass.constructor.apply(this, arguments);
+
+ this._recordVersion = args.recordVersion || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._reference = args.reference || Clipperz.PM.Crypto.randomKey();
+
+ return this;
+}
+
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.Record.Version.Field, Object, {
+
+ 'toString': function() {
+ return "Record.Version.Field (" + this.reference() + ")";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'recordVersion': function () {
+ return this._recordVersion;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'reference': function () {
+ return this._reference;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getItem': function (aKey) {
+ return Clipperz.Async.callbacks("Clipperz.PM.DataModel.Record.Version.Field.getItem", [
+ MochiKit.Base.method(this, 'recordVersion'),
+ MochiKit.Base.methodcaller('getValue', 'fields' + '.' + this.reference() + '.' + aKey)
+ ], {trace:false});
+ },
+
+ 'setItem': function (aKey, aValue) {
+ return Clipperz.Async.callbacks("Clipperz.PM.DataModel.Record.Version.Field.getItem", [
+ MochiKit.Base.method(this, 'recordVersion'),
+ MochiKit.Base.methodcaller('setValue', 'fields' + '.' + this.reference() + '.' + aKey, aValue)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'label': function () {
+ return this.getItem('label');
+ },
+
+ 'setLabel': function (aValue) {
+ return this.setItem('label', aValue);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'value': function () {
+ return this.getItem('value');
+ },
+
+ 'setValue': function (aValue) {
+ return this.setItem('value', aValue);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'actionType': function () {
+ return Clipperz.Async.callbacks("Clipperz.PM.DataModel.Record.Version.Field.actionType", [
+ Clipperz.Async.collectResults("Clipperz.PM.DataModel.Record.Version.Field.actionType [collect results]", {
+ 'isHidden': MochiKit.Base.method(this, 'isHidden'),
+ 'value': MochiKit.Base.method(this, 'value')
+ }, {trace:false}),
+ function (someValues) {
+ var result; // 'NONE', 'URL', 'EMAIL', 'PASSWORD'
+
+ result = 'NONE';
+
+ if (someValues['isHidden']) {
+ result = 'PASSWORD';
+ } else if (Clipperz.Base.isUrl(someValues['value'])) {
+ result = 'URL'
+ } else if (Clipperz.Base.isEmail(someValues['value'])) {
+ result = 'EMAIL'
+ };
+
+ return result;
+ }
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'isHidden': function () {
+ return this.getItem('hidden');
+ },
+
+ 'setIsHidden': function (aValue) {
+ return this.setItem('hidden', aValue);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'isEmpty': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.Version.Field.isEmpty", {trace:false});
+
+ deferredResult.collectResults({
+ 'label': [
+ MochiKit.Base.method(this, 'label'),
+ MochiKit.Base.partial(MochiKit.Base.operator.eq, '')
+ ],
+ 'value': [
+ MochiKit.Base.method(this, 'value'),
+ MochiKit.Base.partial(MochiKit.Base.operator.eq, '')
+ ],
+ 'isHidden': [
+ MochiKit.Base.method(this, 'isHidden'),
+ MochiKit.Base.partial(MochiKit.Base.operator.eq, false)
+ ]
+ });
+ deferredResult.addCallback(MochiKit.Base.values);
+ deferredResult.addCallback(function(someValues) {
+ return MochiKit.Iter.every(someValues, MochiKit.Base.operator.identity);
+ });
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'content': function () {
+ var deferredResult;
+ var fieldValues;
+
+ fieldValues = {};
+ deferredResult = new Clipperz.Async.Deferred("Record.Version.Field.content", {trace:false});
+ deferredResult.addMethod(this, 'reference');
+ deferredResult.addCallback(function (aValue) { fieldValues['reference'] = aValue; });
+ deferredResult.addMethod(this, 'label');
+ deferredResult.addCallback(function (aValue) { fieldValues['label'] = aValue; });
+ deferredResult.addMethod(this, 'value');
+ deferredResult.addCallback(function (aValue) { fieldValues['value'] = aValue; });
+ deferredResult.addMethod(this, 'actionType');
+ deferredResult.addCallback(function (aValue) { fieldValues['actionType'] = aValue; });
+ deferredResult.addMethod(this, 'isHidden');
+ deferredResult.addCallback(function (aValue) { fieldValues['isHidden'] = aValue; });
+ deferredResult.addCallback(function () { return fieldValues; });
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+});
+
+
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/Record.Version.js b/frontend/delta/js/Clipperz/PM/DataModel/Record.Version.js
new file mode 100644
index 0000000..87b319c
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/Record.Version.js
@@ -0,0 +1,328 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.PM.DataModel.Record) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.DataModel.Record.Version depends on Clipperz.PM.DataModel.Record!";
+}
+
+Clipperz.PM.DataModel.Record.Version = function(args) {
+ Clipperz.PM.DataModel.Record.Version.superclass.constructor.apply(this, arguments);
+
+ this._getVersionFunction = args.getVersion || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._fields = null;
+
+ return this;
+}
+
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.Record.Version, Clipperz.PM.DataModel.EncryptedRemoteObject, {
+
+ 'toString': function() {
+ return "Record.Version (" + this.reference() + ")";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'reference': function () {
+ return this._reference;
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'hasPendingChanges': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.Version.hasPendingChanges", {trace:false});
+ deferredResult.addCallback(MochiKit.Base.bind(Clipperz.PM.DataModel.Record.Version.superclass.hasPendingChanges, this));
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+*/
+ //-------------------------------------------------------------------------
+
+
+ 'hasPendingChangesWhenBrandNew': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.Version.hasPendingChangesWhenBrandNew", {trace:false});
+ deferredResult.addMethod(this, 'fields');
+ deferredResult.addCallback(MochiKit.Base.values);
+ deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.methodcaller('isEmpty'))
+ deferredResult.addCallback(Clipperz.Async.collectAll);
+ deferredResult.addCallback(function(someValues) {
+ return MochiKit.Iter.every(someValues, MochiKit.Base.operator.identity);
+ });
+ deferredResult.addCallback(MochiKit.Base.operator.lognot)
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'commitTransientState': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.Version.commitTransientState", {trace:false});
+ deferredResult.addCallback(MochiKit.Base.bind(Clipperz.PM.DataModel.Record.Version.superclass.commitTransientState, this));
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'unpackData': function (someData) { // ++
+ var result;
+
+ result = someData;
+ if ((someData['fields'] != null) && (someData['fields'] instanceof Array)) {
+ var fields;
+ var i,c;
+
+ fields = someData['fields'];
+ delete someData['fields'];
+
+ someData['fields'] = {};
+ c = fields.length;
+ for (i=0; i<c; i++) {
+ someData['fields'][i] = fields[i];
+ }
+ }
+
+
+
+ return result;
+ },
+
+ //=========================================================================
+
+ 'fields': function () {
+ var deferredResult;
+ var deferredLock;
+
+ deferredLock = this.getDeferredLockForKey('fields');
+
+ deferredResult = new Clipperz.Async.Deferred("Record.Version.fields", {trace:false});
+ deferredResult.acquireLock(deferredLock);
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ var innerDeferredResult;
+
+ if (this._fields == null) {
+ innerDeferredResult = new Clipperz.Async.Deferred("Record.Version.fields <inner deferred>", {trace:false});
+ innerDeferredResult.addMethod(this, 'getValue', 'fields');
+ innerDeferredResult.addCallback(MochiKit.Base.bind(function (someObjectData) {
+ var reference;
+
+ this._fields = {};
+
+ for (reference in someObjectData) {
+ var recordVersionField;
+
+ recordVersionField = new Clipperz.PM.DataModel.Record.Version.Field({
+ 'recordVersion': this,
+ 'reference': reference
+ });
+
+ this._fields[reference] = recordVersionField;
+ }
+
+ return this._fields;
+ }, this));
+ innerDeferredResult.callback();
+ } else {
+ innerDeferredResult = MochiKit.Async.succeed(this._fields);
+ }
+
+ return innerDeferredResult;
+ }, this));
+ deferredResult.releaseLock(deferredLock);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getFieldsValues': function () {
+ return this.getValue('fields');
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'addField': function (someParameters) {
+ var newField;
+
+ newField = new Clipperz.PM.DataModel.Record.Version.Field({recordVersion:this});
+
+ return Clipperz.Async.callbacks("Record.Version.addField", [
+ MochiKit.Base.method(this, 'fields'),
+
+ MochiKit.Base.method(this, '_getObjectDataStore'),
+ MochiKit.Base.methodcaller('values'),
+ Clipperz.Base.serializeJSON,
+
+ 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(this, '_getObjectDataStore'),
+ MochiKit.Base.methodcaller('values'),
+ Clipperz.Base.serializeJSON,
+
+ MochiKit.Base.partial(MochiKit.Async.succeed, newField)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'removeField': function (aField) {
+ return Clipperz.Async.callbacks("Record.Version.removeField", [
+ MochiKit.Base.method(this, 'fields'),
+ MochiKit.Base.bind(function () { delete this._fields[aField.reference()]; }, this),
+ MochiKit.Base.method(this, 'removeValue', 'fields' + '.' + aField.reference())
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'sortFieldReference': function (someSortedFieldReferences) {
+
+
+
+ },
+*/
+ //=========================================================================
+/*
+ 'directLogins': function () {
+ return MochiKit.Base.values(this._directLogins);
+ },
+
+ 'addDirectLogin': function (aDirectLogin) {
+ this._directLogins[aDirectLogin.reference()] = aDirectLogin;
+ },
+*/
+
+ //=========================================================================
+/*
+ 'updateValues': function (anotherVersion) {
+ return Clipperz.Async.callbacks("Record.Version.updateValue", [
+ MochiKit.Base.partial(MochiKit.Async.succeed, this)
+ ], {trace:false});
+ },
+*/
+ //=========================================================================
+
+ 'setRemoteData': function (aValue) {
+ this._remoteData = aValue;
+
+ return aValue;
+ },
+
+ //=========================================================================
+
+ 'getVersionFunction': function () {
+ return this._getVersionFunction;
+ },
+
+ 'previousVersion': function () {
+ return Clipperz.Async.callbacks("Record.Versions.previousVersion", [
+ MochiKit.Base.method(this, 'previousVersionReference'),
+ this.getVersionFunction()
+ ], {trace:false});
+ },
+
+ 'previousVersionReference': function () {
+ return this.getValue('previousVersionReference');
+ },
+
+ 'previousVersionKey': function () {
+// TODO: this value i encrypted on its own. So it can not be saved in the main objectStore!!!
+ return this.getValue('previousVersionKey');
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'setPreviousVersionReferenceAndKey': function (aVersionObjectAndKey) {
+// this._previousVersion = anotherVersion;
+ return Clipperz.Async.callbacks("Record.Version.setPreviousVersion", [
+ MochiKit.Base.method(this, 'setValue', 'previousVersionReference', aVersionObjectAndKey['reference']),
+ MochiKit.Base.method(this, 'setValue', 'previousVersionKey', aVersionObjectAndKey['key'])
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'revertChanges': function () {
+ this.setReference(this.transientState()['originalReference']);
+ Clipperz.PM.DataModel.Record.Version.superclass.revertChanges.apply(this, arguments);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'prepareRemoteDataWithKey': function (aKey) {
+ var deferredResult;
+ var result;
+
+ result = {};
+
+ deferredResult = new Clipperz.Async.Deferred("Record.Version.prepareRemoteDataWithKey", {trace:false});
+ if (this.isBrandNew() == false) {
+ this.transientState()['originalReference'] = this.reference();
+
+ deferredResult.collectResults({
+ 'key': MochiKit.Base.partial(MochiKit.Async.succeed, aKey),
+ 'value': MochiKit.Base.method(this, 'getKey'),
+ 'version': MochiKit.Base.partial(MochiKit.Async.succeed, Clipperz.PM.Crypto.encryptingFunctions.currentVersion)
+ });
+ deferredResult.addCallback(Clipperz.PM.Crypto.deferredEncrypt);
+ deferredResult.addCallback(Clipperz.Async.setItem, result, 'previousVersionKey');
+ } else {
+ deferredResult.addCallback(Clipperz.Async.setItem, result, 'previousVersionKey', Clipperz.PM.Crypto.nullValue);
+ }
+ deferredResult.addCallback(MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.prepareRemoteDataWithKey, this, aKey));
+ deferredResult.addCallback(MochiKit.Base.update, result);
+ deferredResult.addMethod(this, 'setRemoteData');
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+/*
+ 'deleteAllCleanTextData': function () {
+ return Clipperz.PM.DataModel.Record.Version.superclass.deleteAllCleanTextData.apply(this, arguments);
+ },
+
+ 'hasAnyCleanTextData': function () {
+ return Clipperz.PM.DataModel.Record.Version.superclass.hasAnyCleanTextData.apply(this, arguments);
+ },
+*/
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
+
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/Record.js b/frontend/delta/js/Clipperz/PM/DataModel/Record.js
new file mode 100644
index 0000000..379872a
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/Record.js
@@ -0,0 +1,891 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; }
+
+
+Clipperz.PM.DataModel.Record = function(args) {
+ Clipperz.PM.DataModel.Record.superclass.constructor.apply(this, arguments);
+
+ this._updateDate = (args.updateDate ? Clipperz.PM.Date.parse(args.updateDate) : Clipperz.Base.exception.raise('MandatoryParameter'));
+
+ this._retrieveIndexDataFunction = args.retrieveIndexDataFunction || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._updateIndexDataFunction = args.updateIndexDataFunction || Clipperz.Base.exception.raise('MandatoryParameter');
+
+ this._retrieveDirectLoginIndexDataFunction = args.retrieveDirectLoginIndexDataFunction || null;
+ this._setDirectLoginIndexDataFunction = args.setDirectLoginIndexDataFunction || null;
+ this._removeDirectLoginIndexDataFunction = args.removeDirectLoginIndexDataFunction || null;
+
+ this._createNewDirectLoginFunction = args.createNewDirectLoginFunction || null;
+
+ this._directLogins = {};
+
+ this._versions = {};
+
+ this._currentRecordVersion = null;
+ if (this.isBrandNew()) {
+ var newVersion;
+
+ this.setNotes('');
+ newVersion = new Clipperz.PM.DataModel.Record.Version({
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getVersionKey'),
+ 'getVersion': MochiKit.Base.method(this, 'getVersion')
+
+ });
+ this._versions[newVersion.reference()] = newVersion;
+ this._currentVersionReference = newVersion.reference();
+// this.setLabel('');
+ }
+
+ return this;
+}
+
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.EncryptedRemoteObject, {
+
+ 'toString': function() {
+ return "Record (" + this.reference() + ")";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'reference': function () {
+ return this._reference;
+ },
+
+ //=========================================================================
+
+ 'getIndexData': function () {
+ return this._retrieveIndexDataFunction(this.reference());
+ },
+
+ //.........................................................................
+
+ 'getIndexDataForKey': function (aKey) {
+ return Clipperz.Async.callbacks("Record.getIndexDataForKey", [
+ MochiKit.Base.method(this, 'getIndexData'),
+ MochiKit.Base.itemgetter(aKey)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'setIndexDataForKey': function (aKey, aValue) {
+// return this._updateIndexDataFunction(this.reference(), aKey, aValue);
+
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Record.setIndexDataForKey", {trace:false});
+ deferredResult.addMethod(this, 'getIndexDataForKey', aKey);
+ deferredResult.addCallback(MochiKit.Base.bind(function (aCurrentValue) {
+ var result;
+ var originalValue;
+
+ originalValue = this.transientState().getValue('originalValues.indexData.' + aKey);
+ if (originalValue == null) {
+ originalValue = this.transientState().setValue('originalValues.indexData.' + aKey, aCurrentValue);
+ }
+
+ if (aCurrentValue != aValue) {
+ if (originalValue != aValue) {
+ this.transientState().setValue('hasPendingChanges.indexData.' + aKey, true);
+ } else {
+ this.transientState().setValue('hasPendingChanges.indexData.' + aKey, false);
+ }
+
+ result = this._updateIndexDataFunction(this.reference(), aKey, aValue);
+ } else {
+ result = MochiKit.Async.succeed(aValue);
+ }
+
+ return result;
+ }, this));
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+/*
+ 'key': function () {
+ return this.getIndexDataForKey('key');
+ },
+*/
+ //=========================================================================
+
+ 'label': function () {
+ return this.getIndexDataForKey('label');
+ },
+
+ //.........................................................................
+
+ 'setLabel': function (aValue) {
+ return this.setIndexDataForKey('label', aValue);
+ },
+
+ //=========================================================================
+
+ 'headerNotes': function () {
+ return this.getIndexDataForKey('notes');
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'notes': function () {
+ return Clipperz.Async.callbacks("Record.notes", [
+ MochiKit.Base.method(this, 'headerNotes'),
+ MochiKit.Base.bind(function (someHeaderNotes) {
+ var result;
+
+ if ((someHeaderNotes == null) || (typeof(someHeaderNotes) == 'undefined')) {
+ result = this.getValue('notes');
+ } else {
+ result = MochiKit.Async.succeed(someHeaderNotes);
+ }
+
+ return result;
+ }, this)
+ ], {trace:false});
+ },
+
+ //.........................................................................
+
+ 'setNotes': function (aValue) {
+ return this.setValue('notes', aValue);
+ },
+
+ //=========================================================================
+
+ 'updateDate': function () {
+ return MochiKit.Async.succeed(this._updateDate);
+ },
+
+ //=========================================================================
+
+ 'favicon': function () {
+ var result;
+ var directLogins;
+
+ directLogins = MochiKit.Base.values(this.directLogins());
+ if (directLogins.length > 0) {
+ result = directLogins[0].favicon();
+// } else if (/* is there an URL to use for searching a favicon */){
+ } else {
+ result = null; // MochiKit.Async.succeed(Clipperz.PM.Strings['defaultFaviconUrl']);
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'searchableContent': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Record.searchableContent", {trace:false});
+
+ deferredResult.collectResults({
+ 'recordLabel': MochiKit.Base.method(this, 'label'),
+ 'directLoginLabels': [
+ MochiKit.Base.method(this, 'directLoginReferences'),
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.itemgetter('label'))
+ ]
+ })
+ deferredResult.addCallback(function (someValues) {
+ return someValues['recordLabel'] + ' ' + someValues['directLoginLabels'].join(' ');
+ });
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'isMatching': function (aRegExp) {
+ return Clipperz.Async.callbacks("deferredFilterFunction", [
+ MochiKit.Base.method(this, 'searchableContent'),
+ MochiKit.Base.method(aRegExp, 'test'),
+ function (doesItMatch) {
+ var result;
+
+ if (doesItMatch) {
+ result = MochiKit.Async.succeed('match');
+ } else {
+ result = MochiKit.Async.fail('miss');
+ }
+
+ return result;
+ }
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'content': function () {
+ var deferredResult;
+ var result;
+
+ result = {
+ 'fields': [],
+ 'directLogins': []
+ };
+
+ deferredResult = new Clipperz.Async.Deferred("Record.content", {trace:false});
+ deferredResult.addMethod(this, 'reference');
+ deferredResult.addCallback(function (aValue) { result['reference'] = aValue; });
+ deferredResult.addMethod(this, 'label');
+ deferredResult.addCallback(function (aValue) { result['title'] = aValue; });
+ deferredResult.addMethod(this, 'notes');
+ deferredResult.addCallback(function (aValue) { result['notes'] = aValue; });
+
+ deferredResult.addMethod(this, 'fields');
+ deferredResult.addCallback(MochiKit.Base.values);
+ deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.methodcaller('content'));
+ deferredResult.addCallback(Clipperz.Async.collectAll);
+ deferredResult.addCallback(MochiKit.Base.map, function (aValue) { result['fields'].push(aValue); });
+
+ deferredResult.addMethod(this, 'directLogins');
+ deferredResult.addCallback(MochiKit.Base.values);
+ deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.methodcaller('content'));
+ deferredResult.addCallback(Clipperz.Async.collectAll);
+ deferredResult.addCallback(MochiKit.Base.map, function (aValue) { result['directLogins'].push(aValue); });
+ deferredResult.addCallback(function () { return result; });
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'directLogins': function () {
+ return this._directLogins;
+ },
+
+ 'addDirectLogin': function (aDirectLogin) {
+ this._directLogins[aDirectLogin.reference()] = aDirectLogin;
+ },
+
+ 'directLoginWithReference': function (aDirectLoginReference) {
+ return this._directLogins[aDirectLoginReference];
+ },
+
+ 'createNewDirectLoginFunction': function () {
+ return this._createNewDirectLoginFunction;
+ },
+
+ 'saveOriginalDirectLoginStatusToTransientState': function () {
+ if (this.transientState().getValue('directLogins') == null) {
+// this.transientState().setValue('directLogins', this._directLogins)
+ MochiKit.Iter.forEach(MochiKit.Base.keys(this._directLogins), MochiKit.Base.bind(function(aKey) {
+ this.transientState().setValue('directLogins' + '.' + aKey, this._directLogins[aKey])
+ }, this))
+ }
+ },
+
+ 'createNewDirectLogin': function () {
+ this.saveOriginalDirectLoginStatusToTransientState();
+
+ return this.createNewDirectLoginFunction()(this);
+ },
+
+ 'removeDirectLogin': function(aDirectLogin) {
+ this.saveOriginalDirectLoginStatusToTransientState();
+
+ return Clipperz.Async.callbacks("Record.removeDirectLogin", [
+ MochiKit.Base.method(this, 'removeValue', 'directLogins' + '.' + aDirectLogin.reference()),
+ MochiKit.Base.bind(function () {
+ delete this._directLogins[aDirectLogin.reference()]
+ }, this)
+ ], {trace:false});
+
+ },
+
+ 'directLoginReferences': function () {
+ var result;
+
+ result = Clipperz.Async.callbacks("Record.directLoginReferences", [
+ MochiKit.Base.method(this, 'directLogins'),
+ MochiKit.Base.values,
+ function (someDirectLogins) {
+ var result;
+ var i,c;
+
+ result = [];
+ c = someDirectLogins.length;
+ for (i=0; i<c; i++) {
+ result.push(Clipperz.Async.collectResults("Record.directLoginReferences - collectResults", {
+ '_rowObject': MochiKit.Async.succeed,
+ '_reference': MochiKit.Base.methodcaller('reference'),
+ 'label': MochiKit.Base.methodcaller('label'),
+ 'favicon': MochiKit.Base.methodcaller('favicon')
+ }, {trace:false})(someDirectLogins[i]));
+ };
+
+ return result;
+ },
+ Clipperz.Async.collectAll
+ ], {trace:false});
+
+ return result;
+ },
+
+ //=========================================================================
+
+ 'unpackRemoteData': function (someData) {
+ var result;
+
+/*
+ this._currentRecordVersion = new Clipperz.PM.DataModel.Record.Version({
+ 'reference': someData['currentVersion']['reference'],
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getCurrentRecordVersionKey'),
+ 'remoteData': someData['currentVersion'],
+ });
+*/
+ var versionKey;
+
+ for (versionKey in someData['versions']) {
+ this._versions[versionKey] = new Clipperz.PM.DataModel.Record.Version({
+ 'reference': versionKey,
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getVersionKey'),
+ 'remoteData': someData['versions'][versionKey],
+ 'getVersion': MochiKit.Base.method(this, 'getVersion')
+ })
+ }
+
+// this._currentVersionReference = someData['currentVersion']['reference'];
+ this._currentVersionReference = someData['currentVersion'];
+
+ result = Clipperz.PM.DataModel.Record.superclass.unpackRemoteData.apply(this, arguments);
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'unpackData': function (someData) {
+ var result;
+
+ result = Clipperz.PM.DataModel.Record.superclass.unpackData.apply(this, arguments);
+
+ if (MochiKit.Base.isUndefinedOrNull(result['notes'])) {
+ result['notes'] = ''
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'prepareRemoteDataWithKey': function (aKey) {
+ var deferredResult;
+ var newVersionKey;
+ var result;
+
+ newVersionKey = Clipperz.PM.Crypto.randomKey();
+ result = {};
+
+ deferredResult = new Clipperz.Async.Deferred("Record.prepareRemoteDataWithKey", {trace:false});
+ deferredResult.addCallbackList([
+ Clipperz.Async.collectResults("Record.prepareRemoteDataWithKey - collect results", {
+ 'isBrandNew': MochiKit.Base.method(this, 'isBrandNew'),
+ 'versionHasPendingChanges': [
+// MochiKit.Base.method(this, 'getCurrentRecordVersion'),
+// MochiKit.Base.methodcaller('hasPendingChanges')
+ MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'hasPendingChanges')
+ ]
+ }),
+ Clipperz.Async.or,
+
+ Clipperz.Async.deferredIf("Current Version has pending changes", [
+ MochiKit.Base.method(this, 'createNewRecordVersion'),
+ MochiKit.Base.methodcaller('prepareRemoteDataWithKey', newVersionKey),
+ MochiKit.Base.partial(Clipperz.Async.setItem, result, 'currentRecordVersion'),
+ MochiKit.Base.method(this, 'setCurrentRecordVersionKey', newVersionKey)
+ ], []),
+
+ MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.prepareRemoteDataWithKey, this, aKey),
+ MochiKit.Base.partial(Clipperz.Async.setItem, result, 'record'),
+
+ MochiKit.Base.partial(MochiKit.Async.succeed, result)
+ ]);
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'fields': function () {
+ return this.invokeCurrentRecordVersionMethod('fields');
+ },
+
+ 'addField': function (someParameters) {
+ return this.invokeCurrentRecordVersionMethod('addField', someParameters);
+ },
+
+ 'removeField': function (someParameters) {
+ return this.invokeCurrentRecordVersionMethod('removeField', someParameters);
+ },
+
+// 'sortFieldReference': function (someSortedFieldReferences) {
+// return this.invokeCurrentRecordVersionMethod('sortFieldReference', someSortedFieldReferences);
+// },
+
+ 'getFieldsValues': function () {
+ return this.invokeCurrentRecordVersionMethod('getFieldsValues');
+ },
+
+ 'fieldWithLabel': function (aLabel) {
+ return Clipperz.Async.callbacks("Record.fieldWithLabel", [
+ MochiKit.Base.method(this, 'fields'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(Clipperz.Async.deferredFilter, function (aField) {
+ return Clipperz.Async.callbacks("Record.fieldWithLabel - check field label", [
+ MochiKit.Base.methodcaller('label'),
+ MochiKit.Base.partial(MochiKit.Base.operator.eq, aLabel)
+ ], {trace:false}, aField);
+ }),
+ function (someFilteredResults) {
+ var result;
+
+ switch (someFilteredResults.length) {
+ case 0:
+ result = null;
+ break;
+ case 1:
+ result = someFilteredResults[0];
+ break;
+ default:
+ WTF = TODO;
+ break;
+ }
+
+ return result;
+ }
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'getVersion': function (aVersionReference) {
+ return Clipperz.Async.callbacks("Record.getVersion", [
+ MochiKit.Base.method(this, 'getVersions'),
+ MochiKit.Base.itemgetter(aVersionReference)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getVersionKey': function (aVersionReference) {
+ var deferredResult;
+ var transientStateKey;
+
+ transientStateKey = 'versionKeys' + '.' + aVersionReference;
+ if (this.transientState().getValue(transientStateKey) != null) {
+ deferredResult = MochiKit.Async.succeed(this.transientState().getValue(transientStateKey));
+ } else {
+ deferredResult = Clipperz.Async.callbacks("Record.getVersionKey", [
+ MochiKit.Base.method(this, 'getVersions'),
+ MochiKit.Base.partial(MochiKit.Base.operator.eq, aVersionReference, this.currentVersionReference()),
+ Clipperz.Async.deferredIf("getVersionKey for current version", [
+ MochiKit.Base.method(this, 'getCurrentRecordVersionKey'),
+ MochiKit.Base.method(this.transientState(), 'setValue', transientStateKey)
+ ],[
+ MochiKit.Async.fail
+ ])
+ ], {trace:false});
+ }
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'versions': function () {
+ return this._versions;
+ },
+
+ 'getVersions': function () {
+ return Clipperz.Async.callbacks("Record.versions", [
+ MochiKit.Base.method(this, 'getValue', 'fakeKey, just to trigger unpackRemoteData'),
+ MochiKit.Base.bind(function () { return this._versions; }, this)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getCurrentRecordVersion': function () {
+ return Clipperz.Async.callbacks("Record.getCurrentRecordVersion", [
+// MochiKit.Base.method(this, 'getValue', 'fakeKey, just to trigger unpackRemoteData'),
+// MochiKit.Base.bind(function () { return this._currentRecordVersion; }, this)
+
+ MochiKit.Base.method(this, 'versions'),
+ MochiKit.Base.itemgetter(this.currentVersionReference()),
+ Clipperz.Async.deferredIf("The current version is available", [
+ MochiKit.Async.succeed
+ ], [
+ MochiKit.Base.method(this, 'getVersions'),
+ MochiKit.Base.bind(function (someVersions) { return someVersions[this.currentVersionReference()]}, this)
+ ])
+ ], {trace:false});
+ },
+
+ 'setCurrentRecordVersion': function (aRecordVersion) {
+ this._currentVersionReference = aRecordVersion.reference();
+ },
+
+ //.........................................................................
+
+ 'currentVersionReference': function () {
+ return this._currentVersionReference;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'createNewRecordVersion': function () {
+ var deferredResult;
+
+ if (this.isBrandNew()) {
+ deferredResult = this.getCurrentRecordVersion();
+ } else {
+ var newVersion;
+
+ newVersion = new Clipperz.PM.DataModel.Record.Version({
+ // 'reference': versionKey,
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getVersionKey'),
+// 'remoteData': {},
+ 'getVersion': MochiKit.Base.method(this, 'getVersion')
+ })
+ this._versions[newVersion.reference()] = newVersion;
+
+ deferredResult = Clipperz.Async.callbacks("Record.createNewRecordVersion", [
+// MochiKit.Base.method(this, 'getCurrentRecordVersion'),
+// MochiKit.Base.methodcaller('values'),
+ MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'values'),
+ MochiKit.Base.method(newVersion, 'setValues'),
+
+ Clipperz.Async.collectResults("Record.createNewRecordVersion [collect results]", {
+ 'reference': MochiKit.Base.method(this, 'currentVersionReference'),
+ 'key': MochiKit.Base.method(this, 'getCurrentRecordVersionKey')
+ }, {trace:false}),
+ MochiKit.Base.method(newVersion, 'setPreviousVersionReferenceAndKey'),
+
+// MochiKit.Base.method(this, 'getCurrentRecordVersion'),
+// MochiKit.Base.method(this, 'revertChanges'),
+ MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'revertChanges'),
+
+ MochiKit.Base.method(this, 'setCurrentRecordVersion', newVersion),
+ MochiKit.Base.partial(MochiKit.Async.succeed, newVersion)
+ ], {trace:false});
+ }
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getCurrentRecordVersionKey': function () {
+ return Clipperz.Async.callbacks("Record.getCurrentRecordVersionKey", [
+ MochiKit.Base.method(this, 'getValue', 'currentVersionKey'),
+ Clipperz.Async.deferredIf("currentVersionKey is NOT null", [
+ MochiKit.Async.succeed
+ ], [
+ MochiKit.Base.method(this, 'getKey')
+ ])
+ ], {trace:false});
+ },
+
+ 'setCurrentRecordVersionKey': function (aValue) {
+ // TODO: triple check this method!
+ return Clipperz.Async.callbacks("Record.setCurrentRecordVersionKey", [
+ MochiKit.Base.method(this, 'setValue', 'currentVersionKey', aValue)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'invokeCurrentRecordVersionMethod': function (aMethodName, someValues) {
+ return Clipperz.Async.callbacks("Record.invokeCurrentRecordVersionMethod", [
+ MochiKit.Base.method(this, 'getCurrentRecordVersion'),
+ MochiKit.Base.methodcaller(aMethodName, someValues)
+ ], {trace:false});
+ },
+
+
+ 'lazilyinvokeCurrentRecordVersionMethod': function (aMethodName, someValues, defaultResult) {
+ return Clipperz.Async.callbacks("Record.lazilyinvokeCurrentRecordVersionMethod", [
+ MochiKit.Base.method(this, 'currentVersionReference'),
+ Clipperz.Async.deferredIf("versions has been loaded", [
+ MochiKit.Base.method(this, 'getCurrentRecordVersion'),
+ MochiKit.Base.methodcaller(aMethodName, someValues),
+ ], [
+ MochiKit.Base.partial(MochiKit.Async.succeed, defaultResult),
+ ])
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'hasPendingChanges': function () {
+ var deferredResult;
+
+ if (this.hasInitiatedObjectDataStore()) {
+ deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.hasPendingChanges", {trace:false});
+ deferredResult.collectResults({
+ 'super': MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.hasPendingChanges, this),
+ 'currentVersion': [
+// MochiKit.Base.method(this, 'getCurrentRecordVersion'),
+// MochiKit.Base.methodcaller('hasPendingChanges')
+ MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'hasPendingChanges')
+ ],
+ 'directLogins': [
+ MochiKit.Base.method(this, 'directLogins'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasPendingChanges')),
+ Clipperz.Async.collectAll,
+ Clipperz.Async.or
+// function(someValues) {
+// return MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
+// }
+ ]
+ });
+ deferredResult.addCallback(MochiKit.Base.values);
+ deferredResult.addCallback(MochiKit.Base.bind(function(someValues) {
+ var result;
+ result = MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
+
+ if ((result == false) && (this.isBrandNew() == false)) {
+ result = MochiKit.Iter.some(MochiKit.Base.values(this.transientState().getValue('hasPendingChanges.indexData')), MochiKit.Base.operator.identity);
+ }
+
+ return result;
+ }, this));
+
+ deferredResult.callback();
+ } else {
+ deferredResult = Clipperz.Async.callbacks("Recrod.hasPendingChanges [hasInitiatedObjectDataStore == false]", [
+ MochiKit.Base.method(this, 'directLogins'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasPendingChanges')),
+ Clipperz.Async.collectAll,
+ Clipperz.Async.or
+// function(someValues) {
+// return MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
+// }
+ ], {trace:false})
+ }
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'hasPendingChangesWhenBrandNew': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.hasPendingChangesWhenBrandNew", {trace:false});
+ deferredResult.collectResults({
+ 'label': [
+ MochiKit.Base.method(this, 'label'),
+ MochiKit.Base.partial(MochiKit.Base.operator.ne, '')
+ ],
+ 'notes': [
+ MochiKit.Base.method(this, 'notes'),
+ MochiKit.Base.partial(MochiKit.Base.operator.ne, '')
+ ]
+ });
+// deferredResult.addCallback(MochiKit.Base.values);
+// deferredResult.addCallback(function(someValues) {
+// return MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
+// });
+ deferredResult.addCallback(Clipperz.Async.or);
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'isBrandNewWithNoPendingChanges': function () {
+ var deferredResult;
+
+ if (this.isBrandNew() == false) {
+ deferredResult = MochiKit.Async.succeed(false);
+ } else {
+ deferredResult = Clipperz.Async.callbacks("Record.isBrandNewWithNoPendingChanges", [
+ MochiKit.Base.method(this, 'hasPendingChanges'),
+ MochiKit.Base.operator.lognot
+ ], {trace:false});
+ }
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'revertChanges': function () {
+ var deferredResult;
+
+ if (this.isBrandNew() == false) {
+ deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.revertChanges", {trace:false});
+ deferredResult.addMethod(this, 'hasPendingChanges');
+ deferredResult.addIf([
+// MochiKit.Base.method(this, 'getCurrentRecordVersion'),
+// MochiKit.Base.methodcaller('revertChanges'),
+ MochiKit.Base.method(this,'invokeCurrentRecordVersionMethod', 'revertChanges'),
+
+ MochiKit.Base.method(this, 'directLogins'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('revertChanges')),
+
+ MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.revertChanges, this)
+ ], [
+ MochiKit.Async.succeed
+ ]);
+ deferredResult.callback();
+ } else {
+// this.deleteAllCleanTextData();
+ deferredResult = MochiKit.Async.succeed();
+ }
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'resetTransientState': function (isCommitting) {
+// if ((isCommitting == false) && (this.transientState().getValue('directLogins') != null)) {
+// this._directLogins = this.transientState().getValue('directLogins');
+// }
+
+ return Clipperz.Async.callbacks("Record.resetTransientState", [
+//- MochiKit.Base.method(this, 'getCurrentRecordVersion'),
+//- MochiKit.Base.methodcaller('resetTransientState'),
+// MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'resetTransientState'),
+ MochiKit.Base.method(this, 'lazilyinvokeCurrentRecordVersionMethod', 'resetTransientState'),
+
+ MochiKit.Base.method(this, 'directLogins'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('resetTransientState')),
+
+ MochiKit.Base.bind(function () {
+ if ((isCommitting == false) && (this.transientState().getValue('directLogins') != null)) {
+ this._directLogins = this.transientState().getValue('directLogins');
+ }
+ }, this),
+
+ MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.resetTransientState, this, isCommitting)
+ ], {trace:false})
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'commitTransientState': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.commitTransientState", {trace:false});
+ deferredResult.addMethod(this, 'hasPendingChanges');
+ deferredResult.addIf([
+ MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.commitTransientState, this),
+// MochiKit.Base.method(this, 'getCurrentRecordVersion'),
+// MochiKit.Base.methodcaller('commitTransientState'),
+ MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'commitTransientState'),
+ MochiKit.Base.method(this, 'directLogins'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('commitTransientState'))
+ ], [
+ MochiKit.Async.succeed
+ ]);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'retrieveDirectLoginIndexDataFunction': function () {
+ return this._retrieveDirectLoginIndexDataFunction;
+ },
+
+ 'setDirectLoginIndexDataFunction': function () {
+ return this._setDirectLoginIndexDataFunction;
+ },
+
+ 'removeDirectLoginIndexDataFunction': function () {
+ return this._removeDirectLoginIndexDataFunction;
+ },
+
+ //=========================================================================
+
+ 'deleteAllCleanTextData': function () {
+// return Clipperz.PM.DataModel.Record.superclass.deleteAllCleanTextData.apply(this, arguments);
+
+ return Clipperz.Async.callbacks("Record.deleteAllCleanTextData", [
+ MochiKit.Base.method(this, 'versions'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('deleteAllCleanTextData')),
+
+ MochiKit.Base.method(this, 'directLogins'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('deleteAllCleanTextData')),
+
+ MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.deleteAllCleanTextData, this)
+ ], {trace:false});
+ },
+
+ 'hasAnyCleanTextData': function () {
+// return Clipperz.PM.DataModel.Record.superclass.hasAnyCleanTextData.apply(this, arguments);
+
+ return Clipperz.Async.callbacks("Record.hasAnyCleanTextData", [
+ Clipperz.Async.collectResults("Record.hasAnyCleanTextData [collect results]", {
+ 'versions': [
+ MochiKit.Base.method(this, 'versions'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasAnyCleanTextData')),
+ Clipperz.Async.collectAll
+ ],
+ 'directLogins': [
+ MochiKit.Base.method(this, 'directLogins'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasAnyCleanTextData')),
+ Clipperz.Async.collectAll
+ ],
+ 'super': [
+ MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.hasAnyCleanTextData, this)
+ ]
+ }, {trace:false}),
+ Clipperz.Async.or
+ ])
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
+
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/User.Header.Legacy.js b/frontend/delta/js/Clipperz/PM/DataModel/User.Header.Legacy.js
new file mode 100644
index 0000000..cda5a41
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/User.Header.Legacy.js
@@ -0,0 +1,182 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.PM.DataModel.User) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.DataModel.User.Header.Legacy depends on Clipperz.PM.DataModel.User!";
+}
+
+if (typeof(Clipperz.PM.DataModel.User.Header) == 'undefined') { Clipperz.PM.DataModel.User.Header = {}; }
+
+Clipperz.PM.DataModel.User.Header.Legacy = function(args) {
+// args = args || {};
+ Clipperz.PM.DataModel.User.Header.Legacy.superclass.constructor.apply(this, arguments);
+
+ this._retrieveRecordDetailFunction = args.retrieveRecordDetailFunction || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._records = null;
+// this._directLogins = null;
+
+ return this;
+}
+
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.Legacy, Clipperz.PM.DataModel.EncryptedRemoteObject, {
+
+ 'toString': function() {
+ return "Clipperz.PM.DataModel.User.Header.Legacy";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'retrieveRecordDetailFunction': function () {
+ return this._retrieveRecordDetailFunction;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getRecordKey': function (aRecordReference) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.Legacy.getRecordKey", {trace:false});
+ deferredResult.addMethod(this, 'getRecordIndexData');
+ deferredResult.addCallback(MochiKit.Base.itemgetter('key'))
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'getRecordIndexData': function (aRecordReference) {
+ return this.getValue('records.' + aRecordReference);
+ },
+
+ 'updateRecordIndexData': function (aRecordReference, aKey, aValue) {
+ return this.setValue('records.' + aRecordReference + "." + aKey, aValue);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getDirectLoginIndexData': function (aDirectLoginReference) {
+ return this.getValue('directLogins.' + aDirectLoginReference);
+ },
+
+ 'setDirectLoginIndexData': function (aDirectLoginReference, aKey, aValue) {
+ return this.setValue('directLogins.' + aDirectLoginReference + '.' + aKey, aValue);
+ },
+
+ 'removeDirectLoginIndexData': function (aDirectLoginReference) {
+ return this.removeValue('directLogins.' + aDirectLoginReference);
+ },
+
+ //=========================================================================
+
+ 'records': function () {
+ var deferredResult;
+ var deferredLock;
+
+ deferredLock = this.getDeferredLockForKey('records');
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.Legacy.records", {trace:false});
+ deferredResult.acquireLock(deferredLock);
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ var innerDeferredResult;
+
+ if (this._records == null) {
+ innerDeferredResult = new Clipperz.Async.Deferred("User.Header.Legacy.records <inner deferred>", {trace:false});
+ innerDeferredResult.collectResults({
+ 'header': [
+// MochiKit.Base.method(this, 'getObjectDataStore'),
+// MochiKit.Base.methodcaller('values')
+ MochiKit.Base.method(this, 'values')
+ ],
+ 'recordsStats': [
+ MochiKit.Base.method(this, 'getRemoteData'),
+ MochiKit.Base.itemgetter('recordsStats')
+ ]
+ });
+ innerDeferredResult.addCallback(MochiKit.Base.bind(function (someObjectData) {
+ var reference;
+
+ this._records = {};
+// this._directLogins = {};
+
+ for (reference in someObjectData['header']['records']) {
+ var record;
+
+ record = new Clipperz.PM.DataModel.Record({
+ 'reference': reference,
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getRecordKey'),
+ 'retrieveRemoteDataFunction': this.retrieveRecordDetailFunction(),
+// 'encryptedDataKeypath': 'data',
+// 'encryptedVersionKeypath': 'version',
+
+ 'retrieveIndexDataFunction': MochiKit.Base.method(this, 'getRecordIndexData'),
+ 'updateIndexDataFunction': MochiKit.Base.method(this, 'updateRecordIndexData'),
+ 'updateDate': someObjectData['recordsStats'][reference]['updateDate'],
+
+ 'retrieveDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'getDirectLoginIndexData'),
+ 'setDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'setDirectLoginIndexData'),
+ 'removeDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'removeDirectLoginIndexData')
+ });
+
+ this._records[reference] = record;
+ }
+
+ for (reference in someObjectData['header']['directLogins']) {
+ var directLogin;
+ var record;
+
+ record = this._records[someObjectData['header']['directLogins'][reference]['record']];
+ if (record != null) {
+ directLogin = new Clipperz.PM.DataModel.DirectLogin({
+ 'reference': reference,
+ 'record': record //,
+// 'retrieveIndexDataFunction': MochiKit.Base.method(this, 'getDirectLoginIndexData'),
+// 'setIndexDataFunction': MochiKit.Base.method(this, 'setDirectLoginIndexData'),
+// 'removeIndexDataFunction': MochiKit.Base.method(this, 'removeDirectLoginIndexData')
+ });
+ } else {
+Clipperz.log("WARNING: DIRECT LOGIN without a matching RECORD!!");
+ }
+ }
+
+ return this._records;
+ }, this));
+ innerDeferredResult.callback();
+ } else {
+ innerDeferredResult = MochiKit.Async.succeed(this._records);
+ }
+
+ return innerDeferredResult;
+ }, this));
+ deferredResult.releaseLock(deferredLock);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
+
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/User.Header.OneTimePasswords.js b/frontend/delta/js/Clipperz/PM/DataModel/User.Header.OneTimePasswords.js
new file mode 100644
index 0000000..e82da47
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/User.Header.OneTimePasswords.js
@@ -0,0 +1,117 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.PM.DataModel.User) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.DataModel.User.Header.OneTimePasswords depends on Clipperz.PM.DataModel.User!";
+}
+if (typeof(Clipperz.PM.DataModel.User.Header) == 'undefined') { Clipperz.PM.DataModel.User.Header = {}; }
+
+//-----------------------------------------------------------------------------
+
+Clipperz.PM.DataModel.User.Header.OneTimePasswords = function(args) {
+ Clipperz.PM.DataModel.User.Header.OneTimePasswords.superclass.constructor.apply(this, arguments);
+
+ this._oneTimePasswords = null;
+
+ return this;
+}
+
+//-----------------------------------------------------------------------------
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.OneTimePasswords, Clipperz.PM.DataModel.EncryptedRemoteObject, {
+
+ 'toString': function() {
+ return "Clipperz.PM.DataModel.User.Header.OneTimePasswords";
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'packData': function (someData) { // ++
+ var result;
+
+ result = Clipperz.PM.DataModel.User.Header.OneTimePasswords.superclass.packData.apply(this, arguments);
+
+ return result;
+ },
+*/
+ //-------------------------------------------------------------------------
+/*
+ 'packRemoteData': function (someData) {
+ var result;
+
+ result = Clipperz.PM.DataModel.User.Header.OneTimePasswords.superclass.packRemoteData.apply(this, arguments);
+
+ return result;
+ },
+*/
+ //-------------------------------------------------------------------------
+/*
+ 'prepareRemoteDataWithKey': function (aKey) {
+ var result;
+
+ result = Clipperz.PM.DataModel.User.Header.OneTimePasswords.superclass.prepareRemoteDataWithKey.apply(this, arguments);
+
+ return result;
+ },
+*/
+ //=========================================================================
+
+ 'oneTimePasswords': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.OneTimePasswords.oneTimePasswords", {trace:false});
+ if (this._oneTimePasswords == null) {
+ deferredResult.addMethod(this, 'values')
+ deferredResult.addCallback(MochiKit.Base.bind(function (someData) {
+ var otpKey;
+
+ this._oneTimePasswords = {};
+
+ for (otpKey in someData) {
+ var otp;
+ var otpParameters;
+
+ otpParameters = Clipperz.Base.deepClone(someData[otpKey]);
+ otpParameters['reference'] = otpKey;
+
+ otp = new Clipperz.PM.DataModel.OneTimePassword(otpParameters);
+ this._oneTimePasswords[otpKey] = otp;
+ }
+
+ return this._oneTimePasswords;
+
+ }, this));
+ deferredResult.callback();
+ } else {
+ deferredResult = MochiKit.Async.succeed(this._oneTimePasswords);
+ }
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
+//-----------------------------------------------------------------------------
+
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/User.Header.Preferences.js b/frontend/delta/js/Clipperz/PM/DataModel/User.Header.Preferences.js
new file mode 100644
index 0000000..f1f95e8
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/User.Header.Preferences.js
@@ -0,0 +1,48 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.PM.DataModel.User) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.DataModel.User.Header.Preferences depends on Clipperz.PM.DataModel.User!";
+}
+
+if (typeof(Clipperz.PM.DataModel.User.Header) == 'undefined') { Clipperz.PM.DataModel.User.Header = {}; }
+
+Clipperz.PM.DataModel.User.Header.Preferences = function(args) {
+ Clipperz.PM.DataModel.User.Header.Preferences.superclass.constructor.apply(this, arguments);
+
+ return this;
+}
+
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.Preferences, Clipperz.PM.DataModel.EncryptedRemoteObject, {
+
+ 'toString': function() {
+ return "Clipperz.PM.DataModel.User.Header.Preferences";
+ },
+
+ //-------------------------------------------------------------------------
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
+
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/User.Header.RecordIndex.js b/frontend/delta/js/Clipperz/PM/DataModel/User.Header.RecordIndex.js
new file mode 100644
index 0000000..5681f70
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/User.Header.RecordIndex.js
@@ -0,0 +1,685 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.PM.DataModel.User) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.DataModel.User.Header.RecordIndex depends on Clipperz.PM.DataModel.User!";
+}
+
+if (typeof(Clipperz.PM.DataModel.User.Header) == 'undefined') { Clipperz.PM.DataModel.User.Header = {}; }
+
+Clipperz.PM.DataModel.User.Header.RecordIndex = function(args) {
+ Clipperz.PM.DataModel.User.Header.RecordIndex.superclass.constructor.apply(this, arguments);
+
+ this._recordsData = new Clipperz.PM.DataModel.EncryptedRemoteObject({
+ 'name': 'recordsData',
+ 'retrieveKeyFunction': args.retrieveKeyFunction,
+ 'remoteData': {
+ 'data': args.recordsData['data'],
+ 'version': args.encryptedDataVersion,
+ 'recordsStats': args.recordsStats
+ }//,
+// 'encryptedDataKeypath': 'data',
+// 'encryptedVersionKeypath': 'version'
+ });
+
+ this._directLoginsData = new Clipperz.PM.DataModel.EncryptedRemoteObject({
+ 'name': 'directLoginsData',
+ 'retrieveKeyFunction': args.retrieveKeyFunction,
+ 'remoteData': {
+ 'data': args.directLoginsData['data'],
+ 'version': args.encryptedDataVersion
+ }//,
+// 'encryptedDataKeypath': 'data',
+// 'encryptedVersionKeypath': 'version'
+ });
+
+ this._lock = new MochiKit.Async.DeferredLock();
+ this._transientState = null;
+
+ this._retrieveRecordDetailFunction = args.retrieveRecordDetailFunction || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._recordsIndex = args.recordsData['index'] || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._directLoginsIndex = args.directLoginsData['index'] || Clipperz.Base.exception.raise('MandatoryParameter');
+
+ this._records = null;
+
+ return this;
+}
+
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
+
+ 'toString': function() {
+ return "Clipperz.PM.DataModel.User.Header.RecordIndex";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'retrieveRecordDetailFunction': function () {
+ return this._retrieveRecordDetailFunction;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'recordsIndex': function () {
+ return this._recordsIndex;
+ },
+
+ 'recordsData': function () {
+ return this._recordsData;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'directLoginsIndex': function () {
+ return this._directLoginsIndex;
+ },
+
+ 'directLoginsData': function () {
+ return this._directLoginsData;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'lock': function () {
+ return this._lock;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'transientState': function () {
+ if (this._transientState == null) {
+ this._transientState = new Clipperz.KeyValueObjectStore(/*{'name':'User.Header.RecordIndex.transientState [1]'}*/);
+ }
+
+ return this._transientState;
+ },
+
+ 'resetTransientState': function (isCommitting) {
+ if (this._transientState != null) {
+ this._transientState.removeAllData();
+ }
+
+ this._transientState = null;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getRecordKey': function (aRecordReference) {
+ return Clipperz.Async.callbacks("User.Header.RecordIndex.getRecordKey", [
+ MochiKit.Base.method(this, 'getRecordIndexData', aRecordReference),
+ MochiKit.Base.itemgetter('key')
+ ], {trace:false});
+ },
+
+ 'setRecordKey': function (aRecordReference, aValue) {
+ return this.updateRecordIndexData(aRecordReference, 'key', aValue);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getRecordIndexData': function (aRecordReference) {
+ return this.recordsData().getValue(this.recordsIndex()[aRecordReference]);
+ },
+
+ //.........................................................................
+
+ 'updateRecordIndexData': function (aRecordReference, aKey, aValue) {
+ return this.recordsData().setValue(this.recordsIndex()[aRecordReference]+'.'+aKey, aValue);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getDirectLoginIndexData': function (aDirectLoginReference) {
+ return this.directLoginsData().getValue(this.directLoginsIndex()[aDirectLoginReference]);
+ },
+
+ 'setDirectLoginIndexData': function (aDirectLoginReference, aKey, aValue) {
+//if (MochiKit.Base.isUndefinedOrNull(this.directLoginsIndex()[aDirectLoginReference])) {
+// throw "PIPPO";
+//}
+ return this.directLoginsData().setValue(this.directLoginsIndex()[aDirectLoginReference] + '.' + aKey, aValue);
+ },
+
+ 'addDirectLoginIndexData': function (aDirectLoginReference) {
+ return this.directLoginsData().setValue(this.directLoginsIndex()[aDirectLoginReference], {});
+ },
+
+ 'removeDirectLoginIndexData': function (aDirectLoginReference) {
+ return this.directLoginsData().removeValue(this.directLoginsIndex()[aDirectLoginReference])
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'records': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.RecordIndex.records", {trace:false});
+ deferredResult.acquireLock(this.lock());
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ var innerDeferredResult;
+
+ if (this._records == null) {
+ innerDeferredResult = new Clipperz.Async.Deferred("User.Header.RecordIndex.records <inner deferred>", {trace:false});
+ innerDeferredResult.collectResults({
+ 'records': [
+// MochiKit.Base.method(this.recordsData(), 'getObjectDataStore'),
+// MochiKit.Base.methodcaller('values')
+ MochiKit.Base.method(this.recordsData(), 'values')
+ ],
+ 'recordsStats': [
+ MochiKit.Base.method(this.recordsData(), 'getRemoteData'),
+ MochiKit.Base.itemgetter('recordsStats')
+ ],
+ 'directLogins': [
+// MochiKit.Base.method(this.directLoginsData(), 'getObjectDataStore'),
+// MochiKit.Base.methodcaller('values')
+ MochiKit.Base.method(this.directLoginsData(), 'values')
+ ]
+ })
+ innerDeferredResult.addCallback(MochiKit.Base.bind(function (someData) {
+ var indexReference;
+ var recordsInvertedIndex;
+ var directLoginsInvertedIndex;
+
+ recordsInvertedIndex = Clipperz.PM.DataModel.User.Header.RecordIndex.invertIndex(this.recordsIndex());
+ directLoginsInvertedIndex = Clipperz.PM.DataModel.User.Header.RecordIndex.invertIndex(this.directLoginsIndex());
+
+ this._records = {};
+
+ for (indexReference in someData['records']) {
+ var record;
+ var reference;
+ var updateDate;
+
+ reference = recordsInvertedIndex[indexReference];
+
+ if (typeof(someData['recordsStats'][reference]) != 'undefined') {
+ updateDate = someData['recordsStats'][reference]['updateDate'];
+
+ record = new Clipperz.PM.DataModel.Record({
+ 'reference': reference,
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getRecordKey'),
+ 'retrieveRemoteDataFunction': this.retrieveRecordDetailFunction(),
+
+ 'retrieveIndexDataFunction': MochiKit.Base.method(this, 'getRecordIndexData'),
+ 'updateIndexDataFunction': MochiKit.Base.method(this, 'updateRecordIndexData'),
+ 'updateDate': updateDate,
+
+ 'retrieveDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'getDirectLoginIndexData'),
+ 'setDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'setDirectLoginIndexData'),
+ 'removeDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'removeDirectLoginIndexData'),
+
+ 'createNewDirectLoginFunction': MochiKit.Base.method(this, 'createNewDirectLogin')
+ });
+
+ this._records[reference] = record;
+ } else {
+Clipperz.log("SKIPPING record " + reference + " as there are no stas associated - " + Clipperz.Base.serializeJSON(someData['records'][reference]));
+ // # skip the record, as it seems it is not present in the DB
+ // updateDate = Clipperz.PM.Date.formatDateWithUTCFormat(new Date());
+ }
+ }
+
+ for (indexReference in someData['directLogins']) {
+// var directLogin;
+ var reference;
+ var record;
+
+ reference = directLoginsInvertedIndex[indexReference];
+ record = this._records[recordsInvertedIndex[someData['directLogins'][indexReference]['record']]];
+
+ if (record != null) {
+// directLogin = new Clipperz.PM.DataModel.DirectLogin({
+ new Clipperz.PM.DataModel.DirectLogin({
+ 'reference': reference,
+ 'record': record
+ });
+ } else {
+ Clipperz.logWarning("WARNING: DIRECT LOGIN without a matching RECORD!!");
+ }
+ }
+
+ return this._records;
+ }, this));
+ innerDeferredResult.callback();
+ } else {
+ innerDeferredResult = MochiKit.Async.succeed(this._records);
+ }
+
+ return innerDeferredResult;
+ }, this));
+ deferredResult.releaseLock(this.lock());
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'updateRecordIndexForNewRecord': function (aNewRecord) {
+ var newRecordIndex;
+ var recordReference;
+
+ recordReference = aNewRecord.reference();
+ newRecordIndex = (MochiKit.Base.listMax(MochiKit.Base.map(MochiKit.Base.partial(MochiKit.Base.operator.mul, 1), MochiKit.Base.values(this.recordsIndex()))) + 1) + '';
+ this.recordsIndex()[recordReference] = newRecordIndex;
+
+ this.transientState().setValue('newlyCreatedRecordsIndex' + '.' + recordReference, newRecordIndex);
+ this.transientState().setValue('newlyCreatedRecordsReferences' + '.' + recordReference, aNewRecord);
+ },
+
+ //.........................................................................
+
+ 'createNewRecord': function () {
+ var deferredResult;
+ var newRecord;
+
+ newRecord = new Clipperz.PM.DataModel.Record({
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getRecordKey'),
+ 'retrieveRemoteDataFunction': this.retrieveRecordDetailFunction(),
+
+ 'retrieveIndexDataFunction': MochiKit.Base.method(this, 'getRecordIndexData'),
+ 'updateIndexDataFunction': MochiKit.Base.method(this, 'updateRecordIndexData'),
+ 'updateDate': Clipperz.PM.Date.formatDateWithUTCFormat(new Date()),
+
+ 'retrieveDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'getDirectLoginIndexData'),
+ 'setDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'setDirectLoginIndexData'),
+ 'removeDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'removeDirectLoginIndexData'),
+
+ 'createNewDirectLoginFunction': MochiKit.Base.method(this, 'createNewDirectLogin')
+ });
+
+ this.transientState().setValue('newRecordsReferences' + '.' + newRecord.reference(), newRecord);
+ this.updateRecordIndexForNewRecord(newRecord);
+
+ deferredResult = Clipperz.Async.callbacks("User.Header.RecordIndex.createNewRecord", [
+ MochiKit.Base.method(this, 'records'),
+ MochiKit.Base.partial(Clipperz.Async.setItemOnObject, newRecord.reference(), newRecord),
+ MochiKit.Base.method(this, 'setRecordKey', newRecord.reference(), Clipperz.PM.Crypto.randomKey()),
+ MochiKit.Base.method(newRecord, 'setLabel', ''),
+ MochiKit.Base.partial(MochiKit.Async.succeed, newRecord)
+ ], {trace:false});
+
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'deleteRecord': function (aRecord) {
+ var deferredResult;
+ var recordReference;
+
+ recordReference = aRecord.reference();
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.RecordIndex.deleteRecord", {trace:false});
+
+ deferredResult.addMethod(aRecord, 'directLogins');
+ deferredResult.addCallback(MochiKit.Base.values);
+ deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'removeDirectLogin'));
+
+ deferredResult.addMethod(this.recordsData(), 'removeValue', this.recordsIndex()[recordReference]);
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ this.transientState().setValue('deleteRecordsIndex' + '.' + recordReference, this.recordsIndex()[recordReference]);
+ delete this.recordsIndex()[recordReference];
+ }, this));
+
+ deferredResult.addMethod(this, 'records');
+ deferredResult.addCallback(MochiKit.Base.itemgetter(recordReference));
+ deferredResult.addMethod(this.transientState(), 'setValue', 'deleteRecordsReferences' + '.' + recordReference);
+
+ deferredResult.addMethod(this, 'records');
+ deferredResult.addCallback(MochiKit.Base.bind(function (someRecords) {
+ delete someRecords[recordReference];
+ }, this));
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'removeDirectLogin': function (aDirectLogin) {
+ this.directLoginsData().removeValue(this.directLoginsIndex()[aDirectLogin.reference()]);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'createNewDirectLogin': function (aRecord) {
+ var newDirectLogin;
+ var newDirectLoginIndexValue;
+
+ newDirectLogin = new Clipperz.PM.DataModel.DirectLogin({record:aRecord});
+ newDirectLoginIndexValue = MochiKit.Base.listMax(MochiKit.Base.map(function (aValue) { return aValue * 1; }, MochiKit.Base.values(this.directLoginsIndex()))) + 1;
+
+ this.transientState().setValue('newDirectLoginReferences' + '.' + newDirectLogin.reference(), newDirectLogin);
+
+ this.directLoginsIndex()[newDirectLogin.reference()] = newDirectLoginIndexValue;
+ this.directLoginsData().setValue(this.directLoginsIndex()[newDirectLogin.reference()], {'record': this.recordsIndex()[aRecord.reference()]});
+
+ return newDirectLogin;
+ },
+
+ //=========================================================================
+
+ 'deleteAllCleanTextData': function () {
+ return Clipperz.Async.callbacks("User.Header.RecordIndex.deleteAllCleanTextData", [
+// MochiKit.Base.method(this, 'records'),
+// MochiKit.Base.values,
+// MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('deleteAllCleanTextData')),
+
+ MochiKit.Base.method(this, 'recordsData'),
+ MochiKit.Base.methodcaller('deleteAllCleanTextData'),
+ MochiKit.Base.method(this, 'directLoginsData'),
+ MochiKit.Base.methodcaller('deleteAllCleanTextData')
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'hasAnyCleanTextData': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred({trace:false});
+ deferredResult.collectResults({
+ 'recordsData': [
+ MochiKit.Base.method(this, 'recordsData'),
+ MochiKit.Base.methodcaller('hasAnyCleanTextData')
+ ],
+ 'directLoginsData': [
+ MochiKit.Base.method(this, 'directLoginsData'),
+ MochiKit.Base.methodcaller('hasAnyCleanTextData')
+ ],
+// 'records': [
+// MochiKit.Base.method(this, 'records'),
+// MochiKit.Base.values,
+// MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasAnyCleanTextData')),
+// Clipperz.Async.collectAll
+// ]
+ });
+
+// deferredResult.addCallback(MochiKit.Base.values);
+// deferredResult.addCallback(MochiKit.Base.flattenArguments);
+// deferredResult.addCallback(function(someValues) {
+// return MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
+// });
+ deferredResult.addCallback(Clipperz.Async.or);
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'hasPendingChanges': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.RecordIndex.hasPendingChanges", {trace:false});
+ deferredResult.collectResults({
+ 'recordsData': [
+ MochiKit.Base.method(this, 'recordsData'),
+ MochiKit.Base.methodcaller('hasPendingChanges')
+ ],
+ 'directLoginsData': [
+ MochiKit.Base.method(this, 'directLoginsData'),
+ MochiKit.Base.methodcaller('hasPendingChanges')
+ ]
+ });
+ deferredResult.addCallback(Clipperz.Async.or);
+// deferredResult.addCallback(MochiKit.Base.values);
+// deferredResult.addCallback(MochiKit.Base.flattenArguments);
+// deferredResult.addCallback(function(someValues) {
+// return MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
+// });
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'commitTransientState': function () {
+ var deferredResult;
+
+ deferredResut = Clipperz.Async.callbacks("User.Header.RecordIndex.commitTransientState", [
+ MochiKit.Base.method(this, 'recordsData'),
+ MochiKit.Base.methodcaller('commitTransientState'),
+
+ MochiKit.Base.method(this, 'directLoginsData'),
+ MochiKit.Base.methodcaller('commitTransientState'),
+
+ MochiKit.Base.method(this, 'resetTransientState', true)
+ ], {trace:false});
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'revertChanges': function () {
+ return Clipperz.Async.callbacks("User.Header.RecordIndex.revertChanges", [
+ MochiKit.Base.method(this, 'recordsData'),
+ MochiKit.Base.methodcaller('revertChanges'),
+
+// MochiKit.Base.method(this, 'directLoginsData'),
+// MochiKit.Base.methodcaller('revertChanges'),
+
+ MochiKit.Base.method(this, 'records'),
+ MochiKit.Base.bind(function (someRecords) {
+ var recordReference;
+
+ for (recordReference in this.transientState().getValue('deleteRecordsReferences')) {
+ this.recordsIndex()[recordReference] = this.transientState().getValue('deleteRecordsIndex' + '.' + recordReference);
+ someRecords[recordReference] = this.transientState().getValue('deleteRecordsReferences' + '.' + recordReference);
+ }
+
+ for (recordReference in this.transientState().getValue('newRecordsReferences')) {
+ delete this.recordsIndex()[recordReference];
+ delete someRecords[recordReference];
+ }
+ }, this),
+
+// MochiKit.Base.method(this, 'directLogins'),
+ MochiKit.Base.bind(function () {
+ var directLoginReference;
+
+// this.transientState().setValue('newDirectLoginReferences' + '.' + newDirectLogin.reference(), newDirectLogin);
+//
+// this.directLoginsIndex()[newDirectLogin.reference()] = newDirectLoginIndexValue;
+// this.directLoginsData().setValue(this.directLoginsIndex()[newDirectLogin.reference()], {'record': this.recordsIndex()[aRecord.reference()]});
+
+
+// for (directLoginReference in this.transientState().getValue('deleteDirectLoginReferences')) {
+// someDirectLogins[directLoginReference] = this.transientState().getValue('deleteDirectLoginReferences' + '.' + recordReference);
+// }
+
+ for (directLoginReference in this.transientState().getValue('newDirectLoginReferences')) {
+// this.directLoginsData().removeValue(this.directLoginsIndex()[directLoginReference]);
+ delete this.directLoginsIndex()[directLoginReference];
+ }
+ }, this),
+
+ MochiKit.Base.method(this, 'directLoginsData'),
+ MochiKit.Base.methodcaller('revertChanges'),
+
+ MochiKit.Base.method(this, 'resetTransientState', false)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'prepareRemoteDataWithKey': function (aKey) {
+// "records": {
+// "index": {
+// "eeda70e0392261967bda71c3764da78989c45bbd2bb7be6b941b90f81d9b81b5": "0",
+// "13a5e52976337ab210903cd04872588e1b21fb72bc183e91aa25c494b8138551": "1",
+// ...
+// "465a067a0bd2b470fa834de5397e38494de0c7707938262fae3427932e219744": "18",
+// "4fd1dc2ca860b7fb47cef10a84edb3270da05510b0a30a6b0b083898712d4b9e": "19"
+// },
+// "data": "n+AzGEEQXaSRSY4d ... BDypotrXgPo94uHfoXvGFzwCn8w="
+// },
+// "directLogins": {
+// "index": {
+// "61e87fdc4f1d9112e3b30c1f6812d095dcdb24f014c83319091eb6c9899ec348":"0",
+// "989593d4c48929f0c8f1581aa96969c622807e99619ed4732026e967530a68ad":"1",
+// ...
+// "cb9ae0bba1957075ccdbfd3b3481704d62087687a2ac7c411a4f07d444bde0f7":"17",
+// "7e1d069b7fa57c03bd7bf48807520feb953157834503aaff8c9d493f37dea69d":"18"
+// },
+// "data":"5YG9KKU/OZ5guUgFlms6k1 ... ZG/5Fn0uN+LoAsNfHm+EE62x"
+// },
+
+ var deferredResult;
+ var result;
+
+ result = {};
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.RecordIndex.prepareRemoteDataWithKey", {trace:false});
+ deferredResult.collectResults({
+ 'index': MochiKit.Base.partial(MochiKit.Async.succeed, this.recordsIndex()),
+ 'data': [
+ MochiKit.Base.method(this.recordsData(), 'prepareRemoteDataWithKey', aKey),
+ MochiKit.Base.itemgetter('data')
+ ]
+ });
+ deferredResult.addCallback(Clipperz.Async.setItem, result, 'records');
+
+ deferredResult.collectResults({
+ 'index': MochiKit.Base.partial(MochiKit.Async.succeed, this.directLoginsIndex()),
+ 'data': [
+ MochiKit.Base.method(this.directLoginsData(), 'prepareRemoteDataWithKey', aKey),
+ MochiKit.Base.itemgetter('data')
+ ]
+ });
+ deferredResult.addCallback(Clipperz.Async.setItem, result, 'directLogins');
+
+ deferredResult.addCallback(MochiKit.Async.succeed, result);
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'updateRecordKeyAndPrepareRemoteData': function (aRecord) {
+ var newRecordKey;
+ var deferredResult;
+
+ newRecordKey = Clipperz.PM.Crypto.randomKey();
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.RecordIndex.updateRecordKeyAndPrepareRemoteData", {trace:false});
+ deferredResult.addCallback(MochiKit.Base.method(aRecord, 'prepareRemoteDataWithKey', newRecordKey));
+ deferredResult.addCallbackPass(MochiKit.Base.method(this, 'setRecordKey', aRecord.reference(), newRecordKey));
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //.........................................................................
+
+ 'removeNewRecordWithNoChanges': function (aRecord) {
+ var deferredResult;
+ var recordReference;
+
+ recordReference = aRecord.reference();
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.RecordIndex.removeNewRecordWithNoChanges", {trace:false});
+
+ deferredResult.addMethod(this.recordsData(), 'removeValue', this.recordsIndex()[recordReference]);
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ delete this.recordsIndex()[recordReference];
+ }, this));
+
+ deferredResult.addMethod(this, 'records');
+ deferredResult.addCallback(MochiKit.Base.bind(function (someRecords) {
+ delete someRecords[recordReference];
+ }, this));
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //.........................................................................
+
+ 'prepareRemoteDataForChangedRecords': function () {
+ var deferredResult;
+ var result;
+
+ result = {};
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.RecordIndex.prepareRemoteDataForChangedRecords", {trace:false});
+
+ deferredResult.addMethod(this, 'records');
+ deferredResult.addCallback(MochiKit.Base.values);
+ deferredResult.addCallback(Clipperz.Async.deferredFilter, MochiKit.Base.methodcaller('isBrandNewWithNoPendingChanges'));
+ deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'removeNewRecordWithNoChanges'));
+
+ deferredResult.addMethod(this, 'records');
+ deferredResult.addCallback(MochiKit.Base.values);
+ deferredResult.addCallback(Clipperz.Async.deferredFilter, MochiKit.Base.methodcaller('hasPendingChanges'));
+ deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'updateRecordKeyAndPrepareRemoteData'));
+ deferredResult.addCallback(Clipperz.Async.collectAll);
+
+ deferredResult.addCallback(Clipperz.Async.deferredIf("updated records != null", [
+ MochiKit.Base.operator.identity
+ ], [
+ MochiKit.Base.partial(MochiKit.Async.succeed, [])
+ ]));
+ deferredResult.addCallback(Clipperz.Async.setItem, result, 'updated');
+
+ deferredResult.addMethod(this.transientState(), 'getValue', 'deleteRecordsReferences');
+ deferredResult.addCallback(MochiKit.Base.keys);
+ deferredResult.addCallback(Clipperz.Async.deferredIf("deleted records != null", [
+ MochiKit.Base.operator.identity
+ ], [
+ MochiKit.Base.partial(MochiKit.Async.succeed, [])
+ ]));
+ deferredResult.addCallback(Clipperz.Async.setItem, result, 'deleted');
+
+ deferredResult.addCallback(MochiKit.Async.succeed, result);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+});
+
+
+
+Clipperz.PM.DataModel.User.Header.RecordIndex.invertIndex = function (anIndex) {
+ var result;
+ var key;
+
+ result = {};
+
+ for (key in anIndex) {
+ result[anIndex[key]] = key;
+ }
+
+ return result;
+}; \ No newline at end of file
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/User.Subscription.js b/frontend/delta/js/Clipperz/PM/DataModel/User.Subscription.js
new file mode 100644
index 0000000..341e9f3
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/User.Subscription.js
@@ -0,0 +1,53 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.PM.DataModel.User) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.DataModel.User.Subscription depends on Clipperz.PM.DataModel.User!";
+}
+
+Clipperz.PM.DataModel.User.Subscription = function(args) {
+ this._attributes = args;
+ return this;
+}
+
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.User.Subscription, Object, {
+
+ 'features': function () {
+ return this._attributes['features'];
+ },
+
+ 'type': function () {
+ return this._attributes['type'];
+ },
+
+ 'validity': function () {
+ return {
+ 'from': this._attributes['fromDate'],
+ 'to': this._attributes['toDate']
+ };
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/User.js b/frontend/delta/js/Clipperz/PM/DataModel/User.js
new file mode 100644
index 0000000..1d90800
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/User.js
@@ -0,0 +1,827 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; }
+
+
+//#############################################################################
+
+Clipperz.PM.DataModel.User = function (args) {
+ args = args || {};
+
+ Clipperz.PM.DataModel.User.superclass.constructor.apply(this, arguments);
+
+ this._username = args.username || null;
+ this._getPassphraseFunction = args.getPassphraseFunction || null;
+
+ this._data = null;
+
+ this._connection = null;
+ this._connectionVersion = 'current';
+
+ this._subscription = null;
+ this._serverData = null;
+// this._serverLockValue = null;
+ this._transientState = null;
+
+ this._deferredLocks = {
+ 'passphrase': new MochiKit.Async.DeferredLock(),
+ 'serverData': new MochiKit.Async.DeferredLock(),
+// 'recordsIndex': new MochiKit.Async.DeferredLock(),
+// 'directLoginsIndex': new MochiKit.Async.DeferredLock()
+// 'preferences': new MochiKit.Async.DeferredLock()
+// 'oneTimePasswords': new MochiKit.Async.DeferredLock()
+ '__syntaxFix__': 'syntax fix'
+ };
+
+ return this;
+}
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
+
+ 'toString': function () {
+ return "Clipperz.PM.DataModel.User - " + this.username();
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'username': function () {
+ return this._username;
+ },
+
+ 'setUsername': function (aValue) {
+ this._username = aValue;
+ },
+
+ //-------------------------------------------------------------------------
+
+// this.setSubscription(new Clipperz.PM.DataModel.User.Subscription(someServerData['subscription']));
+ 'subscription': function () {
+ return this._subscription;
+ },
+
+ 'setSubscription': function (aValue) {
+ this._subscription = aValue;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'displayName': function() {
+ return "" + this.username() + "";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'data': function () {
+ if (this._data == null) {
+ this._data = new Clipperz.KeyValueObjectStore(/*{'name':'User.data [1]'}*/);
+ };
+
+ return this._data;
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'serverLockValue': function () {
+ return this._serverLockValue;
+ },
+
+ 'setServerLockValue': function (aValue) {
+ this._serverLockValue = aValue;
+ },
+*/
+ //-------------------------------------------------------------------------
+
+ 'transientState': function () {
+ if (this._transientState == null) {
+ this._transientState = {}
+ }
+
+ return this._transientState;
+ },
+
+ 'resetTransientState': function (isCommitting) {
+ this._transientState = null;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'deferredLockForSection': function(aSectionName) {
+ return this._deferredLocks[aSectionName];
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getPassphrase': function() {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.getPassphrase", {trace:false});
+ deferredResult.acquireLock(this.deferredLockForSection('passphrase'));
+ deferredResult.addMethod(this.data(), 'deferredGetOrSet', 'passphrase', this.getPassphraseFunction());
+ deferredResult.releaseLock(this.deferredLockForSection('passphrase'));
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ 'getPassphraseFunction': function () {
+ return this._getPassphraseFunction;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getCredentials': function () {
+ return Clipperz.Async.collectResults("User; get username and passphrase", {
+ 'username': MochiKit.Base.method(this, 'username'),
+ 'password': MochiKit.Base.method(this, 'getPassphrase')
+ }, {trace:false})();
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'changePassphrase': function (aNewValue) {
+ return this.updateCredentials(this.username(), aNewValue);
+ },
+
+ //.........................................................................
+
+ 'updateCredentials': function (aUsername, aPassphrase) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.updateCredentials", {trace:false});
+// deferredResult.addMethod(this, 'getPassphrase');
+// deferredResult.setValue('currentPassphrase');
+ deferredResult.addMethod(this.connection(), 'ping');
+ deferredResult.addMethod(this, 'setUsername', aUsername)
+ deferredResult.acquireLock(this.deferredLockForSection('passphrase'));
+ deferredResult.addMethod(this.data(), 'deferredGetOrSet', 'passphrase', aPassphrase);
+ deferredResult.releaseLock(this.deferredLockForSection('passphrase'));
+// deferredResult.getValue('currentPassphrase');
+ deferredResult.addMethod(this, 'prepareRemoteDataWithKey', aPassphrase);
+ deferredResult.addMethod(this.connection(), 'updateCredentials', aUsername, aPassphrase);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'initialSetupWithNoData': function () {
+ this._serverData = {
+ 'version': '0.1',
+ 'statistics': "",
+ 'header': {
+ 'data': null,
+ 'version': Clipperz.PM.Crypto.encryptingFunctions.currentVersion,
+
+ 'recordsIndex': new Clipperz.PM.DataModel.User.Header.RecordIndex({
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase'),
+ 'recordsData': {'data':null, 'index':{}},
+ 'recordsStats': null,
+ 'directLoginsData': {'data':null, 'index':{}},
+ 'encryptedDataVersion': Clipperz.PM.Crypto.encryptingFunctions.currentVersion,
+ 'retrieveRecordDetailFunction': MochiKit.Base.method(this, 'getRecordDetail')
+ }),
+ 'preferences': new Clipperz.PM.DataModel.User.Header.Preferences({
+ 'name': 'preferences',
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase')
+ }),
+ 'oneTimePasswords': new Clipperz.PM.DataModel.User.Header.OneTimePasswords({
+ 'name': 'preferences',
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase')
+ })
+ }
+ };
+
+// this._serverLockValue = Clipperz.PM.Crypto.randomKey();
+ },
+
+ //.........................................................................
+
+ 'registerAsNewAccount': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.registerAsNewAccount", {trace:false});
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'updateProgress', {'extraSteps':3});
+ deferredResult.addMethod(this, 'initialSetupWithNoData')
+ deferredResult.addMethod(this, 'getPassphrase');
+ deferredResult.addMethod(this, 'prepareRemoteDataWithKey');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+ deferredResult.addMethod(this.connection(), 'register');
+// deferredResult.addCallback(MochiKit.Base.itemgetter('lock'));
+// deferredResult.addMethod(this, 'setServerLockValue');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'userSuccessfullyRegistered');
+
+// deferredResult.addErrback (MochiKit.Base.method(this, 'handleRegistrationFailure'));
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'login': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.login", {trace:false});
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'updateProgress', {'extraSteps':3});
+ deferredResult.addMethod(this, 'getPassphrase');
+ deferredResult.addCallback(Clipperz.PM.DataModel.OneTimePassword.isValidOneTimePasswordValue);
+ deferredResult.addCallback(Clipperz.Async.deferredIf("Is the passphrase an OTP", [
+// MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'updateProgress', {'extraSteps':1}),
+ MochiKit.Base.method(this, 'getCredentials'),
+ MochiKit.Base.method(this.connection(), 'redeemOneTimePassword'),
+ MochiKit.Base.method(this.data(), 'setValue', 'passphrase')
+ ], []));
+ deferredResult.addErrback(MochiKit.Base.method(this, 'getPassphrase'));
+ deferredResult.addMethod(this.connection(), 'login', false);
+ deferredResult.addMethod(this, 'setupConnectionInfo');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'userSuccessfullyLoggedIn');
+ deferredResult.addErrback (MochiKit.Base.method(this, 'handleConnectionFallback'));
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //.........................................................................
+
+ 'handleConnectionFallback': function(aValue) {
+ var result;
+
+//console.log("USER - handleConnectionFallback", aValue, aValue['isPermanent']);
+ if (aValue instanceof MochiKit.Async.CancelledError) {
+ result = aValue;
+ } else if ((aValue['isPermanent'] === true) || (Clipperz.PM.Connection.communicationProtocol.fallbackVersions[this.connectionVersion()] == null)) {
+ result = Clipperz.Async.callbacks("User.handleConnectionFallback - failed", [
+ MochiKit.Base.method(this.data(), 'removeValue', 'passphrase'),
+ MochiKit.Base.method(this, 'setConnectionVersion', 'current'),
+// MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'userLoginFailed'),
+// MochiKit.Base.partial(MochiKit.Async.fail, Clipperz.PM.DataModel.User.exception.LoginFailed)
+ MochiKit.Base.partial(MochiKit.Async.fail, aValue)
+ ], {trace:false});
+ } else {
+ this.setConnectionVersion(Clipperz.PM.Connection.communicationProtocol.fallbackVersions[this.connectionVersion()]);
+ result = new Clipperz.Async.Deferred("User.handleConnectionFallback - retry");
+ result.addMethod(this, 'login');
+ result.callback();
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'setupConnectionInfo': function (aValue) {
+// this.setLoginInfo(aValue['loginInfo']);
+ this.setSubscription(new Clipperz.PM.DataModel.User.Subscription(aValue['subscription']));
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'lock': function () {
+ return Clipperz.Async.callbacks("User.lock", [
+ MochiKit.Base.method(this, 'deleteAllCleanTextData')
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'logout': function () {
+ return Clipperz.Async.callbacks("User.logout", [
+ MochiKit.Base.method(this, 'deleteAllCleanTextData'),
+ MochiKit.Base.method(this.connection(), 'logout')
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'headerFormatVersion': function(anHeader) {
+ var result;
+
+ if (anHeader.charAt(0) == '{') {
+ var headerData;
+
+ headerData = Clipperz.Base.evalJSON(anHeader);
+ result = headerData['version'];
+ } else {
+ result = 'LEGACY';
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'unpackServerData': function (someServerData) {
+ var unpackedData;
+ var headerVersion;
+
+ var recordsIndex;
+ var preferences;
+ var oneTimePasswords;
+
+// this.setServerLockValue(someServerData['lock']);
+
+ headerVersion = this.headerFormatVersion(someServerData['header']);
+ switch (headerVersion) {
+ case 'LEGACY':
+ var legacyHeader;
+
+ legacyHeader = new Clipperz.PM.DataModel.User.Header.Legacy({
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase'),
+ 'remoteData': {
+ 'data': someServerData['header'],
+ 'version': someServerData['version'],
+ 'recordsStats': someServerData['recordsStats']
+ },
+// 'encryptedDataKeypath': 'data',
+// 'encryptedVersionKeypath': 'version',
+ 'retrieveRecordDetailFunction': MochiKit.Base.method(this, 'getRecordDetail')
+ });
+
+ recordsIndex = legacyHeader;
+ preferences = legacyHeader;
+ oneTimePasswords = legacyHeader;
+ break;
+ case '0.1':
+ var headerData;
+
+ headerData = Clipperz.Base.evalJSON(someServerData['header']);
+
+ recordsIndex = new Clipperz.PM.DataModel.User.Header.RecordIndex({
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase'),
+ 'recordsData': headerData['records'],
+ 'recordsStats': someServerData['recordsStats'],
+ 'directLoginsData': headerData['directLogins'],
+ 'encryptedDataVersion': someServerData['version'],
+ 'retrieveRecordDetailFunction': MochiKit.Base.method(this, 'getRecordDetail')
+ });
+
+ // Still missing a test case that actually fais with the old version of the code, where the check for undefined was missing
+ if (typeof(headerData['preferences']) != 'undefined') {
+ preferences = new Clipperz.PM.DataModel.User.Header.Preferences({
+ 'name': 'preferences',
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase'),
+ 'remoteData': {
+ 'data': headerData['preferences']['data'],
+ 'version': someServerData['version']
+ }
+ });
+ } else {
+ preferences = new Clipperz.PM.DataModel.User.Header.Preferences({
+ 'name': 'preferences',
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase')
+ });
+ }
+
+ if (typeof(headerData['oneTimePasswords']) != 'undefined') {
+ oneTimePasswords = new Clipperz.PM.DataModel.User.Header.OneTimePasswords({
+ 'name': 'preferences',
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase'),
+ 'remoteData': {
+ 'data': headerData['oneTimePasswords']['data'],
+ 'version': someServerData['version']
+ }
+ });
+ } else {
+ oneTimePasswords = new Clipperz.PM.DataModel.User.Header.OneTimePasswords({
+ 'name': 'preferences',
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase')
+ });
+ }
+
+ break;
+ }
+
+ unpackedData = {
+ 'version': someServerData['version'],
+ 'statistics': someServerData['statistics'],
+ 'header': {
+ 'data': someServerData['header'],
+ 'version': headerVersion,
+
+ 'recordsIndex': recordsIndex,
+ 'preferences': preferences,
+ 'oneTimePasswords': oneTimePasswords
+ }
+ };
+
+ this._serverData = unpackedData;
+
+ return this._serverData;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getServerData': function() {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.getServerData", {trace:false});
+ deferredResult.acquireLock(this.deferredLockForSection('serverData'));
+ deferredResult.addCallback(MochiKit.Base.bind(function(aResult) {
+ var innerDeferredResult;
+
+ innerDeferredResult = new Clipperz.Async.Deferred("User.getUserDetails.innerDeferred", {trace:false});
+ if (this._serverData == null) {
+ innerDeferredResult.addCallbackPass(MochiKit.Signal.signal, this, 'loadingUserDetails');
+ innerDeferredResult.addMethod(this.connection(), 'message', 'getUserDetails');
+ innerDeferredResult.addMethod(this, 'unpackServerData');
+ innerDeferredResult.addCallbackPass(MochiKit.Signal.signal, this, 'loadedUserDetails');
+ }
+
+ innerDeferredResult.addCallback(MochiKit.Base.bind(function () {
+ return this._serverData;
+ },this));
+ innerDeferredResult.callback();
+
+ return innerDeferredResult;
+ }, this));
+ deferredResult.releaseLock(this.deferredLockForSection('serverData'));
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'connectionVersion': function() {
+ return this._connectionVersion;
+ },
+
+ 'setConnectionVersion': function(aValue) {
+ if (this._connectionVersion != aValue) {
+ this.resetConnection();
+ }
+ this._connectionVersion = aValue;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'connection': function() {
+ if ((this._connection == null) && (this.connectionVersion() != null) ){
+ this._connection = new Clipperz.PM.Connection.communicationProtocol.versions[this.connectionVersion()]({
+ getCredentialsFunction: MochiKit.Base.method(this, 'getCredentials')
+ });
+ }
+
+ return this._connection;
+ },
+
+ 'resetConnection': function(aValue) {
+ if (this._connection != null) {
+ this._connection.reset();
+ }
+
+ this._connection = null;
+ },
+
+ //=========================================================================
+
+ 'getHeaderIndex': function (aKey) {
+ return Clipperz.Async.callbacks("User.getHeaderIndex", [
+ MochiKit.Base.method(this, 'getServerData'),
+ MochiKit.Base.itemgetter('header'),
+ MochiKit.Base.itemgetter(aKey)
+ ], {trace:false})
+ },
+
+ //=========================================================================
+
+ 'getRecords': function () {
+ return Clipperz.Async.callbacks("User.getRecords", [
+ MochiKit.Base.method(this, 'getHeaderIndex', 'recordsIndex'),
+ MochiKit.Base.methodcaller('records'),
+ MochiKit.Base.values
+ ], {trace:false});
+ },
+
+ 'recordWithLabel': function (aLabel) {
+ return Clipperz.Async.callbacks("User.recordWithLabel", [
+ MochiKit.Base.method(this, 'getRecords'),
+ MochiKit.Base.partial(Clipperz.Async.deferredFilter, function (aRecord) {
+ return Clipperz.Async.callbacks("User.recordWithLabel - check record label", [
+ MochiKit.Base.methodcaller('label'),
+ MochiKit.Base.partial(MochiKit.Base.operator.eq, aLabel)
+ ], {trace:false}, aRecord);
+ }),
+ function (someFilteredResults) {
+ var result;
+
+ switch (someFilteredResults.length) {
+ case 0:
+ result = null;
+ break;
+ case 1:
+ result = someFilteredResults[0];
+ break;
+ default:
+ WTF = TODO;
+ break;
+ }
+
+ return result;
+ }
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getRecord': function (aRecordReference) {
+ return Clipperz.Async.callbacks("User.getRecord", [
+ MochiKit.Base.method(this, 'getHeaderIndex', 'recordsIndex'),
+ MochiKit.Base.methodcaller('records'),
+ MochiKit.Base.itemgetter(aRecordReference),
+
+ Clipperz.Async.deferredIf("record != null", [
+ MochiKit.Base.operator.identity
+ ], [
+ function () { throw "Record does not exists"}
+ ])
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getRecordDetail': function (aRecordReference) {
+ return this.connection().message('getRecordDetail', {reference: aRecordReference});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'deleteRecord': function (aRecord) {
+ return Clipperz.Async.callbacks("User.deleteRecord", [
+ MochiKit.Base.method(this, 'getHeaderIndex', 'recordsIndex'),
+ MochiKit.Base.methodcaller('deleteRecord', aRecord)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'createNewRecord': function () {
+ return Clipperz.Async.callbacks("User.createNewRecord", [
+ MochiKit.Base.method(this, 'getHeaderIndex', 'recordsIndex'),
+ MochiKit.Base.methodcaller('createNewRecord')
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'getDirectLogins': function() {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.getDirectLogins", {trace:false});
+ deferredResult.addMethod(this, 'getRecords');
+ deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.compose(MochiKit.Base.values, MochiKit.Base.methodcaller('directLogins')));
+ deferredResult.addCallback(MochiKit.Base.flattenArray);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'getOneTimePasswords': function () {
+ return Clipperz.Async.callbacks("User.getOneTimePasswords", [
+ MochiKit.Base.method(this, 'getHeaderIndex', 'oneTimePasswords'),
+ MochiKit.Base.methodcaller('oneTimePasswords'),
+ MochiKit.Base.values
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'invokeMethodNamedOnHeader': function (aMethodName, aValue) {
+ return Clipperz.Async.collectResults("User.invokeMethodNamedOnHeader [" + aMethodName + "]", {
+ 'recordIndex': [
+ MochiKit.Base.method(this, 'getHeaderIndex', 'recordsIndex'),
+ MochiKit.Base.methodcaller(aMethodName, aValue)
+ ],
+ 'preferences': [
+ MochiKit.Base.method(this, 'getHeaderIndex', 'preferences'),
+ MochiKit.Base.methodcaller(aMethodName, aValue)
+ ],
+ 'oneTimePasswords': [
+ MochiKit.Base.method(this, 'getHeaderIndex', 'oneTimePasswords'),
+ MochiKit.Base.methodcaller(aMethodName, aValue)
+ ]//,
+// 'statistics': [
+// MochiKit.Base.method(this, 'getStatistics'),
+// MochiKit.Base.methodcaller(aMethodName, aValue)
+// ]
+ }, {trace:false})();
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'invokeMethodNamedOnRecords': function (aMethodName, aValue) {
+ return Clipperz.Async.callbacks("User.invokeMethodNamedOnRecords[" + aMethodName + "]", [
+ MochiKit.Base.method(this, 'getRecords'),
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller(aMethodName, aValue)),
+ Clipperz.Async.collectAll
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'hasPendingChanges': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.hasPendingChanges", {trace:false});
+ deferredResult.collectResults({
+ 'header': [
+ MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'hasPendingChanges'),
+ MochiKit.Base.values
+ ],
+ 'records': MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'hasPendingChanges')
+ });
+ deferredResult.addCallback(Clipperz.Async.or);
+ deferredResult.callback();
+// recordsIndex = legacyHeader;
+// preferences = legacyHeader;
+// oneTimePasswords = legacyHeader;
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'commitTransientState': function () {
+ return Clipperz.Async.callbacks("User.commitTransientState", [
+ MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'commitTransientState'),
+ MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'commitTransientState'),
+
+ MochiKit.Base.method(this, 'transientState'),
+// MochiKit.Base.itemgetter('lock'),
+// MochiKit.Base.method(this, 'setServerLockValue'),
+ MochiKit.Base.method(this, 'resetTransientState', true)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'revertChanges': function () {
+ return Clipperz.Async.callbacks("User.revertChanges", [
+ MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'revertChanges'),
+ MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'revertChanges'),
+ MochiKit.Base.method(this, 'resetTransientState', false)
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'deleteAllCleanTextData': function () {
+ return Clipperz.Async.callbacks("User.deleteAllCleanTextData", [
+ MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'deleteAllCleanTextData'),
+ MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'deleteAllCleanTextData'),
+
+ MochiKit.Base.method(this.data(), 'removeAllData'),
+ MochiKit.Base.method(this, 'resetTransientState', false)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'hasAnyCleanTextData': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.hasAnyCleanTextData", {trace:false});
+ deferredResult.collectResults({
+ 'header': [
+ MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'hasAnyCleanTextData'),
+ MochiKit.Base.values
+ ],
+ 'records': MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'hasAnyCleanTextData'),
+ 'data': MochiKit.Base.bind(function () {
+ return MochiKit.Async.succeed(! this.data().isEmpty());
+ }, this),
+ 'transientState': MochiKit.Base.bind(function () {
+ return MochiKit.Async.succeed(MochiKit.Base.keys(this.transientState()).length != 0);
+ }, this)
+ });
+ deferredResult.addCallback(Clipperz.Async.or);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'prepareRemoteDataWithKey': function (aKey /*, aCurrentKey*/) {
+ var deferredResult;
+ var result;
+
+ result = {};
+ deferredResult = new Clipperz.Async.Deferred("User.prepareRemoteDataWithKey", {trace:false});
+ deferredResult.addMethod(this, 'invokeMethodNamedOnHeader', 'prepareRemoteDataWithKey', aKey /*, aCurrentKey*/);
+ deferredResult.addCallback(MochiKit.Base.bind(function (aResult, someHeaderPackedData) {
+ var header;
+
+ header = {};
+ header['records'] = someHeaderPackedData['recordIndex']['records'];
+ header['directLogins'] = someHeaderPackedData['recordIndex']['directLogins'];
+ header['preferences'] = {'data': someHeaderPackedData['preferences']['data']};
+ header['oneTimePasswords'] = {'data': someHeaderPackedData['oneTimePasswords']['data']};
+ header['version'] = '0.1';
+
+ aResult['header'] = Clipperz.Base.serializeJSON(header);
+ aResult['statistics'] = this._serverData['statistics']; // "someHeaderPackedData['statistics']['data']";
+
+ return aResult;
+ }, this), result);
+ deferredResult.addCallback(Clipperz.Async.setItem, result, 'version', Clipperz.PM.Crypto.encryptingFunctions.currentVersion);
+// deferredResult.addCallback(Clipperz.Async.setItem, result, 'lock', this.serverLockValue());
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'saveChanges': function () {
+ var deferredResult;
+ var messageParameters;
+
+ messageParameters = {};
+
+ deferredResult = new Clipperz.Async.Deferred("User.saveChangaes", {trace:false});
+
+ deferredResult.addMethod(this, 'getHeaderIndex', 'recordsIndex');
+ deferredResult.addCallback(MochiKit.Base.methodcaller('prepareRemoteDataForChangedRecords'));
+ deferredResult.addCallback(Clipperz.Async.setItem, messageParameters, 'records');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+
+ deferredResult.addMethod(this, 'getPassphrase');
+ deferredResult.addMethod(this, 'prepareRemoteDataWithKey');
+ deferredResult.addCallback(Clipperz.Async.setItem, messageParameters, 'user');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+
+ deferredResult.addCallback(MochiKit.Async.succeed, messageParameters);
+ deferredResult.addMethod(this.connection(), 'message', 'saveChanges');
+ deferredResult.addCallback(MochiKit.Base.update, this.transientState())
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+
+ deferredResult.addMethod(this, 'commitTransientState');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'userDataSuccessfullySaved');
+
+ deferredResult.addErrbackPass(MochiKit.Base.method(this, 'revertChanges'));
+// deferredResult.addErrbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'failureWhileSavingUserData');
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
+//-----------------------------------------------------------------------------
+
+Clipperz.PM.DataModel.User.registerNewAccount = function (anUsername, aPassphraseFunction) {
+ var deferredResult;
+ var user;
+
+ user = new Clipperz.PM.DataModel.User({'username':anUsername, 'getPassphraseFunction':aPassphraseFunction});
+
+ deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.User.registerNewAccount", {trace:false});
+ deferredResult.addMethod(user, 'registerAsNewAccount');
+ deferredResult.addMethod(user, 'login');
+ deferredResult.addCallback(MochiKit.Async.succeed, user);
+ deferredResult.callback();
+
+ return deferredResult;
+}
+
+//-----------------------------------------------------------------------------
+
+Clipperz.PM.DataModel.User.exception = {
+ LoginFailed: new MochiKit.Base.NamedError("Clipperz.PM.DataModel.User.exception.LoginFailed"),
+ CredentialUpgradeFailed: new MochiKit.Base.NamedError("Clipperz.PM.DataModel.User.exception.CredentialUpgradeFailed")
+};
+
+//-----------------------------------------------------------------------------