import/export
This commit is contained in:
@@ -17,4 +17,12 @@ angular.module('app.frontend')
|
||||
apiController.setGk(new_keys.gk);
|
||||
}
|
||||
|
||||
// var note = new Note();
|
||||
// note.content = {title: "hello", text: "world"};
|
||||
// console.log("note content", note.content);
|
||||
// console.log("note title", note.title);
|
||||
// console.log("note json", JSON.stringify(note));
|
||||
//
|
||||
// console.log("Copy", _.cloneDeep(note));
|
||||
|
||||
});
|
||||
|
||||
@@ -102,7 +102,7 @@ angular.module('app.frontend')
|
||||
|
||||
this.setNote = function(note, oldNote) {
|
||||
this.editorMode = 'edit';
|
||||
if(note.text.length == 0) {
|
||||
if(note.content.text.length == 0) {
|
||||
this.focusTitle(100);
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ angular.module('app.frontend')
|
||||
}
|
||||
|
||||
this.renderedContent = function() {
|
||||
return markdownRenderer.renderHtml(markdownRenderer.renderedContentForText(this.note.text));
|
||||
return markdownRenderer.renderHtml(markdownRenderer.renderedContentForText(this.note.content.text));
|
||||
}
|
||||
|
||||
var statusTimeout;
|
||||
|
||||
@@ -18,6 +18,7 @@ angular.module('app.frontend')
|
||||
// });
|
||||
|
||||
scope.$on('auth:validation-success', function(ev) {
|
||||
// TODO
|
||||
setTimeout(function(){
|
||||
ctrl.onValidationSuccess();
|
||||
})
|
||||
@@ -64,7 +65,6 @@ angular.module('app.frontend')
|
||||
})
|
||||
|
||||
}.bind(this))
|
||||
|
||||
}
|
||||
|
||||
this.hasLocalData = function() {
|
||||
@@ -119,11 +119,9 @@ angular.module('app.frontend')
|
||||
}
|
||||
|
||||
this.onValidationSuccess = function() {
|
||||
if(this.user.local_encryption_enabled) {
|
||||
apiController.verifyEncryptionStatusOfAllNotes(this.user, function(success){
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.encryptionStatusForNotes = function() {
|
||||
@@ -138,31 +136,6 @@ angular.module('app.frontend')
|
||||
return countEncrypted + "/" + allNotes.length + " notes encrypted";
|
||||
}
|
||||
|
||||
this.toggleEncryptionStatus = function() {
|
||||
this.encryptionConfirmation = true;
|
||||
}
|
||||
|
||||
this.cancelEncryptionChange = function() {
|
||||
this.encryptionConfirmation = false;
|
||||
}
|
||||
|
||||
this.confirmEncryptionChange = function() {
|
||||
|
||||
var callback = function(success, enabled) {
|
||||
if(success) {
|
||||
this.encryptionConfirmation = false;
|
||||
this.user.local_encryption_enabled = enabled;
|
||||
}
|
||||
}.bind(this)
|
||||
|
||||
if(this.user.local_encryption_enabled) {
|
||||
apiController.disableEncryptionForUser(this.user, callback);
|
||||
} else {
|
||||
apiController.enableEncryptionForUser(this.user, callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.downloadDataArchive = function() {
|
||||
var link = document.createElement('a');
|
||||
link.setAttribute('download', 'neeto.json');
|
||||
@@ -170,6 +143,22 @@ angular.module('app.frontend')
|
||||
link.click();
|
||||
}
|
||||
|
||||
this.importFileSelected = function(files) {
|
||||
var file = files[0];
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
apiController.importJSONData(e.target.result, function(success, response){
|
||||
console.log("import response", success, response);
|
||||
if(success) {
|
||||
// window.location.reload();
|
||||
} else {
|
||||
alert("There was an error importing your data. Please try again.");
|
||||
}
|
||||
})
|
||||
}
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
this.onAuthSuccess = function(user) {
|
||||
this.user.id = user.id;
|
||||
|
||||
|
||||
@@ -164,8 +164,6 @@ angular.module('app.frontend')
|
||||
$scope.selectedNote = null;
|
||||
}
|
||||
|
||||
note.onDelete();
|
||||
|
||||
if(note.dummy) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -76,18 +76,12 @@ angular.module('app.frontend')
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.user.local_encryption_enabled) {
|
||||
if(!confirm("Sharing this group will disable local encryption on all group notes.")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var callback = function(username) {
|
||||
apiController.shareGroup(this.user, this.group, function(response){
|
||||
})
|
||||
}.bind(this);
|
||||
|
||||
if(!this.user.getUsername()) {
|
||||
if(!this.user.username) {
|
||||
ngDialog.open({
|
||||
template: 'frontend/modals/username.html',
|
||||
controller: 'UsernameModalCtrl',
|
||||
@@ -99,7 +93,7 @@ angular.module('app.frontend')
|
||||
disableAnimation: true
|
||||
});
|
||||
} else {
|
||||
callback(this.user.getUsername());
|
||||
callback(this.user.username);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,15 +132,12 @@ angular.module('app.frontend')
|
||||
this.selectNote = function(note) {
|
||||
this.selectedNote = note;
|
||||
this.selectionMade()(note);
|
||||
|
||||
note.onDelete = function() {
|
||||
this.setNotes(this.group.notes, false);
|
||||
}.bind(this);
|
||||
}
|
||||
|
||||
this.createNewNote = function() {
|
||||
var title = "New Note" + (this.notes ? (" " + (this.notes.length + 1)) : "");
|
||||
this.newNote = new Note({title: title, content: '', dummy: true});
|
||||
this.newNote = new Note({dummy: true});
|
||||
this.newNote.content.title = title;
|
||||
this.newNote.shared_via_group = this.group.presentation && this.group.presentation.enabled;
|
||||
this.selectNote(this.newNote);
|
||||
this.addNew()(this.newNote);
|
||||
|
||||
@@ -1,22 +1,33 @@
|
||||
var Note = function (json_obj) {
|
||||
var content;
|
||||
|
||||
Object.defineProperty(this, "content", {
|
||||
get: function() {
|
||||
return content;
|
||||
},
|
||||
set: function(value) {
|
||||
var finalValue = value;
|
||||
|
||||
if(typeof value === 'string') {
|
||||
try {
|
||||
decodedValue = JSON.parse(value);
|
||||
finalValue = decodedValue;
|
||||
}
|
||||
catch(e) {
|
||||
finalValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
content = finalValue;
|
||||
},
|
||||
enumerable: true,
|
||||
});
|
||||
|
||||
_.merge(this, json_obj);
|
||||
};
|
||||
|
||||
Note.prototype = {
|
||||
set content(content) {
|
||||
try {
|
||||
var data = JSON.parse(content);
|
||||
this.title = data.title || data.name;
|
||||
this.text = data.text || data.content;
|
||||
}
|
||||
catch(e) {
|
||||
this.text = content;
|
||||
}
|
||||
if(!this.content) {
|
||||
this.content = {title: "", text: ""};
|
||||
}
|
||||
}
|
||||
|
||||
Note.prototype.JSONContent = function() {
|
||||
return JSON.stringify({title: this.title, text: this.text});
|
||||
};
|
||||
|
||||
/* Returns true if note is shared individually or via group */
|
||||
@@ -25,7 +36,7 @@ Note.prototype.isPublic = function() {
|
||||
};
|
||||
|
||||
Note.prototype.isEncrypted = function() {
|
||||
return this.loc_eek || this.local_eek ? true : false;
|
||||
return (this.loc_eek || this.local_eek) && typeof this.content === 'string' ? true : false;
|
||||
}
|
||||
|
||||
Note.prototype.hasEnabledPresentation = function() {
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
var User = function (json_obj) {
|
||||
_.merge(this, json_obj);
|
||||
};
|
||||
|
||||
User.prototype.getUsername = function() {
|
||||
if(!this.presentation) {
|
||||
return null;
|
||||
}
|
||||
return this.presentation.root_path;
|
||||
};
|
||||
|
||||
@@ -99,23 +99,19 @@ angular.module('app.services')
|
||||
this._performPasswordChange(current_keys, new_keys, function(response){
|
||||
if(response && !response.errors) {
|
||||
// this.showNewPasswordForm = false;
|
||||
if(user.local_encryption_enabled) {
|
||||
// reencrypt data with new gk
|
||||
this.reencryptAllNotesAndSave(user, new_keys.gk, current_keys.gk, function(success){
|
||||
if(success) {
|
||||
this.setGk(new_keys.gk);
|
||||
alert("Your password has been changed and your data re-encrypted.");
|
||||
} else {
|
||||
// rollback password
|
||||
this._performPasswordChange(new_keys, current_keys, function(response){
|
||||
alert("There was an error changing your password. Your password has been rolled back.");
|
||||
window.location.reload();
|
||||
})
|
||||
}
|
||||
}.bind(this));
|
||||
} else {
|
||||
alert("Your password has been changed.");
|
||||
}
|
||||
// reencrypt data with new gk
|
||||
this.reencryptAllNotesAndSave(user, new_keys.gk, current_keys.gk, function(success){
|
||||
if(success) {
|
||||
this.setGk(new_keys.gk);
|
||||
alert("Your password has been changed and your data re-encrypted.");
|
||||
} else {
|
||||
// rollback password
|
||||
this._performPasswordChange(new_keys, current_keys, function(response){
|
||||
alert("There was an error changing your password. Your password has been rolled back.");
|
||||
window.location.reload();
|
||||
})
|
||||
}
|
||||
}.bind(this));
|
||||
} else {
|
||||
// this.showNewPasswordForm = false;
|
||||
alert("There was an error changing your password. Please try again.");
|
||||
@@ -144,46 +140,6 @@ angular.module('app.services')
|
||||
})
|
||||
}
|
||||
|
||||
this.enableEncryptionForUser = function(user, callback) {
|
||||
Restangular.one("users", user.id).one('enable_encryption').post().then(function(response){
|
||||
|
||||
var enabled = response.plain().local_encryption_enabled;
|
||||
if(!enabled) {
|
||||
callback(false, enabled);
|
||||
return;
|
||||
}
|
||||
this.handleEncryptionStatusChange(user, enabled, callback);
|
||||
}.bind(this))
|
||||
}
|
||||
|
||||
this.disableEncryptionForUser = function(user, callback) {
|
||||
|
||||
Restangular.one("users", user.id).one('disable_encryption').post().then(function(response){
|
||||
var enabled = response.plain().local_encryption_enabled;
|
||||
|
||||
if(enabled) {
|
||||
// something went wrong
|
||||
callback(false, enabled);
|
||||
return;
|
||||
}
|
||||
this.handleEncryptionStatusChange(user, enabled, callback);
|
||||
}.bind(this))
|
||||
}
|
||||
|
||||
this.handleEncryptionStatusChange = function(user, encryptionEnabled, callback) {
|
||||
var allNotes = user.filteredNotes();
|
||||
if(encryptionEnabled) {
|
||||
allNotes = allNotes.filter(function(note){return note.isPublic() == false});
|
||||
this.encryptNotes(allNotes, this.retrieveGk());
|
||||
} else {
|
||||
this.decryptNotes(allNotes, this.retrieveGk());
|
||||
}
|
||||
|
||||
this.saveBatchNotes(user, allNotes, encryptionEnabled, function(success) {
|
||||
callback(success, encryptionEnabled);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
/*
|
||||
Ensures that if encryption is disabled, all local notes are uncrypted,
|
||||
and that if it's enabled, that all local notes are encrypted
|
||||
@@ -193,7 +149,7 @@ angular.module('app.services')
|
||||
var notesNeedingUpdate = [];
|
||||
var key = this.retrieveGk();
|
||||
allNotes.forEach(function(note){
|
||||
if(user.local_encryption_enabled && !note.isPublic()) {
|
||||
if(!note.isPublic()) {
|
||||
if(!note.isEncrypted()) {
|
||||
// needs encryption
|
||||
this.encryptSingleNote(note, key);
|
||||
@@ -209,7 +165,7 @@ angular.module('app.services')
|
||||
}.bind(this))
|
||||
|
||||
if(notesNeedingUpdate.length > 0) {
|
||||
this.saveBatchNotes(user, notesNeedingUpdate, user.local_encryption_enabled, callback)
|
||||
this.saveBatchNotes(user, notesNeedingUpdate, true, callback)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,9 +222,12 @@ angular.module('app.services')
|
||||
})
|
||||
}
|
||||
|
||||
if(user.local_encryption_enabled && group.notes.length > 0) {
|
||||
if(group.notes.length > 0) {
|
||||
// decrypt group notes first
|
||||
var notes = group.notes;
|
||||
notes.forEach(function(note){
|
||||
note.shared_via_group = true;
|
||||
})
|
||||
this.decryptNotesWithLocalKey(notes);
|
||||
this.saveBatchNotes(user, notes, false, function(success){
|
||||
shareFn();
|
||||
@@ -333,17 +292,16 @@ angular.module('app.services')
|
||||
this.createRequestParamsFromNote = function(note, user) {
|
||||
var params = {id: note.id};
|
||||
|
||||
if(user.local_encryption_enabled && !note.pending_share && !note.isPublic()) {
|
||||
if(!note.pending_share && !note.isPublic()) {
|
||||
// encrypted
|
||||
var noteCopy = _.cloneDeep(note);
|
||||
this.encryptSingleNote(noteCopy, this.retrieveGk());
|
||||
|
||||
params.loc_enc_content = noteCopy.loc_enc_content || local_encrypted_content;
|
||||
params.loc_eek = noteCopy.loc_eek || noteCopy.local_eek;
|
||||
params.content = noteCopy.content;
|
||||
params.loc_eek = noteCopy.loc_eek;
|
||||
}
|
||||
else {
|
||||
// decrypted
|
||||
params.content = note.JSONContent();
|
||||
params.content = JSON.stringify(note.content);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
@@ -365,7 +323,7 @@ angular.module('app.services')
|
||||
if(!user.id) {
|
||||
if(confirm("Note: You are not signed in. Any note you share cannot be edited or unshared.")) {
|
||||
var request = Restangular.one("notes").one("share");
|
||||
_.merge(request, {name: note.title, content: note.content});
|
||||
_.merge(request, {name: note.content.title, content: note.content});
|
||||
request.post().then(function(response){
|
||||
var presentation = response.plain();
|
||||
_.merge(note, {presentation: presentation});
|
||||
@@ -384,16 +342,10 @@ angular.module('app.services')
|
||||
})
|
||||
}
|
||||
|
||||
if(user.local_encryption_enabled) {
|
||||
if(confirm("Note: Sharing this note will remove its local encryption.")) {
|
||||
note.pending_share = true;
|
||||
this.saveNote(user, note, function(saved_note){
|
||||
shareFn(saved_note, callback);
|
||||
})
|
||||
}
|
||||
} else {
|
||||
shareFn(note, callback);
|
||||
}
|
||||
note.pending_share = true;
|
||||
this.saveNote(user, note, function(saved_note){
|
||||
shareFn(saved_note, callback);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,6 +359,53 @@ angular.module('app.services')
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Import
|
||||
*/
|
||||
|
||||
this.importJSONData = function(jsonString, callback) {
|
||||
var data = JSON.parse(jsonString);
|
||||
console.log("importing data", JSON.parse(jsonString));
|
||||
// data.notes = _.map(data.notes, function(json_obj) {
|
||||
// return new Note(json_obj);
|
||||
// });
|
||||
console.log("objectifying data", this.staticifyObject(data));
|
||||
|
||||
data.notes.forEach(function(note){
|
||||
var presentation = data.presentations.find(function(presentation){
|
||||
return presentation.presentable_type == "Note" && presentation.presentable_id == note.id;
|
||||
})
|
||||
|
||||
if(presentation) {
|
||||
// public
|
||||
// console.log("public note", note);
|
||||
note.content = JSON.stringify(note.content);
|
||||
// console.log("after json", note);
|
||||
} else {
|
||||
// private
|
||||
this.encryptSingleNoteWithLocalKey(note);
|
||||
}
|
||||
}.bind(this))
|
||||
|
||||
|
||||
var request = Restangular.one("import");
|
||||
request.data = data;
|
||||
console.log("posting import request", request);
|
||||
request.post().then(function(response){
|
||||
callback(true, response);
|
||||
})
|
||||
.catch(function(error){
|
||||
callback(false, error);
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
Export
|
||||
*/
|
||||
|
||||
this.notesDataFile = function(user) {
|
||||
var textFile = null;
|
||||
var makeTextFile = function (text) {
|
||||
@@ -424,18 +423,59 @@ angular.module('app.services')
|
||||
return textFile;
|
||||
}.bind(this);
|
||||
|
||||
// remove irrelevant keys
|
||||
|
||||
var notes = _.map(user.filteredNotes(), function(note){
|
||||
console.log("mapping note", note);
|
||||
return {
|
||||
id: note.id,
|
||||
title: note.title,
|
||||
text: note.text,
|
||||
uuid: note.uuid,
|
||||
content: note.content,
|
||||
group_id: note.group_id,
|
||||
created_at: note.created_at,
|
||||
modified_at: note.modified_at,
|
||||
group_id: note.group_id
|
||||
}
|
||||
});
|
||||
return makeTextFile(JSON.stringify(notes, null, 2 /* pretty print */));
|
||||
|
||||
var groups = _.map(user.groups, function(group){
|
||||
return {
|
||||
id: group.id,
|
||||
uuid: group.uuid,
|
||||
name: group.name,
|
||||
created_at: group.created_at,
|
||||
modified_at: group.modified_at,
|
||||
}
|
||||
});
|
||||
|
||||
var modelsWithPresentations = user.groups.concat(user.notes).filter(function(model){
|
||||
return model.presentation != null;
|
||||
})
|
||||
|
||||
var presentations = _.map(modelsWithPresentations, function(model){
|
||||
return model.presentation;
|
||||
})
|
||||
|
||||
presentations = _.map(presentations, function(presentation){
|
||||
return {
|
||||
id: presentation.id,
|
||||
uuid: presentation.uuid,
|
||||
host: presentation.host,
|
||||
root_path: presentation.root_path,
|
||||
relative_path: presentation.relative_path,
|
||||
presentable_type: presentation.presentable_type,
|
||||
presentable_id: presentation.presentable_id,
|
||||
enabled: presentation.enabled,
|
||||
created_at: presentation.created_at,
|
||||
modified_at: presentation.modified_at,
|
||||
}
|
||||
});
|
||||
|
||||
var data = {
|
||||
notes: notes,
|
||||
groups: groups,
|
||||
presentations: presentations
|
||||
}
|
||||
|
||||
return makeTextFile(JSON.stringify(data, null, 2 /* pretty print */));
|
||||
}
|
||||
|
||||
|
||||
@@ -539,13 +579,13 @@ angular.module('app.services')
|
||||
|
||||
this.encryptSingleNote = function(note, key) {
|
||||
var ek = null;
|
||||
if(note.isEncrypted()) {
|
||||
ek = Neeto.crypto.decryptText(note.loc_eek || note.local_eek, key);
|
||||
if(note.loc_eek) {
|
||||
ek = Neeto.crypto.decryptText(note.loc_eek, key);
|
||||
} else {
|
||||
ek = Neeto.crypto.generateRandomEncryptionKey();
|
||||
note.loc_eek = Neeto.crypto.encryptText(ek, key);
|
||||
}
|
||||
note.loc_enc_content = Neeto.crypto.encryptText(note.JSONContent(), ek);
|
||||
note.content = Neeto.crypto.encryptText(JSON.stringify(note.content), ek);
|
||||
note.local_encryption_scheme = "1.0";
|
||||
}
|
||||
|
||||
@@ -563,18 +603,27 @@ angular.module('app.services')
|
||||
this.encryptNotes(notes, this.retrieveGk());
|
||||
}
|
||||
|
||||
this.encryptNonPublicNotesWithLocalKey = function(notes) {
|
||||
var nonpublic = notes.filter(function(note){
|
||||
return !note.isPublic() && !note.pending_share;
|
||||
})
|
||||
this.encryptNotes(nonpublic, this.retrieveGk());
|
||||
}
|
||||
|
||||
this.decryptSingleNoteWithLocalKey = function(note) {
|
||||
this.decryptSingleNote(note, this.retrieveGk());
|
||||
}
|
||||
|
||||
this.decryptSingleNote = function(note, key) {
|
||||
var ek = Neeto.crypto.decryptText(note.loc_eek || note.local_eek, key);
|
||||
var content = Neeto.crypto.decryptText(note.loc_enc_content || note.local_encrypted_content, ek);
|
||||
var content = Neeto.crypto.decryptText(note.content, ek);
|
||||
// console.log("decrypted contnet", content);
|
||||
note.content = content;
|
||||
}
|
||||
|
||||
this.decryptNotes = function(notes, key) {
|
||||
notes.forEach(function(note){
|
||||
// console.log("is encrypted?", note);
|
||||
if(note.isEncrypted()) {
|
||||
this.decryptSingleNote(note, key);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
angular
|
||||
.module('app.services')
|
||||
.directive('fileChange', function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
handler: '&'
|
||||
},
|
||||
link: function (scope, element) {
|
||||
element.on('change', function (event) {
|
||||
scope.$apply(function(){
|
||||
scope.handler({files: event.target.files});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user