1
0
mirror of http://git.whoc.org.uk/git/password-manager.git synced 2025-10-25 17:47:34 +02:00

Implemented Attachments in client

This commit is contained in:
Dario Chiappetta
2015-11-23 16:10:44 +01:00
parent 8608fb4253
commit 8c59393433
50 changed files with 4862 additions and 272 deletions

View File

@@ -0,0 +1,374 @@
/*
Copyright 2008-2015 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.Attachment = function(args) {
args = args || {};
Clipperz.PM.DataModel.Attachment.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().retrieveAttachmentIndexDataFunction()
// || Clipperz.Base.exception.raise('MandatoryParameter');
// this._setIndexDataFunction = args.setIndexDataFunction
// || this.record().setAttachmentIndexDataFunction()
// || Clipperz.Base.exception.raise('MandatoryParameter');
// this._removeIndexDataFunction = args.removeIndexDataFunction
// || this.record().removeAttachmentIndexDataFunction()
// || Clipperz.Base.exception.raise('MandatoryParameter');
// this.setFile(args.file);
this._transientState = null;
this._isBrandNew = MochiKit.Base.isUndefinedOrNull(args.reference);
this.record().bindAttachment(this);
if (this._isBrandNew) {
this.setKey(Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(256/8));
this.setNonce(Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(128/8));
}
return this;
}
Clipperz.Base.extend(Clipperz.PM.DataModel.Attachment, Object, {
'toString': function() {
return "Attachment (" + 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(), 'removeAttachment', this)
], {trace:false});
},
//=========================================================================
'file': function () {
// return this.getValue('name');
return MochiKit.Async.succeed(this._file);
},
'setFile': function (aFile) {
this._file = aFile || null;
/* These ones will disappear when the application is closed */
this._name = aFile ? aFile['name'] : null;
this._contentType = aFile ? aFile['type'] : null;
this._size = aFile ? aFile['size'] : null;
/* These ones will be saved in the Record */
return Clipperz.Async.callbacks("Attachment.setFile", [
MochiKit.Base.method(this, 'setValue', 'name', aFile['name']),
MochiKit.Base.method(this, 'setValue', 'contentType', aFile['type']),
MochiKit.Base.method(this, 'setValue', 'size', aFile['size']),
MochiKit.Base.partial(MochiKit.Async.succeed, this),
], {trace:false});
},
//-------------------------------------------------------------------------
'name': function () {
return this.getValue('name');
},
'contentType': function () {
return this.getValue('contentType');
},
'size': function () {
return this.getValue('size');
},
'metadata': function () {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred("Attachment.metadata [collect results]", {trace:false});
deferredResult.collectResults({
'name': MochiKit.Base.method(this, 'name'),
'type': MochiKit.Base.method(this, 'contentType'),
'size': MochiKit.Base.method(this, 'size'),
}, {trace:false});
deferredResult.callback();
return deferredResult;
// return {
// 'name': this._name,
// 'type': this._type,
// 'size': this._size,
// }
},
//-------------------------------------------------------------------------
'key': function () {
var byteArray;
byteArray = new Clipperz.ByteArray();
return Clipperz.Async.callbacks("Attachment.key", [
MochiKit.Base.method(this, 'getValue', 'key'),
MochiKit.Base.method(byteArray, 'appendBase64String'),
function(aByteArray) { return new Uint8Array(aByteArray.arrayValues()); }
], {trace:false});
},
// 'key': function () {
// var result;
// result = new Clipperz.ByteArray();
// return Clipperz.Async.callbacks("Attachment.key", [
// MochiKit.Base.method(this, 'getValue', 'key'),
// MochiKit.Base.method(result, 'appendBase64String'),
// function(aByteArray) { return new Clipperz.Crypto.AES.Key({key: aByteArray}); }
// ], {trace:false});
// },
'nonce': function () {
var byteArray;
byteArray = new Clipperz.ByteArray();
return Clipperz.Async.callbacks("Attachment.nonce", [
MochiKit.Base.method(this, 'getValue', 'nonce'),
MochiKit.Base.method(byteArray, 'appendBase64String'),
function(aByteArray) { return new Uint8Array(aByteArray.arrayValues()); }
], {trace:false});
},
// 'nonce': function () {
// var result;
// result = new Clipperz.ByteArray();
// return Clipperz.Async.callbacks("Attachment.nonce", [
// MochiKit.Base.method(this, 'getValue', 'nonce'),
// MochiKit.Base.method(result, 'appendBase64String')
// ], {trace:false});
// },
'setKey': function (aByteArray) {
this.setValue('key', aByteArray.toBase64String());
},
// 'setKey': function (aKey) {
// var byteArray = aKey.key();
// var serializedData = byteArray.toBase64String();
// this.setValue('key', serializedData);
// },
'setNonce': function (aByteArray) {
this.setValue('nonce', aByteArray.toBase64String());
},
//=========================================================================
'serializedData': function () {
return Clipperz.Async.collectResults("Attachment.serializedData", {
'name': MochiKit.Base.method(this, 'name'),
'contentType': MochiKit.Base.method(this, 'contentType'),
'size': MochiKit.Base.method(this, 'size'),
}, {trace:false})()
},
//=========================================================================
'hasPendingChanges': function () {
var result;
// var deferredResult;
result = false;
result = result || this.isBrandNew();
return MochiKit.Async.succeed(result);
},
//-------------------------------------------------------------------------
'revertChanges': function () {
return MochiKit.Async.succeed();
},
//=========================================================================
'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;
},
//=========================================================================
'actualKey': function (aValueKey) {
var actualKey;
actualKey = 'attachments' + '.' + this.reference();
if (aValueKey != '') {
actualKey = actualKey + '.' + aValueKey;
}
return actualKey;
},
//-------------------------------------------------------------------------
'getValue': function (aValueKey) {
return this.record().getValue(this.actualKey(aValueKey));
},
'setValue': function (aValueKey, aValue) {
return Clipperz.Async.callbacks("Attachment.setValue", [
// MochiKit.Base.method(this, 'getValue', ''),
// MochiKit.Base.bind(function (aValue) {
// if (this.originalConfiguration() == null) {
// this.setOriginalConfiguration(aValue);
// }
// }, this),
MochiKit.Base.method(this.record(), 'setValue', this.actualKey(aValueKey), aValue)
], {trace:false});
},
'removeValue': function (aValueKey) {
return this.record().removeValue(this.actualKey(aValueKey));
},
//=========================================================================
'content': function () {
// return this.serializedData();
// return MochiKit.Async.succeed(this);
var deferredResult;
var fieldValues;
fieldValues = {};
deferredResult = new Clipperz.Async.Deferred("Attachment.content", {trace:false});
deferredResult.addMethod(this, 'reference');
deferredResult.addCallback(function (aValue) { fieldValues['reference'] = aValue; });
deferredResult.callback();
return deferredResult;
},
//=========================================================================
'deleteAllCleanTextData': function () {
this._name = null;
this._contentType = null;
this._size = null;
this.resetTransientState();
},
//-------------------------------------------------------------------------
'hasAnyCleanTextData': function () {
var result;
result = false;
result = result || (this._name != null);
result = result || (this._contentType != null);
result = result || (this._size != null);
result = result || (MochiKit.Base.keys(this.transientState()).length != 0);
return MochiKit.Async.succeed(result);
},
//=========================================================================
__syntaxFix__: "syntax fix"
});
Clipperz.PM.DataModel.Attachment.MAX_ATTACHMENT_SIZE = 50*1024*1024;
Clipperz.PM.DataModel.Attachment.contentTypeIcon = function (aContentType) {
var result;
result = 'other file';
if (aContentType == "application/pdf") {
result = 'pdf file';
} else if (aContentType.indexOf('image/') == 0) {
result = 'image file';
} else if (aContentType.indexOf('model/') == 0) {
result = 'other file';
} else if (aContentType.indexOf('audio/') == 0) {
result = 'audio file';
} else if (aContentType.indexOf('text/') == 0) {
result = 'text file';
} else if (aContentType.indexOf('video/') == 0) {
result = 'video file';
}
return result;
};

