working functionality
This commit is contained in:
@@ -14,9 +14,10 @@ angular
|
|||||||
])
|
])
|
||||||
// Configure path to API
|
// Configure path to API
|
||||||
.config(function (RestangularProvider, apiControllerProvider) {
|
.config(function (RestangularProvider, apiControllerProvider) {
|
||||||
|
RestangularProvider.setDefaultHeaders({"Content-Type": "application/json"});
|
||||||
|
|
||||||
var url = apiControllerProvider.defaultServerURL();
|
var url = apiControllerProvider.defaultServerURL();
|
||||||
RestangularProvider.setBaseUrl(url);
|
RestangularProvider.setBaseUrl(url);
|
||||||
console.log(url);
|
|
||||||
|
|
||||||
RestangularProvider.setFullRequestInterceptor(function(element, operation, route, url, headers, params, httpConfig) {
|
RestangularProvider.setFullRequestInterceptor(function(element, operation, route, url, headers, params, httpConfig) {
|
||||||
var token = localStorage.getItem("jwt");
|
var token = localStorage.getItem("jwt");
|
||||||
@@ -32,4 +33,3 @@ angular
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ angular.module('app.frontend')
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.controller('EditorCtrl', function ($sce, $timeout, apiController, markdownRenderer, $rootScope) {
|
.controller('EditorCtrl', function ($sce, $timeout, apiController, modelManager, markdownRenderer, $rootScope) {
|
||||||
|
|
||||||
this.demoNotes = [
|
this.demoNotes = [
|
||||||
{title: "Live print a file with tail", content: "tail -f log/production.log"},
|
{title: "Live print a file with tail", content: "tail -f log/production.log"},
|
||||||
@@ -74,14 +74,14 @@ angular.module('app.frontend')
|
|||||||
{title: "NPM install without sudo", content: "sudo chown -R $(whoami) ~/.npm"},
|
{title: "NPM install without sudo", content: "sudo chown -R $(whoami) ~/.npm"},
|
||||||
{title: "Email validation regex", content: "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"},
|
{title: "Email validation regex", content: "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"},
|
||||||
{title: "Ruby generate 256 bit key", content: "Digest::SHA256.hexdigest(SecureRandom.random_bytes(32))"},
|
{title: "Ruby generate 256 bit key", content: "Digest::SHA256.hexdigest(SecureRandom.random_bytes(32))"},
|
||||||
{title: "Mac add user to user group", content: "sudo dseditgroup -o edit -a USERNAME -t user GROUPNAME"},
|
{title: "Mac add user to user tag", content: "sudo dsedittag -o edit -a USERNAME -t user GROUPNAME"},
|
||||||
{title: "Kill Mac OS System Apache", content: "sudo launchctl unload -w /System/Library/LaunchDaemons/org.apache.httpd.plist"},
|
{title: "Kill Mac OS System Apache", content: "sudo launchctl unload -w /System/Library/LaunchDaemons/org.apache.httpd.plist"},
|
||||||
{title: "Docker run with mount binding and port", content: "docker run -v /home/vagrant/www/app:/var/www/app -p 8080:80 -d kpi/s3"},
|
{title: "Docker run with mount binding and port", content: "docker run -v /home/vagrant/www/app:/var/www/app -p 8080:80 -d kpi/s3"},
|
||||||
{title: "MySQL grant privileges", content: "GRANT [type of permission] ON [database name].[table name] TO ‘[username]’@'%’;"},
|
{title: "MySQL grant privileges", content: "GRANT [type of permission] ON [database name].[table name] TO ‘[username]’@'%’;"},
|
||||||
{title: "MySQL list users", content: "SELECT User FROM mysql.user;"},
|
{title: "MySQL list users", content: "SELECT User FROM mysql.user;"},
|
||||||
];
|
];
|
||||||
|
|
||||||
this.showSampler = !this.user.id && this.user.filteredNotes().length == 0;
|
this.showSampler = !this.user.id && modelManager.filteredNotes.length == 0;
|
||||||
|
|
||||||
this.demoNoteNames = _.map(this.demoNotes, function(note){
|
this.demoNoteNames = _.map(this.demoNotes, function(note){
|
||||||
return note.title;
|
return note.title;
|
||||||
|
|||||||
@@ -1,104 +0,0 @@
|
|||||||
angular.module('app.frontend')
|
|
||||||
.directive("groupsSection", function(){
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
scope: {
|
|
||||||
addNew: "&",
|
|
||||||
selectionMade: "&",
|
|
||||||
willSelect: "&",
|
|
||||||
save: "&",
|
|
||||||
groups: "=",
|
|
||||||
allGroup: "=",
|
|
||||||
user: "=",
|
|
||||||
updateNoteGroup: "&"
|
|
||||||
},
|
|
||||||
templateUrl: 'frontend/groups.html',
|
|
||||||
replace: true,
|
|
||||||
controller: 'GroupsCtrl',
|
|
||||||
controllerAs: 'ctrl',
|
|
||||||
bindToController: true,
|
|
||||||
|
|
||||||
link:function(scope, elem, attrs, ctrl) {
|
|
||||||
scope.$watch('ctrl.groups', function(newGroups){
|
|
||||||
if(newGroups) {
|
|
||||||
ctrl.setGroups(newGroups);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.controller('GroupsCtrl', function () {
|
|
||||||
|
|
||||||
var initialLoad = true;
|
|
||||||
|
|
||||||
this.setGroups = function(groups) {
|
|
||||||
if(initialLoad) {
|
|
||||||
initialLoad = false;
|
|
||||||
this.selectGroup(this.allGroup);
|
|
||||||
} else {
|
|
||||||
if(groups && groups.length > 0) {
|
|
||||||
this.selectGroup(groups[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selectGroup = function(group) {
|
|
||||||
this.willSelect()(group);
|
|
||||||
this.selectedGroup = group;
|
|
||||||
this.selectionMade()(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.clickedAddNewGroup = function() {
|
|
||||||
if(this.editingGroup) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.newGroup = new Group({notes : []});
|
|
||||||
if(!this.user.uuid) {
|
|
||||||
this.newGroup.uuid = Neeto.crypto.generateRandomKey()
|
|
||||||
}
|
|
||||||
this.selectedGroup = this.newGroup;
|
|
||||||
this.editingGroup = this.newGroup;
|
|
||||||
this.addNew()(this.newGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
var originalGroupName = "";
|
|
||||||
this.onGroupTitleFocus = function(group) {
|
|
||||||
originalGroupName = group.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.groupTitleDidChange = function(group) {
|
|
||||||
this.editingGroup = group;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.saveGroup = function($event, group) {
|
|
||||||
this.editingGroup = null;
|
|
||||||
if(group.name.length == 0) {
|
|
||||||
group.name = originalGroupName;
|
|
||||||
originalGroupName = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$event.target.blur();
|
|
||||||
if(!group.name || group.name.length == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.save()(group, function(savedGroup){
|
|
||||||
_.merge(group, savedGroup);
|
|
||||||
this.selectGroup(group);
|
|
||||||
this.newGroup = null;
|
|
||||||
}.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.noteCount = function(group) {
|
|
||||||
var validNotes = Note.filterDummyNotes(group.notes);
|
|
||||||
return validNotes.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.handleDrop = function(e, newGroup, note) {
|
|
||||||
this.updateNoteGroup()(note, newGroup, this.selectedGroup);
|
|
||||||
}.bind(this)
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
@@ -17,7 +17,7 @@ angular.module('app.frontend')
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.controller('HeaderCtrl', function ($auth, $state, apiController, serverSideValidation, $timeout) {
|
.controller('HeaderCtrl', function ($auth, $state, apiController, modelManager, serverSideValidation, $timeout) {
|
||||||
|
|
||||||
this.changePasswordPressed = function() {
|
this.changePasswordPressed = function() {
|
||||||
this.showNewPasswordForm = !this.showNewPasswordForm;
|
this.showNewPasswordForm = !this.showNewPasswordForm;
|
||||||
@@ -58,12 +58,12 @@ angular.module('app.frontend')
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.hasLocalData = function() {
|
this.hasLocalData = function() {
|
||||||
return this.user.filteredNotes().length > 0;
|
return modelManager.filteredNotes.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mergeLocalChanged = function() {
|
this.mergeLocalChanged = function() {
|
||||||
if(!this.user.shouldMerge) {
|
if(!this.user.shouldMerge) {
|
||||||
if(!confirm("Unchecking this option means any locally stored groups and notes you have now will be deleted. Are you sure you want to continue?")) {
|
if(!confirm("Unchecking this option means any locally stored tags and notes you have now will be deleted. Are you sure you want to continue?")) {
|
||||||
this.user.shouldMerge = true;
|
this.user.shouldMerge = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,7 +109,7 @@ angular.module('app.frontend')
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.encryptionStatusForNotes = function() {
|
this.encryptionStatusForNotes = function() {
|
||||||
var allNotes = this.user.filteredNotes();
|
var allNotes = modelManager.filteredNotes;
|
||||||
var countEncrypted = 0;
|
var countEncrypted = 0;
|
||||||
allNotes.forEach(function(note){
|
allNotes.forEach(function(note){
|
||||||
if(note.encryptionEnabled()) {
|
if(note.encryptionEnabled()) {
|
||||||
|
|||||||
@@ -5,19 +5,19 @@ angular.module('app.frontend')
|
|||||||
$rootScope.description = "A secure code box for developers to store common commands and useful notes.";
|
$rootScope.description = "A secure code box for developers to store common commands and useful notes.";
|
||||||
|
|
||||||
var onUserSet = function() {
|
var onUserSet = function() {
|
||||||
|
apiController.setUser($scope.defaultUser);
|
||||||
|
$scope.allTag = new Tag({all: true});
|
||||||
|
$scope.allTag.content.name = "All";
|
||||||
|
$scope.tags = modelManager.tags;
|
||||||
|
|
||||||
$scope.allGroup = new Group({name: "All", all: true});
|
// apiController.verifyEncryptionStatusOfAllItems($scope.defaultUser, function(success){});
|
||||||
$scope.groups = modelManager.groups;
|
|
||||||
|
|
||||||
apiController.verifyEncryptionStatusOfAllItems($scope.defaultUser, function(success){
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apiController.getCurrentUser(function(response){
|
apiController.getCurrentUser(function(response){
|
||||||
if(response && !response.errors) {
|
if(response && !response.errors) {
|
||||||
|
console.log("Get user response", response);
|
||||||
$scope.defaultUser = new User(response);
|
$scope.defaultUser = new User(response);
|
||||||
modelManager.items = response.items;
|
modelManager.items = _.map(response.items, function(json_obj){return new Item(json_obj)});
|
||||||
$rootScope.title = "Notes — Neeto";
|
$rootScope.title = "Notes — Neeto";
|
||||||
onUserSet();
|
onUserSet();
|
||||||
} else {
|
} else {
|
||||||
@@ -27,44 +27,44 @@ angular.module('app.frontend')
|
|||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Groups Ctrl Callbacks
|
Tags Ctrl Callbacks
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$scope.updateAllGroup = function() {
|
$scope.updateAllTag = function() {
|
||||||
$scope.allGroup.notes = modelManager.filteredNotes;
|
$scope.allTag.notes = modelManager.filteredNotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.groupsWillMakeSelection = function(group) {
|
$scope.tagsWillMakeSelection = function(tag) {
|
||||||
if(group.all) {
|
if(tag.all) {
|
||||||
$scope.updateAllGroup();
|
$scope.updateAllTag();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.groupsSelectionMade = function(group) {
|
$scope.tagsSelectionMade = function(tag) {
|
||||||
if(!group.notes) {
|
if(!tag.notes) {
|
||||||
group.notes = [];
|
tag.notes = [];
|
||||||
}
|
}
|
||||||
$scope.selectedGroup = group;
|
$scope.selectedTag = tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.groupsAddNew = function(group) {
|
$scope.tagsAddNew = function(tag) {
|
||||||
modelManager.addTag(group);
|
modelManager.addTag(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.groupsSave = function(group, callback) {
|
$scope.tagsSave = function(tag, callback) {
|
||||||
apiController.saveItems([group], callback);
|
apiController.saveItems([tag], callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Called to update the group of a note after drag and drop change
|
Called to update the tag of a note after drag and drop change
|
||||||
The note object is a copy of the original
|
The note object is a copy of the original
|
||||||
*/
|
*/
|
||||||
$scope.groupsUpdateNoteGroup = function(noteCopy, newGroup, oldGroup) {
|
$scope.tagsUpdateNoteTag = function(noteCopy, newTag, oldTag) {
|
||||||
|
|
||||||
var originalNote = _.find($scope.defaultUser.notes, {uuid: noteCopy.uuid});
|
var originalNote = _.find($scope.defaultUser.notes, {uuid: noteCopy.uuid});
|
||||||
modelManager.removeTagFromNote(oldGroup, originalNote);
|
modelManager.removeTagFromNote(oldTag, originalNote);
|
||||||
if(!newGroup.all) {
|
if(!newTag.all) {
|
||||||
modelManager.addTagToNote(newGroup, originalNote);
|
modelManager.addTagToNote(newTag, originalNote);
|
||||||
}
|
}
|
||||||
|
|
||||||
apiController.saveDirtyItems(function(){});
|
apiController.saveDirtyItems(function(){});
|
||||||
@@ -74,19 +74,19 @@ angular.module('app.frontend')
|
|||||||
Notes Ctrl Callbacks
|
Notes Ctrl Callbacks
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$scope.notesRemoveGroup = function(group) {
|
$scope.notesRemoveTag = function(tag) {
|
||||||
var validNotes = Note.filterDummyNotes(group.notes);
|
var validNotes = Note.filterDummyNotes(tag.notes);
|
||||||
if(validNotes == 0) {
|
if(validNotes == 0) {
|
||||||
// if no more notes, delete group
|
// if no more notes, delete tag
|
||||||
apiController.deleteItem($scope.defaultUser, group, function(){
|
apiController.deleteItem($scope.defaultUser, tag, function(){
|
||||||
// force scope groups to update on sub directives
|
// force scope tags to update on sub directives
|
||||||
$scope.groups = [];
|
$scope.tags = [];
|
||||||
$timeout(function(){
|
$timeout(function(){
|
||||||
$scope.groups = modelManager.groups;
|
$scope.tags = modelManager.tags;
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
alert("To delete this group, remove all its notes first.");
|
alert("To delete this tag, remove all its notes first.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,15 +95,13 @@ angular.module('app.frontend')
|
|||||||
}
|
}
|
||||||
|
|
||||||
$scope.notesAddNew = function(note) {
|
$scope.notesAddNew = function(note) {
|
||||||
if(!$scope.defaultUser.id) {
|
|
||||||
// generate local id for note
|
|
||||||
note.id = Neeto.crypto.generateRandomKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
modelManager.addNote(note);
|
modelManager.addNote(note);
|
||||||
|
|
||||||
if(!$scope.selectedGroup.all) {
|
if(!$scope.selectedTag.all) {
|
||||||
modelManager.addTagToNote($scope.selectedGroup, note);
|
console.log("add tag");
|
||||||
|
modelManager.addTagToNote($scope.selectedTag, note);
|
||||||
|
} else {
|
||||||
|
$scope.selectedTag.notes.unshift(note);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +110,9 @@ angular.module('app.frontend')
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
$scope.saveNote = function(note, callback) {
|
$scope.saveNote = function(note, callback) {
|
||||||
apiController.saveItems([note], function(){
|
modelManager.addDirtyItems(note);
|
||||||
|
|
||||||
|
apiController.saveDirtyItems(function(){
|
||||||
modelManager.addNote(note);
|
modelManager.addNote(note);
|
||||||
note.hasChanges = false;
|
note.hasChanges = false;
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ angular.module('app.frontend')
|
|||||||
|
|
||||||
$scope.headerLogout = function() {
|
$scope.headerLogout = function() {
|
||||||
$scope.defaultUser = apiController.localUser();
|
$scope.defaultUser = apiController.localUser();
|
||||||
$scope.groups = $scope.defaultUser.groups;
|
$scope.tags = $scope.defaultUser.tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ angular.module('app.frontend')
|
|||||||
addNew: "&",
|
addNew: "&",
|
||||||
selectionMade: "&",
|
selectionMade: "&",
|
||||||
remove: "&",
|
remove: "&",
|
||||||
group: "=",
|
tag: "=",
|
||||||
user: "=",
|
user: "=",
|
||||||
removeGroup: "&"
|
removeTag: "&"
|
||||||
},
|
},
|
||||||
|
|
||||||
templateUrl: 'frontend/notes.html',
|
templateUrl: 'frontend/notes.html',
|
||||||
replace: true,
|
replace: true,
|
||||||
controller: 'NotesCtrl',
|
controller: 'NotesCtrl',
|
||||||
@@ -16,15 +17,15 @@ angular.module('app.frontend')
|
|||||||
bindToController: true,
|
bindToController: true,
|
||||||
|
|
||||||
link:function(scope, elem, attrs, ctrl) {
|
link:function(scope, elem, attrs, ctrl) {
|
||||||
scope.$watch('ctrl.group', function(group, oldGroup){
|
scope.$watch('ctrl.tag', function(tag, oldTag){
|
||||||
if(group) {
|
if(tag) {
|
||||||
ctrl.groupDidChange(group, oldGroup);
|
ctrl.tagDidChange(tag, oldTag);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.controller('NotesCtrl', function (apiController, $timeout, ngDialog, $rootScope) {
|
.controller('NotesCtrl', function (apiController, modelManager, $timeout, ngDialog, $rootScope) {
|
||||||
|
|
||||||
$rootScope.$on("editorFocused", function(){
|
$rootScope.$on("editorFocused", function(){
|
||||||
this.showMenu = false;
|
this.showMenu = false;
|
||||||
@@ -32,15 +33,15 @@ angular.module('app.frontend')
|
|||||||
|
|
||||||
var isFirstLoad = true;
|
var isFirstLoad = true;
|
||||||
|
|
||||||
this.groupDidChange = function(group, oldGroup) {
|
this.tagDidChange = function(tag, oldTag) {
|
||||||
this.showMenu = false;
|
this.showMenu = false;
|
||||||
|
|
||||||
if(this.selectedNote && this.selectedNote.dummy) {
|
if(this.selectedNote && this.selectedNote.dummy) {
|
||||||
_.remove(oldGroup.notes, this.selectedNote);
|
_.remove(oldTag.notes, this.selectedNote);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.noteFilter.text = "";
|
this.noteFilter.text = "";
|
||||||
this.setNotes(group.notes, false);
|
this.setNotes(tag.notes, false);
|
||||||
|
|
||||||
if(isFirstLoad) {
|
if(isFirstLoad) {
|
||||||
$timeout(function(){
|
$timeout(function(){
|
||||||
@@ -53,31 +54,31 @@ angular.module('app.frontend')
|
|||||||
isFirstLoad = false;
|
isFirstLoad = false;
|
||||||
}
|
}
|
||||||
}.bind(this))
|
}.bind(this))
|
||||||
} else if(group.notes.length == 0) {
|
} else if(tag.notes.length == 0) {
|
||||||
this.createNewNote();
|
this.createNewNote();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selectedGroupDelete = function() {
|
this.selectedTagDelete = function() {
|
||||||
this.showMenu = false;
|
this.showMenu = false;
|
||||||
this.removeGroup()(this.group);
|
this.removeTag()(this.tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selectedGroupShare = function() {
|
this.selectedTagShare = function() {
|
||||||
this.showMenu = false;
|
this.showMenu = false;
|
||||||
|
|
||||||
if(!this.user.id) {
|
if(!this.user.id) {
|
||||||
alert("You must be signed in to share a group.");
|
alert("You must be signed in to share a tag.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.group.all) {
|
if(this.tag.all) {
|
||||||
alert("You cannot share the 'All' group.");
|
alert("You cannot share the 'All' tag.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var callback = function(username) {
|
var callback = function(username) {
|
||||||
apiController.shareItem(this.user, this.group, function(response){
|
apiController.shareItem(this.user, this.tag, function(response){
|
||||||
})
|
})
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
|
|
||||||
@@ -97,23 +98,23 @@ angular.module('app.frontend')
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selectedGroupUnshare = function() {
|
this.selectedTagUnshare = function() {
|
||||||
this.showMenu = false;
|
this.showMenu = false;
|
||||||
apiController.unshareItem(this.user, this.group, function(response){
|
apiController.unshareItem(this.user, this.tag, function(response){
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.publicUrlForGroup = function() {
|
this.publicUrlForTag = function() {
|
||||||
return this.group.presentation.url;
|
return this.tag.presentation.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setNotes = function(notes, createNew) {
|
this.setNotes = function(notes, createNew) {
|
||||||
this.notes = notes;
|
this.notes = notes;
|
||||||
|
console.log("set notes", notes);
|
||||||
notes.forEach(function(note){
|
notes.forEach(function(note){
|
||||||
note.visible = true;
|
note.visible = true;
|
||||||
})
|
})
|
||||||
apiController.decryptNotesWithLocalKey(notes);
|
|
||||||
this.selectFirstNote(createNew);
|
this.selectFirstNote(createNew);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +139,9 @@ angular.module('app.frontend')
|
|||||||
var title = "New Note" + (this.notes ? (" " + (this.notes.length + 1)) : "");
|
var title = "New Note" + (this.notes ? (" " + (this.notes.length + 1)) : "");
|
||||||
this.newNote = new Note({dummy: true});
|
this.newNote = new Note({dummy: true});
|
||||||
this.newNote.content.title = title;
|
this.newNote.content.title = title;
|
||||||
modelManager.addTagToNote(this.group, this.newNote);
|
if(this.tag && !this.tag.all) {
|
||||||
|
modelManager.addTagToNote(this.tag, this.newNote);
|
||||||
|
}
|
||||||
this.selectNote(this.newNote);
|
this.selectNote(this.newNote);
|
||||||
this.addNew()(this.newNote);
|
this.addNew()(this.newNote);
|
||||||
}
|
}
|
||||||
|
|||||||
114
app/assets/javascripts/app/frontend/controllers/tags.js
Normal file
114
app/assets/javascripts/app/frontend/controllers/tags.js
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
angular.module('app.frontend')
|
||||||
|
.directive("tagsSection", function(){
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
scope: {
|
||||||
|
addNew: "&",
|
||||||
|
selectionMade: "&",
|
||||||
|
willSelect: "&",
|
||||||
|
save: "&",
|
||||||
|
tags: "=",
|
||||||
|
allTag: "=",
|
||||||
|
user: "=",
|
||||||
|
updateNoteTag: "&"
|
||||||
|
},
|
||||||
|
templateUrl: 'frontend/tags.html',
|
||||||
|
replace: true,
|
||||||
|
controller: 'TagsCtrl',
|
||||||
|
controllerAs: 'ctrl',
|
||||||
|
bindToController: true,
|
||||||
|
|
||||||
|
link:function(scope, elem, attrs, ctrl) {
|
||||||
|
scope.$watch('ctrl.tags', function(newTags){
|
||||||
|
if(newTags) {
|
||||||
|
ctrl.setTags(newTags);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.$watch('ctrl.allTag', function(allTag){
|
||||||
|
if(allTag) {
|
||||||
|
ctrl.setAllTag(allTag);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.controller('TagsCtrl', function () {
|
||||||
|
|
||||||
|
var initialLoad = true;
|
||||||
|
|
||||||
|
this.setAllTag = function(allTag) {
|
||||||
|
this.selectTag(this.allTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setTags = function(tags) {
|
||||||
|
if(initialLoad) {
|
||||||
|
initialLoad = false;
|
||||||
|
this.selectTag(this.allTag);
|
||||||
|
} else {
|
||||||
|
if(tags && tags.length > 0) {
|
||||||
|
this.selectTag(tags[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectTag = function(tag) {
|
||||||
|
this.willSelect()(tag);
|
||||||
|
this.selectedTag = tag;
|
||||||
|
this.selectionMade()(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clickedAddNewTag = function() {
|
||||||
|
if(this.editingTag) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.newTag = new Tag({notes : []});
|
||||||
|
if(!this.user.uuid) {
|
||||||
|
this.newTag.uuid = Neeto.crypto.generateRandomKey()
|
||||||
|
}
|
||||||
|
this.selectedTag = this.newTag;
|
||||||
|
this.editingTag = this.newTag;
|
||||||
|
this.addNew()(this.newTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
var originalTagName = "";
|
||||||
|
this.onTagTitleFocus = function(tag) {
|
||||||
|
originalTagName = tag.content.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tagTitleDidChange = function(tag) {
|
||||||
|
this.editingTag = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveTag = function($event, tag) {
|
||||||
|
this.editingTag = null;
|
||||||
|
if(tag.content.name.length == 0) {
|
||||||
|
tag.content.name = originalTagName;
|
||||||
|
originalTagName = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$event.target.blur();
|
||||||
|
if(!tag.content.name || tag.content.name.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.save()(tag, function(savedTag){
|
||||||
|
_.merge(tag, savedTag);
|
||||||
|
this.selectTag(tag);
|
||||||
|
this.newTag = null;
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.noteCount = function(tag) {
|
||||||
|
var validNotes = Note.filterDummyNotes(tag.notes);
|
||||||
|
return validNotes.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.handleDrop = function(e, newTag, note) {
|
||||||
|
this.updateNoteTag()(note, newTag, this.selectedTag);
|
||||||
|
}.bind(this)
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
class Item {
|
class Item {
|
||||||
|
|
||||||
constructor(json_obj) {
|
constructor(json_obj) {
|
||||||
|
|
||||||
var content;
|
var content;
|
||||||
|
|
||||||
Object.defineProperty(this, "content", {
|
Object.defineProperty(this, "content", {
|
||||||
@@ -12,14 +12,13 @@ class Item {
|
|||||||
|
|
||||||
if(typeof value === 'string') {
|
if(typeof value === 'string') {
|
||||||
try {
|
try {
|
||||||
decodedValue = JSON.parse(value);
|
var decodedValue = JSON.parse(value);
|
||||||
finalValue = decodedValue;
|
finalValue = decodedValue;
|
||||||
}
|
}
|
||||||
catch(e) {
|
catch(e) {
|
||||||
finalValue = value;
|
finalValue = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content = finalValue;
|
content = finalValue;
|
||||||
},
|
},
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
@@ -27,14 +26,35 @@ class Item {
|
|||||||
|
|
||||||
_.merge(this, json_obj);
|
_.merge(this, json_obj);
|
||||||
|
|
||||||
|
if(!this.uuid) {
|
||||||
|
this.uuid = Neeto.crypto.generateUUID();
|
||||||
|
}
|
||||||
|
|
||||||
this.setContentRaw = function(rawContent) {
|
this.setContentRaw = function(rawContent) {
|
||||||
content = rawContent;
|
content = rawContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!this.content) {
|
||||||
|
this.content = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!this.content.references) {
|
||||||
|
this.content.references = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addReference(reference) {
|
||||||
|
this.content.references.push(reference);
|
||||||
|
this.content.references = _.uniq(this.content.references);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeReference(reference) {
|
||||||
|
_.remove(this.content.references, _.find(this.content.references, {uuid: reference.uuid}));
|
||||||
}
|
}
|
||||||
|
|
||||||
referencesMatchingContentType(contentType) {
|
referencesMatchingContentType(contentType) {
|
||||||
return this.references.filter(function(reference){
|
return this.content.references.filter(function(reference){
|
||||||
return reference.content_type == content_type;
|
return reference.content_type == contentType;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +62,7 @@ class Item {
|
|||||||
// should be overriden to manage local properties
|
// should be overriden to manage local properties
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns true if note is shared individually or via group */
|
/* Returns true if note is shared individually or via tag */
|
||||||
isPublic() {
|
isPublic() {
|
||||||
return this.presentation;
|
return this.presentation;
|
||||||
}
|
}
|
||||||
@@ -58,4 +78,5 @@ class Item {
|
|||||||
presentationURL() {
|
presentationURL() {
|
||||||
return this.presentation.url;
|
return this.presentation.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,37 @@
|
|||||||
class Note extends Item {
|
class Note extends Item {
|
||||||
|
|
||||||
constructor(json_obj) {
|
constructor(json_obj) {
|
||||||
|
|
||||||
super(json_obj);
|
super(json_obj);
|
||||||
|
|
||||||
if(!this.content) {
|
if(!this.tags) {
|
||||||
this.content = {title: "", text: ""};
|
this.tags = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!this.content.title) {
|
||||||
|
this.content.title = "";
|
||||||
|
this.content.text = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
filterDummyNotes(notes) {
|
static filterDummyNotes(notes) {
|
||||||
var filtered = notes.filter(function(note){return note.dummy == false || note.dummy == null});
|
var filtered = notes.filter(function(note){return note.dummy == false || note.dummy == null});
|
||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasOnePublicGroup() {
|
get hasOnePublicTag() {
|
||||||
var hasPublicGroup = false;
|
var hasPublicTag = false;
|
||||||
this.groups.forEach(function(group){
|
this.tags.forEach(function(tag){
|
||||||
if(group.isPublic()) {
|
if(tag.isPublic()) {
|
||||||
hasPublicGroup = true;
|
hasPublicTag = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return hasPublicGroup;
|
return hasPublicTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
isPublic() {
|
isPublic() {
|
||||||
return super.isPublic() || this.hasOnePublicGroup;
|
return super.isPublic() || this.hasOnePublicTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
get content_type() {
|
get content_type() {
|
||||||
|
|||||||
@@ -2,9 +2,23 @@ class Tag extends Item {
|
|||||||
|
|
||||||
constructor(json_obj) {
|
constructor(json_obj) {
|
||||||
super(json_obj);
|
super(json_obj);
|
||||||
|
|
||||||
|
if(!this.notes) {
|
||||||
|
this.notes = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!this.content.name) {
|
||||||
|
this.content.name = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get content_type() {
|
get content_type() {
|
||||||
return "Tag";
|
return "Tag";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateReferencesLocalMapping() {
|
||||||
|
super.updateReferencesLocalMapping();
|
||||||
|
this.notes = this.referencesMatchingContentType("Note");
|
||||||
|
console.log("notes after maping", this.notes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ angular.module('app.frontend')
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
.state('group', {
|
.state('tag', {
|
||||||
url: '/:root_path/:secondary_path',
|
url: '/:root_path/:secondary_path',
|
||||||
parent: 'base',
|
parent: 'base',
|
||||||
views: {
|
views: {
|
||||||
|
|||||||
@@ -20,11 +20,15 @@ angular.module('app.frontend')
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.$get = function(Restangular) {
|
this.$get = function(Restangular, modelManager) {
|
||||||
return new ApiController(Restangular);
|
return new ApiController(Restangular, modelManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ApiController(Restangular) {
|
function ApiController(Restangular, modelManager) {
|
||||||
|
|
||||||
|
this.setUser = function(user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Config
|
Config
|
||||||
@@ -59,7 +63,13 @@ angular.module('app.frontend')
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Restangular.one("users/current").get().then(function(response){
|
Restangular.one("users/current").get().then(function(response){
|
||||||
callback(response.plain());
|
var plain = response.plain();
|
||||||
|
var items = plain.items;
|
||||||
|
this.decryptItemsWithLocalKey(items);
|
||||||
|
callback(plain);
|
||||||
|
}.bind(this))
|
||||||
|
.catch(function(error){
|
||||||
|
callback(null);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +110,7 @@ angular.module('app.frontend')
|
|||||||
if(response && !response.errors) {
|
if(response && !response.errors) {
|
||||||
// this.showNewPasswordForm = false;
|
// this.showNewPasswordForm = false;
|
||||||
// reencrypt data with new gk
|
// reencrypt data with new gk
|
||||||
this.reencryptAllNotesAndSave(user, new_keys.gk, current_keys.gk, function(success){
|
this.reencryptAllItemsAndSave(user, new_keys.gk, current_keys.gk, function(success){
|
||||||
if(success) {
|
if(success) {
|
||||||
this.setGk(new_keys.gk);
|
this.setGk(new_keys.gk);
|
||||||
alert("Your password has been changed and your data re-encrypted.");
|
alert("Your password has been changed and your data re-encrypted.");
|
||||||
@@ -141,27 +151,27 @@ angular.module('app.frontend')
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Ensures that if encryption is disabled, all local notes are uncrypted,
|
Ensures that if encryption is disabled, all local items are uncrypted,
|
||||||
and that if it's enabled, that all local notes are encrypted
|
and that if it's enabled, that all local items are encrypted
|
||||||
*/
|
*/
|
||||||
this.verifyEncryptionStatusOfAllItems = function(user, callback) {
|
this.verifyEncryptionStatusOfAllItems = function(user, callback) {
|
||||||
var allNotes = user.filteredNotes();
|
var allItems = user.filteredItems();
|
||||||
var notesNeedingUpdate = [];
|
var itemsNeedingUpdate = [];
|
||||||
allNotes.forEach(function(note){
|
allItems.forEach(function(item){
|
||||||
if(!note.isPublic()) {
|
if(!item.isPublic()) {
|
||||||
if(note.encryptionEnabled() && !note.isEncrypted()) {
|
if(item.encryptionEnabled() && !item.isEncrypted()) {
|
||||||
notesNeedingUpdate.push(note);
|
itemsNeedingUpdate.push(item);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(note.isEncrypted()) {
|
if(item.isEncrypted()) {
|
||||||
notesNeedingUpdate.push(note);
|
itemsNeedingUpdate.push(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.bind(this))
|
}.bind(this))
|
||||||
|
|
||||||
if(notesNeedingUpdate.length > 0) {
|
if(itemsNeedingUpdate.length > 0) {
|
||||||
console.log("verifying encryption, notes need updating", notesNeedingUpdate);
|
console.log("verifying encryption, items need updating", itemsNeedingUpdate);
|
||||||
this.saveBatchNotes(user, notesNeedingUpdate, callback)
|
this.saveBatchItems(user, itemsNeedingUpdate, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,31 +186,39 @@ angular.module('app.frontend')
|
|||||||
|
|
||||||
this.saveItems(dirtyItems, function(response){
|
this.saveItems(dirtyItems, function(response){
|
||||||
modelManager.clearDirtyItems();
|
modelManager.clearDirtyItems();
|
||||||
|
callback();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.saveItems = function(items, callback) {
|
this.saveItems = function(items, callback) {
|
||||||
var request = Restangular.one("users", user.uuid).one("items");
|
console.log("saving items", items);
|
||||||
|
var request = Restangular.one("users", this.user.uuid).one("items");
|
||||||
request.items = _.map(items, function(item){
|
request.items = _.map(items, function(item){
|
||||||
return this.createRequestParamsFromItem(item, user);
|
return this.createRequestParamsForItem(item);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
console.log("sending request items", request.items);
|
||||||
|
|
||||||
request.post().then(function(response) {
|
request.post().then(function(response) {
|
||||||
var savedItems = response.items;
|
var savedItems = response.items;
|
||||||
items.forEach(function(item){
|
console.log("response items", savedItems);
|
||||||
_.merge(item, _.find(savedItems, {uuid: item.uuid}));
|
// items.forEach(function(item) {
|
||||||
})
|
// _.merge(item, _.find(savedItems, {uuid: item.uuid}));
|
||||||
|
// })
|
||||||
callback(response);
|
callback(response);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.createRequestParamsForItem = function(item, user) {
|
this.createRequestParamsForItem = function(item) {
|
||||||
var params = {uuid: item.uuid};
|
var itemCopy = _.cloneDeep(item);
|
||||||
|
|
||||||
|
var params = {uuid: item.uuid, content_type: item.content_type};
|
||||||
|
itemCopy.content.references = _.map(itemCopy.content.references, function(reference){
|
||||||
|
return {uuid: reference.uuid, content_type: reference.content_type};
|
||||||
|
})
|
||||||
|
|
||||||
if(!item.isPublic()) {
|
if(!item.isPublic()) {
|
||||||
// encrypted
|
// encrypted
|
||||||
var itemCopy = _.cloneDeep(item);
|
this.encryptSingleItem(itemCopy, this.retrieveGk());
|
||||||
this.encryptSingleNote(itemCopy, this.retrieveGk());
|
|
||||||
params.content = itemCopy.content;
|
params.content = itemCopy.content;
|
||||||
params.loc_eek = itemCopy.loc_eek;
|
params.loc_eek = itemCopy.loc_eek;
|
||||||
}
|
}
|
||||||
@@ -213,23 +231,23 @@ angular.module('app.frontend')
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.deleteItem = function(user, item, callback) {
|
this.deleteItem = function(item, callback) {
|
||||||
if(!user.id) {
|
if(!this.user.id) {
|
||||||
this.writeUserToLocalStorage(user);
|
this.writeUserToLocalStorage(this.user);
|
||||||
callback(true);
|
callback(true);
|
||||||
} else {
|
} else {
|
||||||
Restangular.one("users", user.uuid).one("items", item.uuid).remove()
|
Restangular.one("users", this.user.uuid).one("items", item.uuid).remove()
|
||||||
.then(function(response) {
|
.then(function(response) {
|
||||||
callback(true);
|
callback(true);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.shareItem = function(user, item, callback) {
|
this.shareItem = function(item, callback) {
|
||||||
if(!user.id) {
|
if(!this.user.id) {
|
||||||
alert("You must be signed in to share.");
|
alert("You must be signed in to share.");
|
||||||
} else {
|
} else {
|
||||||
Restangular.one("users", user.uuid).one("items", item.uuid).one("presentations").post()
|
Restangular.one("users", this.user.uuid).one("items", item.uuid).one("presentations").post()
|
||||||
.then(function(response){
|
.then(function(response){
|
||||||
var presentation = response.plain();
|
var presentation = response.plain();
|
||||||
_.merge(item, {presentation: presentation});
|
_.merge(item, {presentation: presentation});
|
||||||
@@ -237,21 +255,21 @@ angular.module('app.frontend')
|
|||||||
|
|
||||||
// decrypt references
|
// decrypt references
|
||||||
if(item.references.length > 0) {
|
if(item.references.length > 0) {
|
||||||
this.saveBatchItems(user, item.references, function(success){})
|
this.saveBatchItems(item.references, function(success){})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.unshareItem = function(user, item, callback) {
|
this.unshareItem = function(item, callback) {
|
||||||
var request = Restangular.one("users", user.uuid).one("notes", item.uuid).one("presentations", item.presentation.uuid);
|
var request = Restangular.one("users", this.user.uuid).one("items", item.uuid).one("presentations", item.presentation.uuid);
|
||||||
request.remove().then(function(response){
|
request.remove().then(function(response){
|
||||||
item.presentation = null;
|
item.presentation = null;
|
||||||
callback(null);
|
callback(null);
|
||||||
|
|
||||||
// encrypt references
|
// encrypt references
|
||||||
if(item.references.length > 0) {
|
if(item.references.length > 0) {
|
||||||
this.saveBatchItems(user, item.references, function(success){})
|
this.saveBatchItems(item.references, function(success){})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -262,7 +280,7 @@ angular.module('app.frontend')
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
this.updatePresentation = function(resource, presentation, callback) {
|
this.updatePresentation = function(resource, presentation, callback) {
|
||||||
var request = Restangular.one("users", user.id)
|
var request = Restangular.one("users", this.user.id)
|
||||||
.one("items", resource.id)
|
.one("items", resource.id)
|
||||||
.one("presentations", resource.presentation.id);
|
.one("presentations", resource.presentation.id);
|
||||||
_.merge(request, presentation);
|
_.merge(request, presentation);
|
||||||
@@ -283,24 +301,24 @@ angular.module('app.frontend')
|
|||||||
var data = JSON.parse(jsonString);
|
var data = JSON.parse(jsonString);
|
||||||
var user = new User(data);
|
var user = new User(data);
|
||||||
console.log("importing data", JSON.parse(jsonString));
|
console.log("importing data", JSON.parse(jsonString));
|
||||||
user.notes.forEach(function(note) {
|
user.items.forEach(function(item) {
|
||||||
if(note.isPublic()) {
|
if(item.isPublic()) {
|
||||||
note.setContentRaw(JSON.stringify(note.content));
|
item.setContentRaw(JSON.stringify(item.content));
|
||||||
} else {
|
} else {
|
||||||
this.encryptSingleNoteWithLocalKey(note);
|
this.encryptSingleItemWithLocalKey(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// prevent circular links
|
// prevent circular links
|
||||||
note.group = null;
|
item.tag = null;
|
||||||
}.bind(this))
|
}.bind(this))
|
||||||
|
|
||||||
user.groups.forEach(function(group){
|
user.tags.forEach(function(tag){
|
||||||
// prevent circular links
|
// prevent circular links
|
||||||
group.notes = null;
|
tag.items = null;
|
||||||
})
|
})
|
||||||
|
|
||||||
var request = Restangular.one("import");
|
var request = Restangular.one("import");
|
||||||
request.data = {notes: user.notes, groups: user.groups};
|
request.data = {items: user.items, tags: user.tags};
|
||||||
request.post().then(function(response){
|
request.post().then(function(response){
|
||||||
callback(true, response);
|
callback(true, response);
|
||||||
})
|
})
|
||||||
@@ -313,7 +331,7 @@ angular.module('app.frontend')
|
|||||||
Export
|
Export
|
||||||
*/
|
*/
|
||||||
|
|
||||||
this.notesDataFile = function(user) {
|
this.itemsDataFile = function(user) {
|
||||||
var textFile = null;
|
var textFile = null;
|
||||||
var makeTextFile = function (text) {
|
var makeTextFile = function (text) {
|
||||||
var data = new Blob([text], {type: 'text/json'});
|
var data = new Blob([text], {type: 'text/json'});
|
||||||
@@ -347,32 +365,32 @@ angular.module('app.frontend')
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var notes = _.map(user.filteredNotes(), function(note){
|
var items = _.map(user.filteredItems(), function(item){
|
||||||
return {
|
return {
|
||||||
id: note.id,
|
id: item.id,
|
||||||
uuid: note.uuid,
|
uuid: item.uuid,
|
||||||
content: note.content,
|
content: item.content,
|
||||||
group_id: note.group_id,
|
tag_id: item.tag_id,
|
||||||
created_at: note.created_at,
|
created_at: item.created_at,
|
||||||
modified_at: note.modified_at,
|
modified_at: item.modified_at,
|
||||||
presentation: presentationParams(note.presentation)
|
presentation: presentationParams(item.presentation)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var groups = _.map(user.groups, function(group){
|
var tags = _.map(user.tags, function(tag){
|
||||||
return {
|
return {
|
||||||
id: group.id,
|
id: tag.id,
|
||||||
uuid: group.uuid,
|
uuid: tag.uuid,
|
||||||
name: group.name,
|
name: tag.name,
|
||||||
created_at: group.created_at,
|
created_at: tag.created_at,
|
||||||
modified_at: group.modified_at,
|
modified_at: tag.modified_at,
|
||||||
presentation: presentationParams(group.presentation)
|
presentation: presentationParams(tag.presentation)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
notes: notes,
|
items: items,
|
||||||
groups: groups
|
tags: tags
|
||||||
}
|
}
|
||||||
|
|
||||||
return makeTextFile(JSON.stringify(data, null, 2 /* pretty print */));
|
return makeTextFile(JSON.stringify(data, null, 2 /* pretty print */));
|
||||||
@@ -386,12 +404,12 @@ angular.module('app.frontend')
|
|||||||
*/
|
*/
|
||||||
this.mergeLocalDataRemotely = function(user, callback) {
|
this.mergeLocalDataRemotely = function(user, callback) {
|
||||||
var request = Restangular.one("users", user.id).one("merge");
|
var request = Restangular.one("users", user.id).one("merge");
|
||||||
var groups = user.groups;
|
var tags = user.tags;
|
||||||
request.notes = user.notes;
|
request.items = user.items;
|
||||||
request.notes.forEach(function(note){
|
request.items.forEach(function(item){
|
||||||
if(note.group_id) {
|
if(item.tag_id) {
|
||||||
var group = groups.filter(function(group){return group.id == note.group_id})[0];
|
var tag = tags.filter(function(tag){return tag.id == item.tag_id})[0];
|
||||||
note.group_name = group.name;
|
item.tag_name = tag.name;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
request.post().then(function(response){
|
request.post().then(function(response){
|
||||||
@@ -411,9 +429,9 @@ angular.module('app.frontend')
|
|||||||
|
|
||||||
this.writeUserToLocalStorage = function(user) {
|
this.writeUserToLocalStorage = function(user) {
|
||||||
var saveUser = _.cloneDeep(user);
|
var saveUser = _.cloneDeep(user);
|
||||||
saveUser.notes = Note.filterDummyNotes(saveUser.notes);
|
saveUser.items = Item.filterDummyItems(saveUser.items);
|
||||||
saveUser.groups.forEach(function(group){
|
saveUser.tags.forEach(function(tag){
|
||||||
group.notes = null;
|
tag.items = null;
|
||||||
}.bind(this))
|
}.bind(this))
|
||||||
this.writeToLocalStorage('user', saveUser);
|
this.writeToLocalStorage('user', saveUser);
|
||||||
}
|
}
|
||||||
@@ -425,7 +443,7 @@ angular.module('app.frontend')
|
|||||||
this.localUser = function() {
|
this.localUser = function() {
|
||||||
var user = JSON.parse(localStorage.getItem('user'));
|
var user = JSON.parse(localStorage.getItem('user'));
|
||||||
if(!user) {
|
if(!user) {
|
||||||
user = {notes: [], groups: []};
|
user = {items: [], tags: []};
|
||||||
}
|
}
|
||||||
user.shouldMerge = true;
|
user.shouldMerge = true;
|
||||||
return user;
|
return user;
|
||||||
@@ -436,7 +454,7 @@ angular.module('app.frontend')
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
this.saveDraftToDisk = function(draft) {
|
this.saveDraftToDisk = function(draft) {
|
||||||
localStorage.setItem("draft", JSON.stringify(draft));
|
// localStorage.setItem("draft", JSON.stringify(draft));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clearDraft = function() {
|
this.clearDraft = function() {
|
||||||
@@ -448,7 +466,7 @@ angular.module('app.frontend')
|
|||||||
if(!draftString || draftString == 'undefined') {
|
if(!draftString || draftString == 'undefined') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new Note(JSON.parse(draftString));
|
return new Item(JSON.parse(draftString));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -472,75 +490,75 @@ angular.module('app.frontend')
|
|||||||
localStorage.removeItem("gk");
|
localStorage.removeItem("gk");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.encryptSingleNote = function(note, key) {
|
this.encryptSingleItem = function(item, key) {
|
||||||
var ek = null;
|
var ek = null;
|
||||||
if(note.loc_eek) {
|
if(item.loc_eek) {
|
||||||
ek = Neeto.crypto.decryptText(note.loc_eek, key);
|
ek = Neeto.crypto.decryptText(item.loc_eek, key);
|
||||||
} else {
|
} else {
|
||||||
ek = Neeto.crypto.generateRandomEncryptionKey();
|
ek = Neeto.crypto.generateRandomEncryptionKey();
|
||||||
note.loc_eek = Neeto.crypto.encryptText(ek, key);
|
item.loc_eek = Neeto.crypto.encryptText(ek, key);
|
||||||
}
|
}
|
||||||
note.content = Neeto.crypto.encryptText(JSON.stringify(note.content), ek);
|
item.content = Neeto.crypto.encryptText(JSON.stringify(item.content), ek);
|
||||||
note.local_encryption_scheme = "1.0";
|
item.local_encryption_scheme = "1.0";
|
||||||
}
|
}
|
||||||
|
|
||||||
this.encryptNotes = function(notes, key) {
|
this.encryptItems = function(items, key) {
|
||||||
notes.forEach(function(note){
|
items.forEach(function(item){
|
||||||
this.encryptSingleNote(note, key);
|
this.encryptSingleItem(item, key);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.encryptSingleNoteWithLocalKey = function(note) {
|
this.encryptSingleItemWithLocalKey = function(item) {
|
||||||
this.encryptSingleNote(note, this.retrieveGk());
|
this.encryptSingleItem(item, this.retrieveGk());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.encryptNotesWithLocalKey = function(notes) {
|
this.encryptItemsWithLocalKey = function(items) {
|
||||||
this.encryptNotes(notes, this.retrieveGk());
|
this.encryptItems(items, this.retrieveGk());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.encryptNonPublicNotesWithLocalKey = function(notes) {
|
this.encryptNonPublicItemsWithLocalKey = function(items) {
|
||||||
var nonpublic = notes.filter(function(note){
|
var nonpublic = items.filter(function(item){
|
||||||
return !note.isPublic() && !note.pending_share;
|
return !item.isPublic() && !item.pending_share;
|
||||||
})
|
})
|
||||||
this.encryptNotes(nonpublic, this.retrieveGk());
|
this.encryptItems(nonpublic, this.retrieveGk());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.decryptSingleNoteWithLocalKey = function(note) {
|
this.decryptSingleItemWithLocalKey = function(item) {
|
||||||
this.decryptSingleNote(note, this.retrieveGk());
|
this.decryptSingleItem(item, this.retrieveGk());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.decryptSingleNote = function(note, key) {
|
this.decryptSingleItem = function(item, key) {
|
||||||
var ek = Neeto.crypto.decryptText(note.loc_eek || note.local_eek, key);
|
var ek = Neeto.crypto.decryptText(item.loc_eek || item.local_eek, key);
|
||||||
var content = Neeto.crypto.decryptText(note.content, ek);
|
var content = Neeto.crypto.decryptText(item.content, ek);
|
||||||
// console.log("decrypted contnet", content);
|
// console.log("decrypted contnet", content);
|
||||||
note.content = content;
|
item.content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.decryptNotes = function(notes, key) {
|
this.decryptItems = function(items, key) {
|
||||||
notes.forEach(function(note){
|
items.forEach(function(item){
|
||||||
// console.log("is encrypted?", note);
|
// console.log("is encrypted?", item);
|
||||||
if(note.isEncrypted()) {
|
if(item.loc_eek && typeof item.content === 'string') {
|
||||||
this.decryptSingleNote(note, key);
|
this.decryptSingleItem(item, key);
|
||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.decryptNotesWithLocalKey = function(notes) {
|
this.decryptItemsWithLocalKey = function(items) {
|
||||||
this.decryptNotes(notes, this.retrieveGk());
|
this.decryptItems(items, this.retrieveGk());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.reencryptAllNotesAndSave = function(user, newKey, oldKey, callback) {
|
this.reencryptAllItemsAndSave = function(user, newKey, oldKey, callback) {
|
||||||
var notes = user.filteredNotes();
|
var items = user.filteredItems();
|
||||||
notes.forEach(function(note){
|
items.forEach(function(item){
|
||||||
if(note.isEncrypted()) {
|
if(item.loc_eek && typeof item.content === 'string') {
|
||||||
// first decrypt eek with old key
|
// first decrypt eek with old key
|
||||||
var ek = Neeto.crypto.decryptText(note.loc_eek, oldKey);
|
var ek = Neeto.crypto.decryptText(item.loc_eek, oldKey);
|
||||||
// now encrypt ek with new key
|
// now encrypt ek with new key
|
||||||
note.loc_eek = Neeto.crypto.encryptText(ek, newKey);
|
item.loc_eek = Neeto.crypto.encryptText(ek, newKey);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.saveBatchNotes(user, notes, function(success) {
|
this.saveBatchItems(user, items, function(success) {
|
||||||
callback(success);
|
callback(success);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ angular
|
|||||||
scope: {
|
scope: {
|
||||||
drop: '&',
|
drop: '&',
|
||||||
bin: '=',
|
bin: '=',
|
||||||
group: "="
|
tag: "="
|
||||||
},
|
},
|
||||||
link: function(scope, element) {
|
link: function(scope, element) {
|
||||||
// again we need the native object
|
// again we need the native object
|
||||||
@@ -96,7 +96,7 @@ angular
|
|||||||
scope.$apply(function(scope) {
|
scope.$apply(function(scope) {
|
||||||
var fn = scope.drop();
|
var fn = scope.drop();
|
||||||
if ('undefined' !== typeof fn) {
|
if ('undefined' !== typeof fn) {
|
||||||
fn(e, scope.group, note);
|
fn(e, scope.tag, note);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,19 @@ Neeto.crypto = {
|
|||||||
return CryptoJS.lib.WordArray.random(256/8).toString();
|
return CryptoJS.lib.WordArray.random(256/8).toString();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
generateUUID: function() {
|
||||||
|
var d = new Date().getTime();
|
||||||
|
if(window.performance && typeof window.performance.now === "function"){
|
||||||
|
d += performance.now(); //use high-precision timer if available
|
||||||
|
}
|
||||||
|
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||||
|
var r = (d + Math.random()*16)%16 | 0;
|
||||||
|
d = Math.floor(d/16);
|
||||||
|
return (c=='x' ? r : (r&0x3|0x8)).toString(16);
|
||||||
|
});
|
||||||
|
return uuid;
|
||||||
|
},
|
||||||
|
|
||||||
decryptText: function(encrypted_content, key) {
|
decryptText: function(encrypted_content, key) {
|
||||||
return CryptoJS.AES.decrypt(encrypted_content, key).toString(CryptoJS.enc.Utf8);
|
return CryptoJS.AES.decrypt(encrypted_content, key).toString(CryptoJS.enc.Utf8);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
class ItemManager {
|
class ItemManager {
|
||||||
|
|
||||||
set items(items) {
|
set items(items) {
|
||||||
this.items = items;
|
this._items = items;
|
||||||
resolveReferences();
|
this.resolveReferences();
|
||||||
|
}
|
||||||
|
|
||||||
|
get items() {
|
||||||
|
return this._items;
|
||||||
}
|
}
|
||||||
|
|
||||||
referencesForItemId(itemId) {
|
referencesForItemId(itemId) {
|
||||||
@@ -12,14 +16,14 @@ class ItemManager {
|
|||||||
resolveReferences() {
|
resolveReferences() {
|
||||||
this.items.forEach(function(item){
|
this.items.forEach(function(item){
|
||||||
// build out references
|
// build out references
|
||||||
_.map(item.references, function(reference){
|
item.content.references = _.map(item.content.references, function(reference){
|
||||||
return referencesForItemId(reference.uuid);
|
return this.referencesForItemId(reference.uuid);
|
||||||
})
|
}.bind(this))
|
||||||
});
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
itemsForContentType(contentType) {
|
itemsForContentType(contentType) {
|
||||||
this.items.filter(function(item){
|
return this.items.filter(function(item){
|
||||||
return item.content_type == contentType;
|
return item.content_type == contentType;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -27,22 +31,22 @@ class ItemManager {
|
|||||||
// returns dirty item references that need saving
|
// returns dirty item references that need saving
|
||||||
deleteItem(item) {
|
deleteItem(item) {
|
||||||
_.remove(this.items, item);
|
_.remove(this.items, item);
|
||||||
item.references.forEach(function(referencedItem){
|
item.content.references.forEach(function(referencedItem){
|
||||||
removeReferencesBetweenItems(referencedItem, item);
|
this.removeReferencesBetweenItems(referencedItem, item);
|
||||||
})
|
}.bind(this))
|
||||||
|
|
||||||
return item.references;
|
return item.content.references;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeReferencesBetweenItems(itemOne, itemTwo) {
|
removeReferencesBetweenItems(itemOne, itemTwo) {
|
||||||
_.remove(itemOne.references, _.find(itemOne.references, {uuid: itemTwo.uuid}));
|
itemOne.removeReference(itemTwo);
|
||||||
_.remove(itemTwo.references, _.find(itemTwo.references, {uuid: itemOne.uuid}));
|
itemTwo.removeReference(itemOne);
|
||||||
return [itemOne, itemTwo];
|
return [itemOne, itemTwo];
|
||||||
}
|
}
|
||||||
|
|
||||||
createReferencesBetweenItems(itemOne, itemTwo) {
|
createReferencesBetweenItems(itemOne, itemTwo) {
|
||||||
itemOne.references.push(itemTwo);
|
itemOne.addReference(itemTwo);
|
||||||
itemTwo.references.push(itemOne);
|
itemTwo.addReference(itemOne);
|
||||||
return [itemOne, itemTwo];
|
return [itemOne, itemTwo];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,37 @@
|
|||||||
class ModelManager extends ItemManager {
|
class ModelManager extends ItemManager {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.notes = [];
|
||||||
|
this.groups = [];
|
||||||
|
this.dirtyItems = [];
|
||||||
|
}
|
||||||
|
|
||||||
set items(items) {
|
set items(items) {
|
||||||
super.items = items;
|
super.items = items;
|
||||||
|
this.notes = _.map(this.itemsForContentType("Note"), function(json_obj) {
|
||||||
this.notes = _.map(this.items.itemsForContentType("Note"), function(json_obj) {
|
|
||||||
return new Note(json_obj);
|
return new Note(json_obj);
|
||||||
})
|
})
|
||||||
|
|
||||||
this.groups = _.map(this.items.itemsForContentType("Group"), function(json_obj) {
|
this.tags = _.map(this.itemsForContentType("Tag"), function(json_obj) {
|
||||||
var group = Group(json_obj);
|
var tag = new Tag(json_obj);
|
||||||
group.updateReferencesLocalMapping();
|
console.log("tag references upon import", tag.content.references);
|
||||||
return group;
|
tag.updateReferencesLocalMapping();
|
||||||
|
return tag;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
addDirtyItems(items) {
|
get items() {
|
||||||
if(this.dirtyItems) {
|
return super.items;
|
||||||
this.dirtyItems = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dirtyItems.concat(items);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get dirtyItems() {
|
addDirtyItems(items) {
|
||||||
return this.dirtyItems || [];
|
if(!(items instanceof Array)) {
|
||||||
|
items = [items];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dirtyItems = this.dirtyItems.concat(items);
|
||||||
|
this.dirtyItems = _.uniq(this.dirtyItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
get filteredNotes() {
|
get filteredNotes() {
|
||||||
@@ -45,6 +53,7 @@ class ModelManager extends ItemManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addTagToNote(tag, note) {
|
addTagToNote(tag, note) {
|
||||||
|
console.log("adding tag to note", tag, note);
|
||||||
var dirty = this.createReferencesBetweenItems(tag, note);
|
var dirty = this.createReferencesBetweenItems(tag, note);
|
||||||
this.refreshRelationshipsForTag(tag);
|
this.refreshRelationshipsForTag(tag);
|
||||||
this.refreshRelationshipsForNote(note);
|
this.refreshRelationshipsForNote(note);
|
||||||
@@ -59,7 +68,7 @@ class ModelManager extends ItemManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
refreshRelationshipsForNote(note) {
|
refreshRelationshipsForNote(note) {
|
||||||
note.groups = note.referencesMatchingContentType("Group");
|
note.tags = note.referencesMatchingContentType("Tag");
|
||||||
}
|
}
|
||||||
|
|
||||||
removeTagFromNote(tag, note) {
|
removeTagFromNote(tag, note) {
|
||||||
|
|||||||
@@ -337,7 +337,7 @@ input, button, select, textarea {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group {
|
.form-tag {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
height: 136px !important;
|
height: 136px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.group-menu-bar {
|
.tag-menu-bar {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 0 -20px;
|
margin: 0 -20px;
|
||||||
width: auto;
|
width: auto;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
.groups {
|
.tags {
|
||||||
width: 22%;
|
width: 22%;
|
||||||
|
|
||||||
.groups-title-bar {
|
.tags-title-bar {
|
||||||
color: #0707ff;
|
color: #0707ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.group {
|
.tag {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
border-bottom: 1px solid $bg-color;
|
border-bottom: 1px solid $bg-color;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* When a note is dragged over group */
|
/* When a note is dragged over tag */
|
||||||
&.over {
|
&.over {
|
||||||
background-color: rgba(#5151ff, 0.8);
|
background-color: rgba(#5151ff, 0.8);
|
||||||
color: white;
|
color: white;
|
||||||
@@ -4,7 +4,7 @@ $dark-gray: #2e2e2e;
|
|||||||
@import "app/common";
|
@import "app/common";
|
||||||
@import "app/main";
|
@import "app/main";
|
||||||
@import "app/header";
|
@import "app/header";
|
||||||
@import "app/groups";
|
@import "app/tags";
|
||||||
@import "app/notes";
|
@import "app/notes";
|
||||||
@import "app/editor";
|
@import "app/editor";
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
.panel-body
|
.panel-body
|
||||||
%p We'll send reset instructions to your email.
|
%p We'll send reset instructions to your email.
|
||||||
%form{'ng-submit' => 'requestPasswordReset(forgotData)', 'ng-init' => 'forgotData = {}'}
|
%form{'ng-submit' => 'requestPasswordReset(forgotData)', 'ng-init' => 'forgotData = {}'}
|
||||||
.form-group.has-feedback
|
.form-tag.has-feedback
|
||||||
%input.form-control{:autofocus => 'autofocus', :name => 'email', :placeholder => 'Email', :required => true, :type => 'email', 'ng-model' => 'forgotData.email'}/
|
%input.form-control{:autofocus => 'autofocus', :name => 'email', :placeholder => 'Email', :required => true, :type => 'email', 'ng-model' => 'forgotData.email'}/
|
||||||
%span.glyphicon.glyphicon-envelope.form-control-feedback
|
%span.glyphicon.glyphicon-envelope.form-control-feedback
|
||||||
.row
|
.row
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
%h3.panel-title Sign in to start your session
|
%h3.panel-title Sign in to start your session
|
||||||
.panel-body
|
.panel-body
|
||||||
%form{'ng-submit' => 'submitLogin(loginData)', 'ng-init' => 'loginData = {}'}
|
%form{'ng-submit' => 'submitLogin(loginData)', 'ng-init' => 'loginData = {}'}
|
||||||
.form-group.has-feedback
|
.form-tag.has-feedback
|
||||||
%input.form-control{:autofocus => 'autofocus', :name => 'email', :placeholder => 'Username', :required => true, :type => 'email', 'ng-model' => 'loginData.email'}/
|
%input.form-control{:autofocus => 'autofocus', :name => 'email', :placeholder => 'Username', :required => true, :type => 'email', 'ng-model' => 'loginData.email'}/
|
||||||
%span.glyphicon.glyphicon-user.form-control-feedback
|
%span.glyphicon.glyphicon-user.form-control-feedback
|
||||||
.form-group.has-feedback
|
.form-tag.has-feedback
|
||||||
%input.form-control{:placeholder => 'Password', :name => 'password', :required => true, :type => 'password', 'ng-model' => 'loginData.password'}/
|
%input.form-control{:placeholder => 'Password', :name => 'password', :required => true, :type => 'password', 'ng-model' => 'loginData.password'}/
|
||||||
%span.glyphicon.glyphicon-lock.form-control-feedback
|
%span.glyphicon.glyphicon-lock.form-control-feedback
|
||||||
.row
|
.row
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
.panel-body
|
.panel-body
|
||||||
%p Type your new password.
|
%p Type your new password.
|
||||||
%form{'ng-submit' => 'resetPasswordSubmit()'}
|
%form{'ng-submit' => 'resetPasswordSubmit()'}
|
||||||
.form-group.has-feedback
|
.form-tag.has-feedback
|
||||||
%input.form-control{:placeholder => 'New password', :name => 'password', :required => true, :type => 'password', 'ng-model' => 'resetData.password'}/
|
%input.form-control{:placeholder => 'New password', :name => 'password', :required => true, :type => 'password', 'ng-model' => 'resetData.password'}/
|
||||||
%span.glyphicon.glyphicon-lock.form-control-feedback
|
%span.glyphicon.glyphicon-lock.form-control-feedback
|
||||||
.form-group.has-feedback
|
.form-tag.has-feedback
|
||||||
%input.form-control{:placeholder => 'Password confirmation', :name => 'password_confirmation', :required => true, :type => 'password', 'ng-model' => 'resetData.password_confirmation'}/
|
%input.form-control{:placeholder => 'Password confirmation', :name => 'password_confirmation', :required => true, :type => 'password', 'ng-model' => 'resetData.password_confirmation'}/
|
||||||
%span.glyphicon.glyphicon-lock.form-control-feedback
|
%span.glyphicon.glyphicon-lock.form-control-feedback
|
||||||
.row
|
.row
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
.section.groups
|
|
||||||
.content
|
|
||||||
.section-title-bar.groups-title-bar
|
|
||||||
.title Groups
|
|
||||||
.add-button{"ng-click" => "ctrl.clickedAddNewGroup()"} +
|
|
||||||
.group{"ng-if" => "ctrl.allGroup", "ng-click" => "ctrl.selectGroup(ctrl.allGroup)", "ng-class" => "{'selected' : ctrl.selectedGroup == ctrl.allGroup}",
|
|
||||||
"droppable" => true, "drop" => "ctrl.handleDrop", "group" => "ctrl.allGroup"}
|
|
||||||
%input.title{"ng-disabled" => "true", "ng-model" => "ctrl.allGroup.name"}
|
|
||||||
.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"}
|
|
||||||
%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)"}
|
|
||||||
.count {{ctrl.noteCount(group)}}
|
|
||||||
@@ -20,9 +20,9 @@
|
|||||||
.title Sign in or Register
|
.title Sign in or Register
|
||||||
.desc
|
.desc
|
||||||
%form.account-form{'name' => "loginForm"}
|
%form.account-form{'name' => "loginForm"}
|
||||||
.form-group.has-feedback
|
.form-tag.has-feedback
|
||||||
%input.form-control.login-input{:autofocus => 'autofocus', :name => 'email', :placeholder => 'Email', :required => true, :type => 'email', 'ng-model' => 'ctrl.loginData.email'}
|
%input.form-control.login-input{:autofocus => 'autofocus', :name => 'email', :placeholder => 'Email', :required => true, :type => 'email', 'ng-model' => 'ctrl.loginData.email'}
|
||||||
.form-group.has-feedback
|
.form-tag.has-feedback
|
||||||
%input.form-control.login-input{:placeholder => 'Password', :name => 'password', :required => true, :type => 'password', 'ng-model' => 'ctrl.loginData.user_password'}
|
%input.form-control.login-input{:placeholder => 'Password', :name => 'password', :required => true, :type => 'password', 'ng-model' => 'ctrl.loginData.user_password'}
|
||||||
.checkbox{"ng-if" => "ctrl.hasLocalData()"}
|
.checkbox{"ng-if" => "ctrl.hasLocalData()"}
|
||||||
%label
|
%label
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
.panel-status-text{"ng-if" => "ctrl.loginData.status", "style" => "font-size: 14px;"} {{ctrl.loginData.status}}
|
.panel-status-text{"ng-if" => "ctrl.loginData.status", "style" => "font-size: 14px;"} {{ctrl.loginData.status}}
|
||||||
|
|
||||||
%form{"style" => "margin-top: 20px;", "ng-if" => "ctrl.showResetForm", "ng-init" => "ctrl.resetData = {}", 'ng-submit' => 'ctrl.forgotPasswordSubmit()', 'name' => "resetForm"}
|
%form{"style" => "margin-top: 20px;", "ng-if" => "ctrl.showResetForm", "ng-init" => "ctrl.resetData = {}", 'ng-submit' => 'ctrl.forgotPasswordSubmit()', 'name' => "resetForm"}
|
||||||
.form-group.has-feedback
|
.form-tag.has-feedback
|
||||||
%input.form-control.login-input{:autofocus => 'autofocus', :name => 'email', :placeholder => 'Email', :required => true, :type => 'email', 'ng-model' => 'ctrl.resetData.email'}
|
%input.form-control.login-input{:autofocus => 'autofocus', :name => 'email', :placeholder => 'Email', :required => true, :type => 'email', 'ng-model' => 'ctrl.resetData.email'}
|
||||||
%button.btn.dark-button.btn-block{:type => 'submit', "data-style" => "expand-right", "data-size" => "s", "state" => "buttonState"}
|
%button.btn.dark-button.btn-block{:type => 'submit', "data-style" => "expand-right", "data-size" => "s", "state" => "buttonState"}
|
||||||
%span Send Reset Email
|
%span Send Reset Email
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
.desc Use a custom Neeto server to store and retrieve your account data.
|
.desc Use a custom Neeto server to store and retrieve your account data.
|
||||||
.action-container
|
.action-container
|
||||||
%form.account-form{'ng-submit' => 'ctrl.changeServer()', 'name' => "serverChangeForm"}
|
%form.account-form{'ng-submit' => 'ctrl.changeServer()', 'name' => "serverChangeForm"}
|
||||||
.form-group.has-feedback
|
.form-tag.has-feedback
|
||||||
%input.form-control{:name => 'server', :placeholder => 'Server URL', :required => true, :type => 'text', 'ng-model' => 'ctrl.serverData.url'}
|
%input.form-control{:name => 'server', :placeholder => 'Server URL', :required => true, :type => 'text', 'ng-model' => 'ctrl.serverData.url'}
|
||||||
%button.btn.dark-button.btn-block{:type => 'submit', "data-style" => "expand-right", "data-size" => "s", "state" => "buttonState"}
|
%button.btn.dark-button.btn-block{:type => 'submit', "data-style" => "expand-right", "data-size" => "s", "state" => "buttonState"}
|
||||||
%span.ladda-label Change Server
|
%span.ladda-label Change Server
|
||||||
@@ -82,11 +82,11 @@
|
|||||||
.link-item
|
.link-item
|
||||||
%a{"ng-click" => "ctrl.changePasswordPressed()"} Change Password
|
%a{"ng-click" => "ctrl.changePasswordPressed()"} Change Password
|
||||||
%form.account-form{"ng-if" => "ctrl.showNewPasswordForm", 'ng-submit' => 'ctrl.submitPasswordChange()', 'name' => "passwordChangeForm"}
|
%form.account-form{"ng-if" => "ctrl.showNewPasswordForm", 'ng-submit' => 'ctrl.submitPasswordChange()', 'name' => "passwordChangeForm"}
|
||||||
.form-group.has-feedback
|
.form-tag.has-feedback
|
||||||
%input.form-control.login-input{:autofocus => 'autofocus', :name => 'current', :placeholder => 'Current password', :required => true, :type => 'password', 'ng-model' => 'ctrl.passwordChangeData.current_password'}
|
%input.form-control.login-input{:autofocus => 'autofocus', :name => 'current', :placeholder => 'Current password', :required => true, :type => 'password', 'ng-model' => 'ctrl.passwordChangeData.current_password'}
|
||||||
.form-group.has-feedback
|
.form-tag.has-feedback
|
||||||
%input.form-control.login-input{:placeholder => 'New password', :name => 'password', :required => true, :type => 'password', 'ng-model' => 'ctrl.passwordChangeData.new_password', "autocomplete" => "new-password"}
|
%input.form-control.login-input{:placeholder => 'New password', :name => 'password', :required => true, :type => 'password', 'ng-model' => 'ctrl.passwordChangeData.new_password', "autocomplete" => "new-password"}
|
||||||
.form-group.has-feedback
|
.form-tag.has-feedback
|
||||||
%input.form-control.login-input{:placeholder => 'Confirm password', :name => 'password', :required => true, :type => 'password', 'ng-model' => 'ctrl.passwordChangeData.new_password_confirmation', "autocomplete" => "new-password"}
|
%input.form-control.login-input{:placeholder => 'Confirm password', :name => 'password', :required => true, :type => 'password', 'ng-model' => 'ctrl.passwordChangeData.new_password_confirmation', "autocomplete" => "new-password"}
|
||||||
%button.btn.dark-button.btn-block{:type => 'submit', "data-style" => "expand-right", "data-size" => "s", "state" => "buttonState"}
|
%button.btn.dark-button.btn-block{:type => 'submit', "data-style" => "expand-right", "data-size" => "s", "state" => "buttonState"}
|
||||||
%span.ladda-label Change Password
|
%span.ladda-label Change Password
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
%header{"user" => "defaultUser", "logout" => "headerLogout"}
|
%header{"user" => "defaultUser", "logout" => "headerLogout"}
|
||||||
.app-container
|
.app-container
|
||||||
.app
|
.app
|
||||||
%groups-section{"save" => "groupsSave", "add-new" => "groupsAddNew", "will-select" => "groupsWillMakeSelection", "selection-made" => "groupsSelectionMade", "all-group" => "allGroup",
|
%tags-section{"save" => "tagsSave", "add-new" => "tagsAddNew", "will-select" => "tagsWillMakeSelection", "selection-made" => "tagsSelectionMade", "all-tag" => "allTag",
|
||||||
"groups" => "groups", "user" => "defaultUser", "update-note-group" => "groupsUpdateNoteGroup"}
|
"tags" => "tags", "user" => "defaultUser", "update-note-tag" => "tagsUpdateNoteTag"}
|
||||||
|
|
||||||
%notes-section{"remove-group" => "notesRemoveGroup", "user" => "defaultUser", "add-new" => "notesAddNew", "selection-made" => "notesSelectionMade",
|
%notes-section{"remove-tag" => "notesRemoveTag", "user" => "defaultUser", "add-new" => "notesAddNew", "selection-made" => "notesSelectionMade",
|
||||||
"group" => "selectedGroup", "user-id" => "defaultUser.id", "remove" => "deleteNote"}
|
"tag" => "selectedTag", "user-id" => "defaultUser.id", "remove" => "deleteNote"}
|
||||||
|
|
||||||
%editor-section{"ng-if" => "selectedNote", "note" => "selectedNote", "remove" => "deleteNote",
|
%editor-section{"ng-if" => "selectedNote", "note" => "selectedNote", "remove" => "deleteNote",
|
||||||
"user" => "defaultUser", "save" => "saveNote"}
|
"user" => "defaultUser", "save" => "saveNote"}
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
%strong Choose a public username for all your shared note groups.
|
%strong Choose a public username for all your shared note tags.
|
||||||
%input{"style" => "margin-top: 10px; padding-left: 8px;", "type" => "text", "ng-keyup" => "$event.keyCode == 13 && saveUsername($event)", "ng-model" => "formData.username"}
|
%input{"style" => "margin-top: 10px; padding-left: 8px;", "type" => "text", "ng-keyup" => "$event.keyCode == 13 && saveUsername($event)", "ng-model" => "formData.username"}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
.section.notes
|
.section.notes
|
||||||
.content
|
.content
|
||||||
.section-title-bar.notes-title-bar
|
.section-title-bar.notes-title-bar
|
||||||
.title {{ctrl.group.name}} notes
|
.title {{ctrl.tag.name}} notes
|
||||||
.add-button{"ng-click" => "ctrl.createNewNote()"} +
|
.add-button{"ng-click" => "ctrl.createNewNote()"} +
|
||||||
%br
|
%br
|
||||||
.filter-section
|
.filter-section
|
||||||
%input.filter-bar{"select-on-click" => "true", "ng-model" => "ctrl.noteFilter.text", "placeholder" => "Filter", "ng-change" => "ctrl.filterTextChanged()", "lowercase" => "true"}
|
%input.filter-bar{"select-on-click" => "true", "ng-model" => "ctrl.noteFilter.text", "placeholder" => "Filter", "ng-change" => "ctrl.filterTextChanged()", "lowercase" => "true"}
|
||||||
.editor-menu.group-menu-bar
|
.editor-menu.tag-menu-bar
|
||||||
%ul.nav.nav-pills
|
%ul.nav.nav-pills
|
||||||
%li.dropdown
|
%li.dropdown
|
||||||
%a.dropdown-toggle{"ng-click" => "ctrl.showMenu = !ctrl.showMenu"}
|
%a.dropdown-toggle{"ng-click" => "ctrl.showMenu = !ctrl.showMenu"}
|
||||||
@@ -14,17 +14,17 @@
|
|||||||
%span.caret
|
%span.caret
|
||||||
%span.sr-only
|
%span.sr-only
|
||||||
%ul.dropdown-menu.dropdown-menu-left.nt-dropdown-menu.dark{"ng-if" => "ctrl.showMenu"}
|
%ul.dropdown-menu.dropdown-menu-left.nt-dropdown-menu.dark{"ng-if" => "ctrl.showMenu"}
|
||||||
%li{"ng-if" => "!ctrl.group.presentation"}
|
%li{"ng-if" => "!ctrl.tag.presentation"}
|
||||||
%a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedGroupShare($event)"} Share Group
|
%a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedTagShare($event)"} Share Tag
|
||||||
%li{"ng-if" => "ctrl.group.presentation"}
|
%li{"ng-if" => "ctrl.tag.presentation"}
|
||||||
%a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedGroupUnshare()"} Unshare Group
|
%a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedTagUnshare()"} Unshare Tag
|
||||||
%li{"ng-if" => "!ctrl.group.all"}
|
%li{"ng-if" => "!ctrl.tag.all"}
|
||||||
%a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedGroupDelete()"} Delete Group
|
%a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedTagDelete()"} Delete Tag
|
||||||
.menu-right-container
|
.menu-right-container
|
||||||
.public-link{"ng-if" => "ctrl.group.presentation"}
|
.public-link{"ng-if" => "ctrl.tag.presentation"}
|
||||||
%a.url{"ng-if" => "!ctrl.editingUrl", "href" => "{{ctrl.publicUrlForGroup(ctrl.group)}}", "target" => "_blank"}
|
%a.url{"ng-if" => "!ctrl.editingUrl", "href" => "{{ctrl.publicUrlForTag(ctrl.tag)}}", "target" => "_blank"}
|
||||||
%span.icon-rss.icon
|
%span.icon-rss.icon
|
||||||
{{ctrl.publicUrlForGroup()}}
|
{{ctrl.publicUrlForTag()}}
|
||||||
.edit-url{"ng-if" => "ctrl.editingUrl"}
|
.edit-url{"ng-if" => "ctrl.editingUrl"}
|
||||||
{{ctrl.url.base}}
|
{{ctrl.url.base}}
|
||||||
%input.input{"ng-model" => "ctrl.url.token", "ng-keyup" => "$event.keyCode == 13 && ctrl.saveUrl($event)",
|
%input.input{"ng-model" => "ctrl.url.token", "ng-keyup" => "$event.keyCode == 13 && ctrl.saveUrl($event)",
|
||||||
|
|||||||
18
app/assets/templates/frontend/tags.html.haml
Normal file
18
app/assets/templates/frontend/tags.html.haml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
.section.tags
|
||||||
|
.content
|
||||||
|
.section-title-bar.tags-title-bar
|
||||||
|
.title Tags
|
||||||
|
.add-button{"ng-click" => "ctrl.clickedAddNewTag()"} +
|
||||||
|
{{ctrl.test}}
|
||||||
|
.tag{"ng-if" => "ctrl.allTag", "ng-click" => "ctrl.selectTag(ctrl.allTag)", "ng-class" => "{'selected' : ctrl.selectedTag == ctrl.allTag}",
|
||||||
|
"droppable" => true, "drop" => "ctrl.handleDrop", "tag" => "ctrl.allTag"}
|
||||||
|
%input.title{"ng-disabled" => "true", "ng-model" => "ctrl.allTag.content.name"}
|
||||||
|
.count {{ctrl.noteCount(ctrl.allTag)}}
|
||||||
|
|
||||||
|
.tag{"ng-repeat" => "tag in ctrl.tags", "ng-click" => "ctrl.selectTag(tag)", "ng-class" => "{'selected' : ctrl.selectedTag == tag}",
|
||||||
|
"droppable" => true, "drop" => "ctrl.handleDrop", "tag" => "tag"}
|
||||||
|
.icon.icon-rss{"ng-if" => "tag.presentation"}
|
||||||
|
%input.title{"ng-disabled" => "tag != ctrl.selectedTag", "ng-model" => "tag.content.name",
|
||||||
|
"ng-keyup" => "$event.keyCode == 13 && ctrl.saveTag($event, tag)", "mb-autofocus" => "true", "should-focus" => "ctrl.newTag",
|
||||||
|
"ng-change" => "ctrl.tagTitleDidChange(tag)", "ng-focus" => "ctrl.onTagTitleFocus(tag)"}
|
||||||
|
.count {{ctrl.noteCount(tag)}}
|
||||||
816
vendor/assets/javascripts/transpiled.js
vendored
816
vendor/assets/javascripts/transpiled.js
vendored
File diff suppressed because it is too large
Load Diff
2
vendor/assets/javascripts/transpiled.js.map
vendored
2
vendor/assets/javascripts/transpiled.js.map
vendored
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user