diff --git a/app/assets/javascripts/app/frontend/controllers/editor.js b/app/assets/javascripts/app/frontend/controllers/editor.js index e42bcf9a3..4bb7cf926 100644 --- a/app/assets/javascripts/app/frontend/controllers/editor.js +++ b/app/assets/javascripts/app/frontend/controllers/editor.js @@ -235,11 +235,13 @@ angular.module('app.frontend') this.saveUrl = function($event) { $event.target.blur(); - var original = this.note.presentation.root_path; - this.note.presentation.root_path = this.url.token; - apiController.saveNote(this.user, this.note, function(note){ - if(!note) { - this.note.token = original; + + var original = this.note.presentation.relative_path; + this.note.presentation.relative_path = this.url.token; + + apiController.updatePresentation(this.note, this.note.presentation, function(response){ + if(!response) { + this.note.presentation.relative_path = original; this.url.token = original; alert("This URL is not available."); } else { diff --git a/app/assets/javascripts/app/frontend/controllers/groups.js b/app/assets/javascripts/app/frontend/controllers/groups.js index 3d1204937..39989394a 100644 --- a/app/assets/javascripts/app/frontend/controllers/groups.js +++ b/app/assets/javascripts/app/frontend/controllers/groups.js @@ -53,7 +53,7 @@ angular.module('app.frontend') return; } - this.newGroup = {notes : []}; + this.newGroup = new Group({notes : []}); if(!this.user.id) { this.newGroup.id = Neeto.crypto.generateRandomKey() } @@ -92,7 +92,7 @@ angular.module('app.frontend') } this.noteCount = function(group) { - var validNotes = apiController.filterDummyNotes(group.notes); + var validNotes = Note.filterDummyNotes(group.notes); return validNotes.length; } diff --git a/app/assets/javascripts/app/frontend/controllers/header.js b/app/assets/javascripts/app/frontend/controllers/header.js index f39de05c7..88bd95f1e 100644 --- a/app/assets/javascripts/app/frontend/controllers/header.js +++ b/app/assets/javascripts/app/frontend/controllers/header.js @@ -13,17 +13,7 @@ angular.module('app.frontend') bindToController: true, link:function(scope, elem, attrs, ctrl) { - // scope.$on('auth:login-success', function(event, user) { - // ctrl.onAuthSuccess(user); - // }); - scope.$on('auth:validation-success', function(ev) { - // TODO - setTimeout(function(){ - ctrl.onValidationSuccess(); - }) - - }); } } }) @@ -118,17 +108,11 @@ angular.module('app.frontend') }.bind(this)); } - this.onValidationSuccess = function() { - apiController.verifyEncryptionStatusOfAllNotes(this.user, function(success){ - - }); - } - this.encryptionStatusForNotes = function() { var allNotes = this.user.filteredNotes(); var countEncrypted = 0; allNotes.forEach(function(note){ - if(note.isEncrypted()) { + if(note.encryptionEnabled()) { countEncrypted++; } }.bind(this)) diff --git a/app/assets/javascripts/app/frontend/controllers/home.js b/app/assets/javascripts/app/frontend/controllers/home.js index 3455790d9..94f541334 100644 --- a/app/assets/javascripts/app/frontend/controllers/home.js +++ b/app/assets/javascripts/app/frontend/controllers/home.js @@ -6,21 +6,12 @@ angular.module('app.frontend') var onUserSet = function() { - $scope.defaultUser.notes = _.map($scope.defaultUser.notes, function(json_obj) { - return new Note(json_obj); - }); + $scope.allGroup = new Group({name: "All", all: true}); + $scope.groups = $scope.defaultUser.groups; - $scope.defaultUser.filteredNotes = function() { - return apiController.filterDummyNotes($scope.defaultUser.notes); - } - var groups = $scope.defaultUser.groups; - var allNotes = $scope.defaultUser.notes; - groups.forEach(function(group){ - var notes = allNotes.filter(function(note){return note.group_id && note.group_id == group.id}); - group.notes = notes; - }) - $scope.allGroup = {name: "All", all: true}; - $scope.groups = groups; + apiController.verifyEncryptionStatusOfAllNotes($scope.defaultUser, function(success){ + + }); } apiController.getCurrentUser(function(response){ @@ -39,7 +30,7 @@ angular.module('app.frontend') */ $scope.updateAllGroup = function() { - var allNotes = apiController.filterDummyNotes($scope.defaultUser.notes); + var allNotes = Note.filterDummyNotes($scope.defaultUser.notes); $scope.defaultUser.notes = allNotes; $scope.allGroup.notes = allNotes; } @@ -78,6 +69,8 @@ angular.module('app.frontend') originalNote.group_id = null; } else { originalNote.group_id = newGroup.id + originalNote.group = newGroup; + newGroup.notes.unshift(originalNote); newGroup.notes.sort(function(a,b){ //subtract to get a value that is either negative, positive, or zero. @@ -85,8 +78,6 @@ angular.module('app.frontend') }); } - originalNote.shared_via_group = newGroup.presentation && newGroup.presentation.enabled; - apiController.saveNote($scope.defaultUser, originalNote, function(note){ _.merge(originalNote, note); }); @@ -97,7 +88,7 @@ angular.module('app.frontend') */ $scope.notesRemoveGroup = function(group) { - var validNotes = apiController.filterDummyNotes(group.notes); + var validNotes = Note.filterDummyNotes(group.notes); if(validNotes == 0) { // if no more notes, delete group apiController.deleteGroup($scope.defaultUser, group, function(){ diff --git a/app/assets/javascripts/app/frontend/controllers/notes.js b/app/assets/javascripts/app/frontend/controllers/notes.js index a81d1f05e..88805218a 100644 --- a/app/assets/javascripts/app/frontend/controllers/notes.js +++ b/app/assets/javascripts/app/frontend/controllers/notes.js @@ -138,7 +138,7 @@ angular.module('app.frontend') var title = "New Note" + (this.notes ? (" " + (this.notes.length + 1)) : ""); this.newNote = new Note({dummy: true}); this.newNote.content.title = title; - this.newNote.shared_via_group = this.group.presentation && this.group.presentation.enabled; + this.newNote.group = this.group; this.selectNote(this.newNote); this.addNew()(this.newNote); } diff --git a/app/assets/javascripts/app/frontend/models/group.js b/app/assets/javascripts/app/frontend/models/group.js new file mode 100644 index 000000000..957642717 --- /dev/null +++ b/app/assets/javascripts/app/frontend/models/group.js @@ -0,0 +1,3 @@ +var Group = function (json_obj) { + _.merge(this, json_obj); +}; diff --git a/app/assets/javascripts/app/frontend/models/note.js b/app/assets/javascripts/app/frontend/models/note.js index 374c98f3b..53d5cb0bf 100644 --- a/app/assets/javascripts/app/frontend/models/note.js +++ b/app/assets/javascripts/app/frontend/models/note.js @@ -23,6 +23,10 @@ var Note = function (json_obj) { enumerable: true, }); + this.setContentRaw = function(rawContent) { + content = rawContent; + } + _.merge(this, json_obj); if(!this.content) { @@ -30,17 +34,22 @@ var Note = function (json_obj) { } }; +Note.filterDummyNotes = function(notes) { + var filtered = notes.filter(function(note){return note.dummy == false || note.dummy == null}); + return filtered; +} + /* Returns true if note is shared individually or via group */ Note.prototype.isPublic = function() { - return this.hasEnabledPresentation() || this.shared_via_group; + return this.presentation || (this.group && this.group.presentation); }; Note.prototype.isEncrypted = function() { - return (this.loc_eek || this.local_eek) && typeof this.content === 'string' ? true : false; + return this.encryptionEnabled() && typeof this.content === 'string' ? true : false; } -Note.prototype.hasEnabledPresentation = function() { - return this.presentation && this.presentation.enabled; +Note.prototype.encryptionEnabled = function() { + return this.loc_eek; } Note.prototype.presentationURL = function() { diff --git a/app/assets/javascripts/app/frontend/models/user.js b/app/assets/javascripts/app/frontend/models/user.js index 1421dd92b..a6ff61349 100644 --- a/app/assets/javascripts/app/frontend/models/user.js +++ b/app/assets/javascripts/app/frontend/models/user.js @@ -1,3 +1,23 @@ var User = function (json_obj) { _.merge(this, json_obj); + + this.notes = _.map(this.notes, function(json_obj) { + return new Note(json_obj); + }); + + this.groups = _.map(this.groups, function(json_obj) { + return new Group(json_obj); + }); + + this.groups.forEach(function(group){ + var notes = this.notes.filter(function(note){return note.group_id && note.group_id == group.id}); + notes.forEach(function(note){ + note.group = group; + }) + group.notes = notes; + }.bind(this)) }; + +User.prototype.filteredNotes = function() { + return Note.filterDummyNotes(this.notes); +} diff --git a/app/assets/javascripts/app/services/apiController.js b/app/assets/javascripts/app/services/apiController.js index beb5b2555..41a1c2d96 100644 --- a/app/assets/javascripts/app/services/apiController.js +++ b/app/assets/javascripts/app/services/apiController.js @@ -147,25 +147,21 @@ angular.module('app.services') this.verifyEncryptionStatusOfAllNotes = function(user, callback) { var allNotes = user.filteredNotes(); var notesNeedingUpdate = []; - var key = this.retrieveGk(); allNotes.forEach(function(note){ if(!note.isPublic()) { - if(!note.isEncrypted()) { - // needs encryption - this.encryptSingleNote(note, key); + if(note.encryptionEnabled() && !note.isEncrypted()) { notesNeedingUpdate.push(note); } } else { if(note.isEncrypted()) { - // needs decrypting - this.decryptSingleNote(note, key); notesNeedingUpdate.push(note); } } }.bind(this)) if(notesNeedingUpdate.length > 0) { - this.saveBatchNotes(user, notesNeedingUpdate, true, callback) + console.log("verifying encryption, notes need updating", notesNeedingUpdate); + this.saveBatchNotes(user, notesNeedingUpdate, callback) } } @@ -210,41 +206,31 @@ angular.module('app.services') } this.shareGroup = function(user, group, callback) { - var shareFn = function() { Restangular.one("users", user.id).one("groups", group.id).one("presentations").post() .then(function(response){ var presentation = response.plain(); - group.notes.forEach(function(note){ - note.shared_via_group = true; - }); _.merge(group, {presentation: presentation}); callback(presentation); - }) - } - 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(); - }) - } else { - shareFn(); - } + if(group.notes.length > 0) { + // decrypt notes + this.saveBatchNotes(user, group.notes, function(success){}) + } + }.bind(this)) } this.unshareGroup = function(user, group, callback) { var request = Restangular.one("users", user.id).one("groups", group.id).one("presentations", group.presentation.id); - request.enabled = false; - request.patch().then(function(response){ - var presentation = response.plain(); - _.merge(group, {presentation: presentation}); - callback(presentation); - }) + request.remove().then(function(response){ + group.presentation = null; + callback(null); + + if(group.notes.length > 0) { + // encrypt notes + var notes = group.notes; + this.saveBatchNotes(user, notes, function(success){}) + } + }.bind(this)) } @@ -255,7 +241,7 @@ angular.module('app.services') Notes */ - this.saveBatchNotes = function(user, notes, encryptionEnabled, callback) { + this.saveBatchNotes = function(user, notes, callback) { var request = Restangular.one("users", user.id).one("notes/batch_update"); request.notes = _.map(notes, function(note){ return this.createRequestParamsFromNote(note, user); @@ -302,6 +288,7 @@ angular.module('app.services') else { // decrypted params.content = JSON.stringify(note.content); + params.loc_eek = null; } return params; } @@ -351,15 +338,29 @@ angular.module('app.services') this.unshareNote = function(user, note, callback) { var request = Restangular.one("users", user.id).one("notes", note.id).one("presentations", note.presentation.id); - request.enabled = false; - request.patch().then(function(response){ - var presentation = response.plain(); - _.merge(note, {presentation: presentation}); - callback(note); + request.remove().then(function(response){ + note.presentation = null; + callback(null); }) } + /* + Presentations + */ + + this.updatePresentation = function(resource, presentation, callback) { + var request = Restangular.one("users", user.id) + .one(resource.constructor.name.toLowerCase() + "s", resource.id) + .one("presentations", resource.presentation.id); + _.merge(request, presentation); + request.patch().then(function(response){ + callback(response.plain()); + }) + .catch(function(error){ + callback(nil); + }) + } /* @@ -368,32 +369,26 @@ angular.module('app.services') this.importJSONData = function(jsonString, callback) { var data = JSON.parse(jsonString); + var user = new User(data); 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); + user.notes.forEach(function(note) { + if(note.isPublic()) { + note.setContentRaw(JSON.stringify(note.content)); } else { - // private this.encryptSingleNoteWithLocalKey(note); } + + // prevent circular links + note.group = null; }.bind(this)) + user.groups.forEach(function(group){ + // prevent circular links + group.notes = null; + }) var request = Restangular.one("import"); - request.data = data; - console.log("posting import request", request); + request.data = {notes: user.notes, groups: user.groups}; request.post().then(function(response){ callback(true, response); }) @@ -423,9 +418,24 @@ angular.module('app.services') return textFile; }.bind(this); + var presentationParams = function(presentation) { + if(!presentation) { + return null; + } + + return { + id: presentation.id, + uuid: presentation.uuid, + root_path: presentation.root_path, + relative_path: presentation.relative_path, + presentable_type: presentation.presentable_type, + presentable_id: presentation.presentable_id, + created_at: presentation.created_at, + modified_at: presentation.modified_at, + } + } var notes = _.map(user.filteredNotes(), function(note){ - console.log("mapping note", note); return { id: note.id, uuid: note.uuid, @@ -433,6 +443,7 @@ angular.module('app.services') group_id: note.group_id, created_at: note.created_at, modified_at: note.modified_at, + presentation: presentationParams(note.presentation) } }); @@ -443,36 +454,13 @@ angular.module('app.services') 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, + presentation: presentationParams(group.presentation) } }); var data = { notes: notes, - groups: groups, - presentations: presentations + groups: groups } return makeTextFile(JSON.stringify(data, null, 2 /* pretty print */)); @@ -505,18 +493,13 @@ angular.module('app.services') - this.filterDummyNotes = function(notes) { - var filtered = notes.filter(function(note){return note.dummy == false || note.dummy == null}); - return filtered; - } - this.staticifyObject = function(object) { return JSON.parse(JSON.stringify(object)); } this.writeUserToLocalStorage = function(user) { var saveUser = _.cloneDeep(user); - saveUser.notes = this.filterDummyNotes(saveUser.notes); + saveUser.notes = Note.filterDummyNotes(saveUser.notes); saveUser.groups.forEach(function(group){ group.notes = null; }.bind(this)) @@ -645,7 +628,7 @@ angular.module('app.services') } }); - this.saveBatchNotes(user, notes, true, function(success) { + this.saveBatchNotes(user, notes, function(success) { callback(success); }.bind(this)); } diff --git a/app/assets/templates/frontend/editor.html.haml b/app/assets/templates/frontend/editor.html.haml index bb0a2b63e..f3e1c0acc 100644 --- a/app/assets/templates/frontend/editor.html.haml +++ b/app/assets/templates/frontend/editor.html.haml @@ -26,11 +26,11 @@ %li %a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.toggleMarkdown()"} Toggle Markdown Preview .shortcut Cmd + M - %li{"ng-if" => "!ctrl.note.hasEnabledPresentation()"} + %li{"ng-if" => "!ctrl.note.presentation"} %a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.shareNote()"} Share - %li{"ng-if" => "ctrl.note.hasEnabledPresentation()"} + %li{"ng-if" => "ctrl.note.presentation"} %a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.editUrlPressed()"} Edit URL - %li{"ng-if" => "ctrl.note.hasEnabledPresentation()"} + %li{"ng-if" => "ctrl.note.presentation"} %a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.unshareNote()"} Unshare %li %a.text{"ng-click" => "ctrl.deleteNote()"} Delete @@ -40,7 +40,7 @@ .panel-body{"style" => "text-align: center; color: black;"} This editor is Markdown enabled. .menu-right-container - .public-link{"ng-if" => "ctrl.note.hasEnabledPresentation()"} + .public-link{"ng-if" => "ctrl.note.presentation"} %a.url{"ng-if" => "!ctrl.editingUrl", "href" => "{{ctrl.publicUrlForNote(ctrl.note)}}", "target" => "_blank"} %span.icon-rss.icon {{ctrl.publicUrlForNote(note)}} diff --git a/app/assets/templates/frontend/groups.html.haml b/app/assets/templates/frontend/groups.html.haml index f5f7cc198..5a2917358 100644 --- a/app/assets/templates/frontend/groups.html.haml +++ b/app/assets/templates/frontend/groups.html.haml @@ -9,7 +9,7 @@ .count {{ctrl.noteCount(ctrl.allGroup)}} .group{"ng-repeat" => "group in ctrl.groups", "ng-click" => "ctrl.selectGroup(group)", "ng-class" => "{'selected' : ctrl.selectedGroup == group}", "droppable" => true, "drop" => "ctrl.handleDrop", "group" => "group"} - .icon.icon-rss{"ng-if" => "group.presentation.enabled"} + .icon.icon-rss{"ng-if" => "group.presentation"} %input.title{"ng-disabled" => "group != ctrl.selectedGroup", "ng-model" => "group.name", "ng-keyup" => "$event.keyCode == 13 && ctrl.saveGroup($event, group)", "mb-autofocus" => "true", "should-focus" => "ctrl.newGroup", "ng-change" => "ctrl.groupTitleDidChange(group)", "ng-focus" => "ctrl.onGroupTitleFocus(group)"} diff --git a/app/assets/templates/frontend/notes.html.haml b/app/assets/templates/frontend/notes.html.haml index df45140d9..0315b646a 100644 --- a/app/assets/templates/frontend/notes.html.haml +++ b/app/assets/templates/frontend/notes.html.haml @@ -14,14 +14,14 @@ %span.caret %span.sr-only %ul.dropdown-menu.dropdown-menu-left.nt-dropdown-menu.dark{"ng-if" => "ctrl.showMenu"} - %li{"ng-if" => "!ctrl.group.presentation.enabled"} + %li{"ng-if" => "!ctrl.group.presentation"} %a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedGroupShare($event)"} Share Group - %li{"ng-if" => "ctrl.group.presentation.enabled"} + %li{"ng-if" => "ctrl.group.presentation"} %a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedGroupUnshare()"} Unshare Group %li{"ng-if" => "!ctrl.group.all"} %a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedGroupDelete()"} Delete Group .menu-right-container - .public-link{"ng-if" => "ctrl.group.presentation.enabled"} + .public-link{"ng-if" => "ctrl.group.presentation"} %a.url{"ng-if" => "!ctrl.editingUrl", "href" => "{{ctrl.publicUrlForGroup(ctrl.group)}}", "target" => "_blank"} %span.icon-rss.icon {{ctrl.publicUrlForGroup()}}