View File

@@ -55,7 +55,7 @@ Clipperz.PM.DataModel.DirectLogin = function(args) {
this._isBrandNew = MochiKit.Base.isUndefinedOrNull(args.reference);
this.record().addDirectLogin(this);
this.record().bindDirectLogin(this);
return this;
}
@@ -1018,7 +1018,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.DirectLogin, Object, {
'removeValue': function (aValueKey) {
// return this.record().removeValue(this.actualKey(aValueKey));
return Clipperz.Async.callbacks("DirectLogin.setValue", [
return Clipperz.Async.callbacks("DirectLogin.removeValue", [
MochiKit.Base.method(this, 'originalConfiguration'),
Clipperz.Async.deferredIf("originalConfiguration has been set", [
], [

View File

@@ -192,7 +192,7 @@ Clipperz.PM.DataModel.EncryptedRemoteObject.prototype = MochiKit.Base.update(nul
innerDeferredResult = MochiKit.Async.succeed(this._remoteData);
} else {
innerDeferredResult = Clipperz.Async.callbacks("EncryptedRemoteObjects.getRemoteData <inner deferred>", [
MochiKit.Base.partial(this.retrieveRemoteDataFunction(), this.reference()),
MochiKit.Base.partial(this.retrieveRemoteDataFunction(), this),
MochiKit.Base.method(this, 'setRemoteData'),
], {trace:false});
}
@@ -542,3 +542,19 @@ Clipperz.PM.DataModel.EncryptedRemoteObject.prototype = MochiKit.Base.update(nul
//-------------------------------------------------------------------------
__syntaxFix__: "syntax fix"
});
/*
Clipperz.PM.DataModel.EncryptedRemoteObject.emptyObjectWithKey = function (aKey) {
var key = "clipperz";
var value = new Clipperz.ByteArray();
var version = Clipperz.PM.Crypto.encryptingFunctions.currentVersion;
// var remoteData = Clipperz.PM.Crypto.encrypt({'key':key, 'value':value, 'version':version});
var remoteData = Clipperz.PM.Crypto.encryptingFunctions.versions[version].encrypt(key, value);
return new Clipperz.PM.DataModel.EncryptedRemoteObject({
'reference': Clipperz.PM.Crypto.randomKey(),
'remoteData': remoteData,
'retrieveKeyFunction': function () { return key; },
});
};
*/

View File

@@ -41,11 +41,18 @@ Clipperz.PM.DataModel.Record = function(args) {
this._retrieveDirectLoginIndexDataFunction = args.retrieveDirectLoginIndexDataFunction || null;
this._setDirectLoginIndexDataFunction = args.setDirectLoginIndexDataFunction || null;
this._removeDirectLoginIndexDataFunction = args.removeDirectLoginIndexDataFunction || null;
this._createNewDirectLoginFunction = args.createNewDirectLoginFunction || null;
this._retrieveAttachmentIndexDataFunction = args.retrieveAttachmentIndexDataFunction || null;
this._setAttachmentIndexDataFunction = args.setAttachmentIndexDataFunction || null;
this._removeAttachmentIndexDataFunction = args.removeAttachmentIndexDataFunction || null;
this._createNewAttachmentFunction = args.createNewAttachmentFunction || null;
this._attachmentServerStatus = {};
this._tags = [];
this._directLogins = {};
this._attachments = {};
this._versions = {};
this._currentRecordVersion = null;
@@ -132,6 +139,16 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
return deferredResult;
},
//----------------------------------------------------------------------------
setAttachmentServerStatus: function(anObject) {
this._attachmentServerStatus = anObject;
},
getAttachmentServerStatus: function() {
return this._attachmentServerStatus;
},
//============================================================================
/*
'key': function () {
@@ -283,6 +300,12 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
return MochiKit.Async.succeed(this._accessDate);
},
//=========================================================================
attachmentsCount: function() {
return MochiKit.Base.keys(this.attachments()).length;
},
//=========================================================================
'favicon': function () {
@@ -374,6 +397,13 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
deferredResult.addCallback(MochiKit.Base.map, function (aValue) { result['directLogins'].push(aValue); });
deferredResult.addCallback(function () { return result; });
deferredResult.addMethod(this, 'attachments');
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;
@@ -385,7 +415,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
return this._directLogins;
},
'addDirectLogin': function (aDirectLogin) {
'bindDirectLogin': function (aDirectLogin) {
this._directLogins[aDirectLogin.reference()] = aDirectLogin;
},
@@ -400,12 +430,13 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
'saveOriginalDirectLoginStatusToTransientState': function () {
if (this.transientState().getValue('directLogins') == null) {
// this.transientState().setValue('directLogins', this._directLogins)
this.transientState().setValue('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();
@@ -453,6 +484,81 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
return result;
},
//=========================================================================
// TODO: !!!!
'attachments': function () {
return this._attachments;
},
'bindAttachment': function (anAttachment) {
this._attachments[anAttachment.reference()] = anAttachment;
},
'attachmentWithReference': function (anAttachmentReference) {
return this._attachments[anAttachmentReference];
},
'createNewAttachmentFunction': function () {
return this._createNewAttachmentFunction;
},
'saveOriginalAttachmentStatusToTransientState': function () {
if (this.transientState().getValue('attachments') == null) {
// console.log("saving attachments to transient state: ts:", this.transientState());
this.transientState().setValue('attachments', {});
MochiKit.Iter.forEach(MochiKit.Base.keys(this._attachments), MochiKit.Base.bind(function(aKey) {
// console.log("saving attachment to transient state:", this._attachments[aKey]);
this.transientState().setValue('attachments' + '.' + aKey, this._attachments[aKey])
}, this))
}
},
'createNewAttachment': function () {
this.saveOriginalAttachmentStatusToTransientState();
return this.createNewAttachmentFunction()(this);
},
'removeAttachment': function(anAttachment) {
this.saveOriginalAttachmentStatusToTransientState();
return Clipperz.Async.callbacks("Record.removeAttachment", [
MochiKit.Base.method(this, 'removeValue', 'attachments' + '.' + anAttachment.reference()),
MochiKit.Base.bind(function () {
this._removeAttachmentIndexDataFunction(anAttachment.reference());
delete this._attachments[anAttachment.reference()]
}, this)
], {trace:false});
},
'attachmentReferences': function () {
var result;
result = Clipperz.Async.callbacks("Record.attachmentReferences", [
MochiKit.Base.method(this, 'attachments'),
MochiKit.Base.values,
function (someAttachments) {
var result;
var i,c;
result = [];
c = someAttachments.length;
for (i=0; i<c; i++) {
result.push(Clipperz.Async.collectResults("Record.attachmentReferences - collectResults", {
'_rowObject': MochiKit.Async.succeed,
'_reference': MochiKit.Base.methodcaller('reference'),
// 'label': MochiKit.Base.methodcaller('label'),
// 'favicon': MochiKit.Base.methodcaller('favicon')
}, {trace:false})(someAttachments[i]));
};
return result;
},
Clipperz.Async.collectAll
], {trace:false});
return result;
},
//=========================================================================
'unpackRemoteData': function (someData) {
@@ -508,6 +614,8 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
newVersionKey = Clipperz.PM.Crypto.randomKey();
result = {};
result['attachments'] = MochiKit.Base.keys(this.attachments());
deferredResult = new Clipperz.Async.Deferred("Record.prepareRemoteDataWithKey", {trace:false});
deferredResult.addCallbackList([
Clipperz.Async.collectResults("Record.prepareRemoteDataWithKey - collect results", {
@@ -581,7 +689,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
result = someFilteredResults[0];
break;
default:
Clipperz.log("Warning: Record.fieldWithLabel('" + aLabel + "') is returning more than one result: " + someFilteredResults.length);
//console.log("Warning: Record.fieldWithLabel('" + aLabel + "') is returning more than one result: " + someFilteredResults.length);
result = someFilteredResults[0];
break;
}
@@ -607,7 +715,7 @@ Clipperz.log("Warning: Record.fieldWithLabel('" + aLabel + "') is returning more
var transientStateKey;
if (typeof(aVersionReference) == 'undefined') {
Clipperz.log("ERROR; getVersionKey aVersionReference is undefined");
console.log("ERROR; getVersionKey aVersionReference is undefined");
}
transientStateKey = 'versionKeys' + '.' + aVersionReference;
@@ -757,60 +865,6 @@ Clipperz.log("Warning: Record.fieldWithLabel('" + aLabel + "') is returning more
},
//=========================================================================
/*
'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(function (aValue) { console.log("Record.hasPendingChanges", aValue); return aValue; });
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;
},
*/
'hasPendingChanges': function () {
var deferredResult;
@@ -836,27 +890,18 @@ deferredResult.addCallback(function (aValue) { console.log("Record.hasPendingCha
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasPendingChanges')),
Clipperz.Async.collectAll,
Clipperz.Async.or
],
'attachments': [
MochiKit.Base.method(this, 'attachments'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasPendingChanges')),
Clipperz.Async.collectAll,
Clipperz.Async.or
]
});
//deferredResult.addCallback(function (someValues) {
// if (recordReference == 'd620764a656bfd4e1d3758500d5db72e460a0cf729d56ed1a7755b5725c50045') {
// console.log("Record.hasPendingChanges VALUES", someValues);
// }
// return someValues;
//})
}, {trace:true});
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)) {
console.log("TRANSIENT STATE", this.transientState());
console.log("TRANSIENT STATE - hasPendingChanges", this.transientState().getValue('hasPendingChanges.indexData'));
result = MochiKit.Iter.some(MochiKit.Base.values(this.transientState().getValue('hasPendingChanges.indexData')), MochiKit.Base.operator.identity);
}
console.log("Record.hasPendingChanges RESULT", result);
*/
return result;
deferredResult.addCallback(MochiKit.Base.bind(function (someValues) {
return MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
}, this));
deferredResult.callback();
@@ -911,6 +956,8 @@ console.log("Record.hasPendingChanges RESULT", result);
//=========================================================================
'revertChanges': function () {
// console.log("Revert changes: attachments in transient state", this.transientState().getValue('attachments'));
var deferredResult;
// var recordReference = this.reference();
@@ -926,19 +973,19 @@ console.log("Record.hasPendingChanges RESULT", result);
// return true;
//});
deferredResult.addIf([
//function (aValue) { console.log("Record.revertChanges - 1"); return aValue; },
MochiKit.Base.method(this, 'cancelRevertedAttachmentsUpload'),
MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'revertChanges'),
//function (aValue) { console.log("Record.revertChanges - 2"); return aValue; },
MochiKit.Base.method(this, 'directLogins'),
//function (aValue) { console.log("Record.revertChanges - 3"); return aValue; },
MochiKit.Base.values,
//function (aValue) { console.log("Record.revertChanges - 4"); return aValue; },
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('revertChanges')),
//function (aValue) { console.log("Record.revertChanges - 5"); return aValue; },
MochiKit.Base.method(this, 'attachments'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('revertChanges')),
MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.revertChanges, this),
//function (aValue) { console.log("Record.revertChanges - 6"); return aValue; },
MochiKit.Base.method(this, '_getObjectDataStore'),
], [
MochiKit.Async.succeed
@@ -962,6 +1009,19 @@ console.log("Record.hasPendingChanges RESULT", result);
return deferredResult;
},
cancelRevertedAttachmentsUpload: function() {
var reference;
var transientStateAttachments = (this.transientState() ? this.transientState().values()['attachments'] : {}) || {};
var savedReferences = MochiKit.Base.keys(transientStateAttachments);
for (reference in this.attachments()) {
if (savedReferences.indexOf(reference) < 0) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'cancelAttachment', this.attachments()[reference]);
}
}
},
//-------------------------------------------------------------------------
'resetTransientState': function (isCommitting) {
@@ -969,6 +1029,8 @@ console.log("Record.hasPendingChanges RESULT", result);
// this._directLogins = this.transientState().getValue('directLogins');
// }
// console.log("Reset transient state: attachments:", this.transientState().getValue('directLogins'));
return Clipperz.Async.callbacks("Record.resetTransientState", [
//- MochiKit.Base.method(this, 'getCurrentRecordVersion'),
//- MochiKit.Base.methodcaller('resetTransientState'),
@@ -979,9 +1041,18 @@ console.log("Record.hasPendingChanges RESULT", result);
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('resetTransientState')),
MochiKit.Base.method(this, 'attachments'),
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');
if (isCommitting == false) {
if (this.transientState().getValue('directLogins') != null) {
this._directLogins = this.transientState().getValue('directLogins');
}
if (this.transientState().getValue('attachments') != null) {
this._attachments = this.transientState().getValue('attachments');
}
}
}, this),
@@ -1003,6 +1074,10 @@ console.log("Record.hasPendingChanges RESULT", result);
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.Base.method(this, 'attachments'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('commitTransientState'))
], [
MochiKit.Async.succeed
@@ -1028,6 +1103,20 @@ console.log("Record.hasPendingChanges RESULT", result);
//=========================================================================
'retrieveAttachmentIndexDataFunction': function () {
return this._retrieveAttachmentIndexDataFunction;
},
'setAttachmentIndexDataFunction': function () {
return this._setAttachmentIndexDataFunction;
},
'removeAttachmentIndexDataFunction': function () {
return this._removeAttachmentIndexDataFunction;
},
//=========================================================================
'deleteAllCleanTextData': function () {
// return Clipperz.PM.DataModel.Record.superclass.deleteAllCleanTextData.apply(this, arguments);
@@ -1040,6 +1129,10 @@ console.log("Record.hasPendingChanges RESULT", result);
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('deleteAllCleanTextData')),
MochiKit.Base.method(this, 'attachments'),
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});
},
@@ -1061,6 +1154,12 @@ console.log("Record.hasPendingChanges RESULT", result);
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasAnyCleanTextData')),
Clipperz.Async.collectAll
],
'attachments': [
MochiKit.Base.method(this, 'attachments'),
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)
]
@@ -1131,10 +1230,12 @@ console.log("Record.hasPendingChanges RESULT", result);
MochiKit.Base.method(aRecord, 'directLogins'), MochiKit.Base.values,
//function (aValue) { console.log("-> SETUP WITH RECORD: DirectLogin Values", aValue); return aValue; },
// TODO: possibly broken implementation of direct login cloning
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, 'addDirectLogin')),
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, 'bindDirectLogin')),
//function (aValue) { console.log("-> DirectLogin Values", aValue); return aValue; },
// Clipperz.Async.collectAll,
// TODO: attachments
MochiKit.Base.bind(function () { return this; }, this)
], {trace:false});
},
@@ -1230,7 +1331,8 @@ Clipperz.PM.DataModel.Record.defaultCardInfo = {
'_isArchived': MochiKit.Base.methodcaller('isArchived'),
'_isBrandNew': MochiKit.Base.methodcaller('isBrandNew'),
'label': MochiKit.Base.methodcaller('label'),
'favicon': MochiKit.Base.methodcaller('favicon')
'favicon': MochiKit.Base.methodcaller('favicon'),
'attachmentsCount': MochiKit.Base.methodcaller('attachmentsCount'),
};
Clipperz.PM.DataModel.Record.defaultSearchField = '_searchableContent';

View File

@@ -23,7 +23,7 @@ refer to http://www.clipperz.com.
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 = {}; }
@@ -49,13 +49,25 @@ Clipperz.PM.DataModel.User.Header.RecordIndex = function(args) {
}
});
//console.log("RECORD INDEX args", args);
this._attachmentsData = new Clipperz.PM.DataModel.EncryptedRemoteObject({
'name': 'attachmentsData',
'retrieveKeyFunction': args.retrieveKeyFunction,
'remoteData': {
'data': args.attachmentsData['data'],
'version': args.encryptedDataVersion,
}
});
this._tagsData =
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._attachmentsIndex = args.attachmentsData['index'] || Clipperz.Base.exception.raise('MandatoryParameter');
this._records = null;
@@ -97,6 +109,16 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
//-------------------------------------------------------------------------
'attachmentsIndex': function () {
return this._attachmentsIndex;
},
'attachmentsData': function () {
return this._attachmentsData;
},
//-------------------------------------------------------------------------
'lock': function () {
return this._lock;
},
@@ -164,6 +186,28 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
//-------------------------------------------------------------------------
'getAttachmentIndexData': function (anAttachmentReference) {
// TODO:
return this.attachmentsData().getValue(this.attachmentsIndex()[anAttachmentReference]);
},
'setAttachmentIndexData': function (anAttachmentReference, aKey, aValue) {
// TODO:
return this.attachmentsData().setValue(this.attachmentsIndex()[anAttachmentReference] + '.' + aKey, aValue);
},
'addAttachmentIndexData': function (anAttachmentReference) {
// TODO:
return this.attachmentsData().setValue(this.attachmentsIndex()[anAttachmentReference], {});
},
'removeAttachmentIndexData': function (anAttachmentReference) {
// TODO:
return this.attachmentsData().removeValue(this.attachmentsIndex()[anAttachmentReference])
},
//-------------------------------------------------------------------------
'records': function () {
var deferredResult;
@@ -184,6 +228,9 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
],
'directLogins': [
MochiKit.Base.method(this.directLoginsData(), 'values')
],
'attachments': [
MochiKit.Base.method(this.attachmentsData(), 'values')
]
})
innerDeferredResult.addCallback(MochiKit.Base.bind(function (someData) {
@@ -193,6 +240,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
recordsInvertedIndex = Clipperz.PM.DataModel.User.Header.RecordIndex.invertIndex(this.recordsIndex());
directLoginsInvertedIndex = Clipperz.PM.DataModel.User.Header.RecordIndex.invertIndex(this.directLoginsIndex());
attachmentsInvertedIndex = Clipperz.PM.DataModel.User.Header.RecordIndex.invertIndex(this.attachmentsIndex());
this._records = {};
@@ -201,13 +249,15 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
var reference;
var updateDate;
var accessDate;
var attachmentsCount;
reference = recordsInvertedIndex[indexReference];
if (typeof(someData['recordsStats'][reference]) != 'undefined') {
updateDate = someData['recordsStats'][reference]['updateDate'];
accessDate = someData['recordsStats'][reference]['accessDate'];
// attachmentsCount = (someData['attachmentsCount'][reference]) ? someData['attachmentsCount'][reference] : 0;
record = new Clipperz.PM.DataModel.Record({
'reference': reference,
'retrieveKeyFunction': MochiKit.Base.method(this, 'getRecordKey'),
@@ -221,8 +271,12 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
'retrieveDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'getDirectLoginIndexData'),
'setDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'setDirectLoginIndexData'),
'removeDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'removeDirectLoginIndexData'),
'createNewDirectLoginFunction': MochiKit.Base.method(this, 'createNewDirectLogin')
'createNewDirectLoginFunction': MochiKit.Base.method(this, 'createNewDirectLogin'),
'retrieveAttachmentIndexDataFunction': MochiKit.Base.method(this, 'getAttachmentIndexData'),
'setAttachmentIndexDataFunction': MochiKit.Base.method(this, 'setAttachmentIndexData'),
'removeAttachmentIndexDataFunction': MochiKit.Base.method(this, 'removeAttachmentIndexData'),
'createNewAttachmentFunction': MochiKit.Base.method(this, 'createNewAttachment'),
});
this._records[reference] = record;
@@ -248,6 +302,24 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stats associated
}
}
//console.log("RecordIndex.Records: attachments data:", someData['attachments']);
for (indexReference in someData['attachments']) {
var reference;
var record;
reference = attachmentsInvertedIndex[indexReference];
record = this._records[recordsInvertedIndex[someData['attachments'][indexReference]['record']]];
if (record != null) {
new Clipperz.PM.DataModel.Attachment({
'reference': reference,
'record': record
});
} else {
Clipperz.logWarning("WARNING: Attachment without a matching RECORD!!");
}
}
return this._records;
}, this));
innerDeferredResult.callback();
@@ -295,8 +367,12 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stats associated
'retrieveDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'getDirectLoginIndexData'),
'setDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'setDirectLoginIndexData'),
'removeDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'removeDirectLoginIndexData'),
'createNewDirectLoginFunction': MochiKit.Base.method(this, 'createNewDirectLogin'),
'createNewDirectLoginFunction': MochiKit.Base.method(this, 'createNewDirectLogin')
'retrieveAttachmentIndexDataFunction': MochiKit.Base.method(this, 'getAttachmentIndexData'),
'setAttachmentIndexDataFunction': MochiKit.Base.method(this, 'setAttachmentIndexData'),
'removeAttachmentIndexDataFunction': MochiKit.Base.method(this, 'removeAttachmentIndexData'),
'createNewAttachmentFunction': MochiKit.Base.method(this, 'createNewAttachment'),
});
this.transientState().setValue('newRecordsReferences' + '.' + newRecord.reference(), newRecord);
@@ -372,6 +448,40 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stats associated
//=========================================================================
'removeAttachment': function (anAttachment) {
// TODO:
// this.directLoginsData().removeValue(this.directLoginsIndex()[aDirectLogin.reference()]);
},
//-------------------------------------------------------------------------
'createNewAttachment': function (aRecord) {
// TODO:
// var newDirectLogin;
var newAttachment;
// var newDirectLoginIndexValue;
var newAttachmentIndexValue;
// newDirectLogin = new Clipperz.PM.DataModel.DirectLogin({record:aRecord});
newAttachment = new Clipperz.PM.DataModel.Attachment({'record':aRecord});
// newDirectLoginIndexValue = MochiKit.Base.listMax(MochiKit.Base.map(function (aValue) { return aValue * 1; }, MochiKit.Base.values(this.directLoginsIndex()))) + 1;
newAttachmentIndexValue = MochiKit.Base.listMax(MochiKit.Base.map(function (aValue) { return aValue * 1; }, MochiKit.Base.values(this.attachmentsIndex()))) + 1;
// this.transientState().setValue('newDirectLoginReferences' + '.' + newDirectLogin.reference(), newDirectLogin);
this.transientState().setValue('newAttachmentReferences' + '.' + newAttachment.reference(), newAttachment);
// this.directLoginsIndex()[newDirectLogin.reference()] = newDirectLoginIndexValue;
this.attachmentsIndex()[newAttachment.reference()] = newAttachmentIndexValue;
// this.directLoginsData().setValue(this.directLoginsIndex()[newDirectLogin.reference()], {'record': this.recordsIndex()[aRecord.reference()]});
this.attachmentsData().setValue(this.attachmentsIndex()[newAttachment.reference()], {'record': this.recordsIndex()[aRecord.reference()]});
// return newDirectLogin;
return newAttachment;
},
//=========================================================================
'deleteAllCleanTextData': function () {
return Clipperz.Async.callbacks("User.Header.RecordIndex.deleteAllCleanTextData", [
MochiKit.Base.method(this, 'recordsData'),
@@ -396,6 +506,10 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stats associated
MochiKit.Base.method(this, 'directLoginsData'),
MochiKit.Base.methodcaller('hasAnyCleanTextData')
],
'attachmentsData': [
MochiKit.Base.method(this, 'attachmentsData'),
MochiKit.Base.methodcaller('hasAnyCleanTextData')
],
});
deferredResult.addCallback(Clipperz.Async.or);
@@ -418,8 +532,13 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stats associated
'directLoginsData': [
MochiKit.Base.method(this, 'directLoginsData'),
MochiKit.Base.methodcaller('hasPendingChanges')
],
'attachments': [
MochiKit.Base.method(this, 'attachmentsData'),
MochiKit.Base.methodcaller('hasPendingChanges')
]
});
//deferredResult.addCallback(function (aValue) { console.log("HAS PENDING CHANGES", aValue); return aValue; });
deferredResult.addCallback(Clipperz.Async.or);
deferredResult.callback();
@@ -437,6 +556,9 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stats associated
MochiKit.Base.method(this, 'directLoginsData'),
MochiKit.Base.methodcaller('commitTransientState'),
MochiKit.Base.method(this, 'attachmentsData'),
MochiKit.Base.methodcaller('commitTransientState'),
MochiKit.Base.method(this, 'resetTransientState', true)
], {trace:false});
@@ -474,9 +596,21 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stats associated
}
}, this),
MochiKit.Base.bind(function () {
// TODO:
var attachmentReference;
for (attachmentReference in this.transientState().getValue('newAttachmentReferences')) {
delete this.attachmentsIndex()[attachmentReference];
}
}, this),
MochiKit.Base.method(this, 'directLoginsData'),
MochiKit.Base.methodcaller('revertChanges'),
MochiKit.Base.method(this, 'attachmentsData'),
MochiKit.Base.methodcaller('revertChanges'),
MochiKit.Base.method(this, 'resetTransientState', false)
], {trace:false});
},
@@ -528,6 +662,15 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stats associated
]
});
deferredResult.addCallback(Clipperz.Async.setItem, result, 'directLogins');
deferredResult.collectResults({
'index': MochiKit.Base.partial(MochiKit.Async.succeed, this.attachmentsIndex()),
'data': [
MochiKit.Base.method(this.attachmentsData(), 'prepareRemoteDataWithKey', aKey),
MochiKit.Base.itemgetter('data')
]
});
deferredResult.addCallback(Clipperz.Async.setItem, result, 'attachments');
deferredResult.addCallback(MochiKit.Async.succeed, result);

View File

@@ -254,6 +254,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
'recordsData': {'data':null, 'index':{}},
'recordsStats': null,
'directLoginsData': {'data':null, 'index':{}},
'attachmentsData': {'data':null, 'index':{}},
'encryptedDataVersion': Clipperz.PM.Crypto.encryptingFunctions.currentVersion,
'retrieveRecordDetailFunction': MochiKit.Base.method(this, 'getRecordDetail')
}),
@@ -465,13 +466,16 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
case '0.1':
var headerData;
//console.log("Server data", someServerData);
headerData = Clipperz.Base.evalJSON(someServerData['header']);
//console.log("headerData", headerData);
recordsIndex = new Clipperz.PM.DataModel.User.Header.RecordIndex({
'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase'),
'recordsData': headerData['records'],
'recordsStats': someServerData['recordsStats'],
'directLoginsData': headerData['directLogins'],
'attachmentsData': headerData['attachments'] || {'data': null, 'index':{}},
'encryptedDataVersion': someServerData['version'],
'retrieveRecordDetailFunction': MochiKit.Base.method(this, 'getRecordDetail')
});
@@ -695,7 +699,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
result = someFilteredResults[0];
break;
default:
Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more than one result: " + someFilteredResults.length);
//console.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more than one result: " + someFilteredResults.length);
result = someFilteredResults[0];
break;
}
@@ -723,8 +727,13 @@ Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more
//-------------------------------------------------------------------------
'getRecordDetail': function (aRecordReference) {
return this.connection().message('getRecordDetail', {reference: aRecordReference});
'getRecordDetail': function (aRecord) {
// return this.connection().message('getRecordDetail', {reference: aRecordReference});
return Clipperz.Async.callbacks("User.getRecordDetail", [
MochiKit.Base.method(this.connection(), 'message', 'getRecordDetail', {reference: aRecord.reference()}),
function(someInfo) { aRecord.setAttachmentServerStatus(someInfo['attachmentStatus']); return someInfo;}, // Couldn't find a better way...
], {trace:false});
},
//-------------------------------------------------------------------------
@@ -750,6 +759,7 @@ Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more
'createNewRecordFromJSON': function(someJSON) {
var deferredResult;
// TODO: how do we handle attachments?
deferredResult = new Clipperz.Async.Deferred("User.createNewRecordFromJSON", {trace:false});
deferredResult.collectResults({
'recordIndex': MochiKit.Base.method(this, 'getHeaderIndex', 'recordsIndex'),
@@ -770,6 +780,7 @@ Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more
});
return Clipperz.Async.callbacks("User.createNewRecordFromJSON__inner", [
// TODO: check if we should invoke the create new direct login methon on Record instead of calling it directly on the index
MochiKit.Base.method(recordIndex, 'createNewDirectLogin', record),
MochiKit.Base.methodcaller('setLabel', aDirectLogin['label']),
MochiKit.Base.methodcaller('setBookmarkletConfiguration', configuration),
@@ -937,6 +948,22 @@ Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more
], {trace:false});
},
//-------------------------------------------------------------------------
/*
'addNewAttachment': function(anAttachment) {
console.log("Adding attachment", anAttachment);
},
*/
'uploadAttachment': function(aRecordReference, anAttachmentReference, someData) {
// TODO: pass a callback to handle onProgress events (and modify Connection accordingly)
this.connection().message('uploadAttachment', {
'recordReference': aRecordReference,
'attachmentReference': anAttachmentReference,
'data': someData
});
},
//=========================================================================
'hasPendingChanges': function () {
@@ -1035,6 +1062,7 @@ Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more
header = {};
header['records'] = someHeaderPackedData['recordIndex']['records'];
header['directLogins'] = someHeaderPackedData['recordIndex']['directLogins'];
header['attachments'] = someHeaderPackedData['recordIndex']['attachments'];
header['preferences'] = {'data': someHeaderPackedData['preferences']['data']};
header['oneTimePasswords'] = {'data': someHeaderPackedData['oneTimePasswords']['data']};
header['version'] = '0.1';