api format, css, auth errors

This commit is contained in:
Mo Bitar
2016-12-27 13:27:57 -06:00
parent 36d90804d0
commit 139c8c62e6
14 changed files with 432 additions and 316 deletions

View File

@@ -75,8 +75,8 @@ module.exports = function(grunt) {
'app/assets/javascripts/app/frontend/*.js', 'app/assets/javascripts/app/frontend/*.js',
'app/assets/javascripts/app/frontend/controllers/*.js', 'app/assets/javascripts/app/frontend/controllers/*.js',
'app/assets/javascripts/app/frontend/models/**/*.js', 'app/assets/javascripts/app/frontend/models/**/*.js',
'app/assets/javascripts/app/services/*.js', 'app/assets/javascripts/app/services/**/*.js',
'app/assets/javascripts/app/services/directives/*.js', // 'app/assets/javascripts/app/services/directives/*.js',
], ],
dest: 'vendor/assets/javascripts/app.js', dest: 'vendor/assets/javascripts/app.js',
}, },

View File

@@ -22,7 +22,7 @@ angular.module('app.frontend', [
RestangularProvider.setDefaultHeaders({"Content-Type": "application/json"}); RestangularProvider.setDefaultHeaders({"Content-Type": "application/json"});
var url = apiControllerProvider.defaultServerURL(); var url = apiControllerProvider.defaultServerURL();
RestangularProvider.setBaseUrl(url); RestangularProvider.setBaseUrl(url + "/api");
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");

View File

@@ -73,9 +73,10 @@ angular.module('app.frontend')
this.loginData.status = "Generating Login Keys..."; this.loginData.status = "Generating Login Keys...";
$timeout(function(){ $timeout(function(){
apiController.login(this.loginData.email, this.loginData.user_password, function(response){ apiController.login(this.loginData.email, this.loginData.user_password, function(response){
if(response.errors) { if(!response || response.error) {
console.log("login error", response.errors); var error = response ? response.error : {message: "An unknown error occured."}
this.loginData.status = response.errors[0]; this.loginData.status = null;
alert(error.message);
} else { } else {
this.onAuthSuccess(response.user); this.onAuthSuccess(response.user);
} }
@@ -88,8 +89,10 @@ angular.module('app.frontend')
$timeout(function(){ $timeout(function(){
apiController.register(this.loginData.email, this.loginData.user_password, function(response){ apiController.register(this.loginData.email, this.loginData.user_password, function(response){
if(response.errors) { if(!response || response.error) {
this.loginData.status = response.errors[0]; var error = response ? response.error : {message: "An unknown error occured."}
this.loginData.status = null;
alert(error.message);
} else { } else {
this.onAuthSuccess(response.user); this.onAuthSuccess(response.user);
} }

View File

@@ -19,7 +19,7 @@ angular.module('app.frontend')
$rootScope.title = "Notes — Standard Notes"; $rootScope.title = "Notes — Standard Notes";
onUserSet(); onUserSet();
} else { } else {
$scope.defaultUser = new User(apiController.localUser()); $scope.defaultUser = new User(apiController.loadLocalItemsAndUser());
onUserSet(); onUserSet();
} }
}); });
@@ -141,7 +141,7 @@ angular.module('app.frontend')
*/ */
$scope.headerLogout = function() { $scope.headerLogout = function() {
$scope.defaultUser = apiController.localUser(); $scope.defaultUser = apiController.loadLocalItemsAndUser();
$scope.tags = $scope.defaultUser.tags; $scope.tags = $scope.defaultUser.tags;
} }

View File

@@ -26,6 +26,14 @@ class Item {
_.merge(this, json_obj); _.merge(this, json_obj);
if(this.created_at) {
this.created_at = new Date(this.created_at);
this.updated_at = new Date(this.updated_at);
} else {
this.created_at = new Date();
this.updated_at = new Date();
}
if(!this.uuid) { if(!this.uuid) {
this.uuid = Neeto.crypto.generateUUID(); this.uuid = Neeto.crypto.generateUUID();
} }
@@ -43,6 +51,12 @@ class Item {
} }
} }
static sortItemsByDate(items) {
items.sort(function(a,b){
return new Date(b.created_at) - new Date(a.created_at);
});
}
addReference(reference) { addReference(reference) {
this.content.references.push(reference); this.content.references.push(reference);
this.content.references = _.uniq(this.content.references); this.content.references = _.uniq(this.content.references);

View File

@@ -62,6 +62,10 @@ angular.module('app.frontend')
request.get({email: email}).then(function(response){ request.get({email: email}).then(function(response){
callback(response.plain()); callback(response.plain());
}) })
.catch(function(response){
console.log("Error getting current user", response);
callback(response.data);
})
} }
this.getCurrentUser = function(callback) { this.getCurrentUser = function(callback) {
@@ -77,22 +81,32 @@ angular.module('app.frontend')
var user = _.omit(plain, ["items"]); var user = _.omit(plain, ["items"]);
callback(user, items); callback(user, items);
}.bind(this)) }.bind(this))
.catch(function(error){ .catch(function(response){
console.log("Error getting current user", error); console.log("Error getting current user", response);
callback(null); callback(response.data);
}) })
} }
this.login = function(email, password, callback) { this.login = function(email, password, callback) {
this.getAuthParamsForEmail(email, function(authParams){ this.getAuthParamsForEmail(email, function(authParams){
if(!authParams) {
callback(null);
return;
}
Neeto.crypto.computeEncryptionKeysForUser(_.merge({email: email, password: password}, authParams), function(keys){ Neeto.crypto.computeEncryptionKeysForUser(_.merge({email: email, password: password}, authParams), function(keys){
this.setMk(keys.mk); this.setMk(keys.mk);
var request = Restangular.one("auth/sign_in"); var request = Restangular.one("auth/sign_in");
request.user = {password: keys.pw, email: email}; console.log("sending pw", keys.pw);
var params = {password: keys.pw, email: email};
_.merge(request, params);
request.post().then(function(response){ request.post().then(function(response){
localStorage.setItem("jwt", response.token); localStorage.setItem("jwt", response.token);
callback(response); callback(response);
}) })
.catch(function(response){
console.log(response.data);
callback(response.data);
})
}.bind(this)); }.bind(this));
}.bind(this)) }.bind(this))
} }
@@ -102,16 +116,25 @@ angular.module('app.frontend')
this.setMk(keys.mk); this.setMk(keys.mk);
keys.mk = null; keys.mk = null;
var request = Restangular.one("auth"); var request = Restangular.one("auth");
request.user = _.merge({password: keys.pw, email: email}, keys); var params = _.merge({password: keys.pw, email: email}, keys);
_.merge(request, params);
request.post().then(function(response){ request.post().then(function(response){
localStorage.setItem("jwt", response.token); localStorage.setItem("jwt", response.token);
callback(response); callback(response);
}) })
.catch(function(response){
console.log(response.data);
callback(response.data);
})
}.bind(this)); }.bind(this));
} }
this.changePassword = function(user, current_password, new_password) { this.changePassword = function(user, current_password, new_password) {
this.getAuthParamsForEmail(email, function(authParams){ this.getAuthParamsForEmail(email, function(authParams){
if(!authParams) {
callback(null);
return;
}
Neeto.crypto.computeEncryptionKeysForUser(_.merge({password: current_password, email: user.email}, authParams), function(currentKeys) { Neeto.crypto.computeEncryptionKeysForUser(_.merge({password: current_password, email: user.email}, authParams), function(currentKeys) {
Neeto.crypto.computeEncryptionKeysForUser(_.merge({password: new_password, email: user.email}, authParams), function(newKeys){ Neeto.crypto.computeEncryptionKeysForUser(_.merge({password: new_password, email: user.email}, authParams), function(newKeys){
var data = {}; var data = {};
@@ -122,7 +145,7 @@ angular.module('app.frontend')
var user = this.user; var user = this.user;
this._performPasswordChange(currentKeys, newKeys, function(response){ this._performPasswordChange(currentKeys, newKeys, function(response){
if(response && !response.errors) { if(response && !response.error) {
// this.showNewPasswordForm = false; // this.showNewPasswordForm = false;
// reencrypt data with new mk // reencrypt data with new mk
this.reencryptAllItemsAndSave(user, newKeys.mk, currentKeys.mk, function(success){ this.reencryptAllItemsAndSave(user, newKeys.mk, currentKeys.mk, function(success){
@@ -149,7 +172,8 @@ angular.module('app.frontend')
this._performPasswordChange = function(email, current_keys, new_keys, callback) { this._performPasswordChange = function(email, current_keys, new_keys, callback) {
var request = Restangular.one("auth"); var request = Restangular.one("auth");
request.user = {password: new_keys.pw, password_confirmation: new_keys.pw, current_password: current_keys.pw, email: email}; var params = {password: new_keys.pw, password_confirmation: new_keys.pw, current_password: current_keys.pw, email: email};
_.merge(request, params);
request.patch().then(function(response){ request.patch().then(function(response){
callback(response); callback(response);
}) })
@@ -417,10 +441,11 @@ angular.module('app.frontend')
localStorage.setItem(key, angular.toJson(value)); localStorage.setItem(key, angular.toJson(value));
} }
this.localUser = function() { this.loadLocalItemsAndUser = function() {
var user = {}; var user = {};
var items = JSON.parse(localStorage.getItem('items')); var items = JSON.parse(localStorage.getItem('items'));
items = this.mapResponseItemsToLocalModels(items); items = this.mapResponseItemsToLocalModels(items);
Item.sortItemsByDate(items);
modelManager.items = items; modelManager.items = items;
user.items = items; user.items = items;
user.shouldMerge = true; user.shouldMerge = true;

View File

@@ -0,0 +1,11 @@
angular.module('app.frontend')
.filter('appDate', function ($filter) {
return function (input) {
return input ? $filter('date')(new Date(input), 'MM/dd/yyyy', 'UTC') : '';
};
})
.filter('appDateTime', function ($filter) {
return function (input) {
return input ? $filter('date')(new Date(input), 'MM/dd/yyyy h:mm a') : '';
};
});

View File

@@ -62,9 +62,7 @@ class ModelManager extends ItemManager {
refreshRelationshipsForTag(tag) { refreshRelationshipsForTag(tag) {
tag.notes = tag.referencesMatchingContentType("Note"); tag.notes = tag.referencesMatchingContentType("Note");
tag.notes.sort(function(a,b){ Item.sortItemsByDate(tag.notes);
return new Date(b.created_at) - new Date(a.created_at);
});
} }
refreshRelationshipsForNote(note) { refreshRelationshipsForNote(note) {

View File

@@ -98,10 +98,20 @@
.item.account { .item.account {
.email {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
}
.links {
margin-bottom: 25px;
}
.link-item { .link-item {
margin-bottom: 8px; margin-bottom: 8px;
a { a {
font-size: 14px; font-size: 12px;
color: #00228f; color: #00228f;
font-weight: bold; font-weight: bold;
} }
@@ -141,16 +151,22 @@
} }
> .action-container { > .action-container {
font-size: 12px; font-size: 12px;
margin-top: 6px; margin-top: 6px;
.status-title {
font-weight: bold;
}
.subtext { .status-title {
font-size: 10px; font-weight: bold;
margin-top: 2px; }
}
.subtext {
font-size: 10px;
margin-top: 2px;
}
a {
display: block;
margin-bottom: -10px;
}
} }
.encryption-confirmation { .encryption-confirmation {
@@ -186,7 +202,6 @@
> .desc { > .desc {
font-size: 12px; font-size: 12px;
margin-top: 3px; margin-top: 3px;
// line-height: 18px;
} }
} }
} }
@@ -198,6 +213,10 @@
} }
} }
a.disabled {
pointer-events: none;
}
.account-form { .account-form {
margin-top: 10px; margin-top: 10px;
} }
@@ -214,8 +233,3 @@
} }
} }
} }
.account-menu {
width: 200px;
}

View File

@@ -71,6 +71,7 @@
> .date { > .date {
font-size: 12px; font-size: 12px;
margin-top: 4px;
} }
&.selected { &.selected {

View File

@@ -50,39 +50,42 @@
{{ctrl.resetData.response}} {{ctrl.resetData.response}}
.account-item{"ng-if" => "ctrl.user.email"} .account-item{"ng-if" => "ctrl.user.email"}
.email {{ctrl.user.email}}
.links{"ng-if" => "ctrl.user.email"}
.link-item
%a{"ng-click" => "ctrl.changePasswordPressed()"} Change Password
%form.account-form{"ng-if" => "ctrl.showNewPasswordForm", 'ng-submit' => 'ctrl.submitPasswordChange()', 'name' => "passwordChangeForm"}
.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'}
.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"}
.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"}
%button.btn.dark-button.btn-block{:type => 'submit', "data-style" => "expand-right", "data-size" => "s", "state" => "buttonState"}
%span.ladda-label Change Password
.panel-status-text{"ng-if" => "ctrl.passwordChangeData.status", "style" => "font-size: 14px;"}
{{ctrl.passwordChangeData.status}}
.link-item
%a{"ng-click" => "ctrl.signOutPressed()"} Sign Out
.meta-container .meta-container
.title Local Encryption .title Local Encryption
.desc Encrypt notes locally before sending to server. Neither the server owner nor an intruder can decrypt your locally encrypted notes. .desc Encrypt notes locally before sending to server. Neither the server owner nor an intruder can decrypt your locally encrypted notes.
.action-container .action-container
%span.status-title Status: enabled. %span.status-title Status:
{{ctrl.encryptionStatusForNotes()}} (shared notes not encrypted) {{ctrl.encryptionStatusForNotes()}} (shared notes not encrypted)
.account-item{"ng-if" => "ctrl.user.email"} .account-item{"ng-if" => "ctrl.user.email"}
.meta-container .meta-container
.title Data Archives .title Data Archives
.desc Note: data archives that you download using the link below are decrypted before save. You should take care to store them in a safe location. .desc Note: data archives that you download using the link below are decrypted before save. You should take care to store them in a safe location.
.action-container .action-container
%a#download-archive{"ng-click" => "ctrl.downloadDataArchive()"} Download Latest Data Archive %a{"ng-click" => "ctrl.downloadDataArchive()"} Download Latest Data Archive
%br
%label#import-archive %label#import-archive
%input{"type" => "file", "style" => "display: none;", "file-change" => "", "handler" => "ctrl.importFileSelected(files)"} %input{"type" => "file", "style" => "display: none;", "file-change" => "->", "handler" => "ctrl.importFileSelected(files)"}
%span %a.disabled
Import Data from Archive %span
Import Data from Archive
.links{"ng-if" => "ctrl.user.email"}
.link-item
%a{"ng-click" => "ctrl.changePasswordPressed()"} Change Password
%form.account-form{"ng-if" => "ctrl.showNewPasswordForm", 'ng-submit' => 'ctrl.submitPasswordChange()', 'name' => "passwordChangeForm"}
.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'}
.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"}
.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"}
%button.btn.dark-button.btn-block{:type => 'submit', "data-style" => "expand-right", "data-size" => "s", "state" => "buttonState"}
%span.ladda-label Change Password
.panel-status-text{"ng-if" => "ctrl.passwordChangeData.status", "style" => "font-size: 14px;"}
{{ctrl.passwordChangeData.status}}
.link-item
%a{"ng-click" => "ctrl.signOutPressed()"} Sign Out
.item .item
%a{"href" => "https://standardnotes.org", "target" => "_blank"} %a{"href" => "https://standardnotes.org", "target" => "_blank"}

View File

@@ -35,4 +35,4 @@
"ng-attr-draggable" => "{{note.dummy ? undefined : 'true'}}", "note" => "note"} "ng-attr-draggable" => "{{note.dummy ? undefined : 'true'}}", "note" => "note"}
.name .name
{{note.content.title}} {{note.content.title}}
.date {{note.created_at || 'Now'}} .date {{(note.created_at | appDateTime) || 'Now'}}

View File

@@ -373,7 +373,7 @@ angular.module('app.frontend', ['ui.router', 'restangular', 'oc.lazyLoad', 'angu
RestangularProvider.setDefaultHeaders({ "Content-Type": "application/json" }); RestangularProvider.setDefaultHeaders({ "Content-Type": "application/json" });
var url = apiControllerProvider.defaultServerURL(); var url = apiControllerProvider.defaultServerURL();
RestangularProvider.setBaseUrl(url); RestangularProvider.setBaseUrl(url + "/api");
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");
@@ -767,9 +767,10 @@ angular.module('app.frontend').controller('BaseCtrl', BaseCtrl);
this.loginData.status = "Generating Login Keys..."; this.loginData.status = "Generating Login Keys...";
$timeout(function () { $timeout(function () {
apiController.login(this.loginData.email, this.loginData.user_password, function (response) { apiController.login(this.loginData.email, this.loginData.user_password, function (response) {
if (response.errors) { if (!response || response.error) {
console.log("login error", response.errors); var error = response ? response.error : { message: "An unknown error occured." };
this.loginData.status = response.errors[0]; this.loginData.status = null;
alert(error.message);
} else { } else {
this.onAuthSuccess(response.user); this.onAuthSuccess(response.user);
} }
@@ -782,8 +783,10 @@ angular.module('app.frontend').controller('BaseCtrl', BaseCtrl);
$timeout(function () { $timeout(function () {
apiController.register(this.loginData.email, this.loginData.user_password, function (response) { apiController.register(this.loginData.email, this.loginData.user_password, function (response) {
if (response.errors) { if (!response || response.error) {
this.loginData.status = response.errors[0]; var error = response ? response.error : { message: "An unknown error occured." };
this.loginData.status = null;
alert(error.message);
} else { } else {
this.onAuthSuccess(response.user); this.onAuthSuccess(response.user);
} }
@@ -873,7 +876,7 @@ angular.module('app.frontend').controller('BaseCtrl', BaseCtrl);
$rootScope.title = "Notes — Standard Notes"; $rootScope.title = "Notes — Standard Notes";
onUserSet(); onUserSet();
} else { } else {
$scope.defaultUser = new User(apiController.localUser()); $scope.defaultUser = new User(apiController.loadLocalItemsAndUser());
onUserSet(); onUserSet();
} }
}); });
@@ -994,7 +997,7 @@ angular.module('app.frontend').controller('BaseCtrl', BaseCtrl);
*/ */
$scope.headerLogout = function () { $scope.headerLogout = function () {
$scope.defaultUser = apiController.localUser(); $scope.defaultUser = apiController.loadLocalItemsAndUser();
$scope.tags = $scope.defaultUser.tags; $scope.tags = $scope.defaultUser.tags;
}; };
}); });
@@ -1279,6 +1282,14 @@ var Item = function () {
_.merge(this, json_obj); _.merge(this, json_obj);
if (this.created_at) {
this.created_at = new Date(this.created_at);
this.updated_at = new Date(this.updated_at);
} else {
this.created_at = new Date();
this.updated_at = new Date();
}
if (!this.uuid) { if (!this.uuid) {
this.uuid = Neeto.crypto.generateUUID(); this.uuid = Neeto.crypto.generateUUID();
} }
@@ -1352,6 +1363,13 @@ var Item = function () {
value: function presentationURL() { value: function presentationURL() {
return this.presentation_url; return this.presentation_url;
} }
}], [{
key: 'sortItemsByDate',
value: function sortItemsByDate(items) {
items.sort(function (a, b) {
return new Date(b.created_at) - new Date(a.created_at);
});
}
}]); }]);
return Item; return Item;
@@ -1536,6 +1554,9 @@ var User = function User(json_obj) {
var request = Restangular.one("auth", "params"); var request = Restangular.one("auth", "params");
request.get({ email: email }).then(function (response) { request.get({ email: email }).then(function (response) {
callback(response.plain()); callback(response.plain());
}).catch(function (response) {
console.log("Error getting current user", response);
callback(response.data);
}); });
}; };
@@ -1551,21 +1572,30 @@ var User = function User(json_obj) {
items = this.mapResponseItemsToLocalModels(items); items = this.mapResponseItemsToLocalModels(items);
var user = _.omit(plain, ["items"]); var user = _.omit(plain, ["items"]);
callback(user, items); callback(user, items);
}.bind(this)).catch(function (error) { }.bind(this)).catch(function (response) {
console.log("Error getting current user", error); console.log("Error getting current user", response);
callback(null); callback(response.data);
}); });
}; };
this.login = function (email, password, callback) { this.login = function (email, password, callback) {
this.getAuthParamsForEmail(email, function (authParams) { this.getAuthParamsForEmail(email, function (authParams) {
if (!authParams) {
callback(null);
return;
}
Neeto.crypto.computeEncryptionKeysForUser(_.merge({ email: email, password: password }, authParams), function (keys) { Neeto.crypto.computeEncryptionKeysForUser(_.merge({ email: email, password: password }, authParams), function (keys) {
this.setMk(keys.mk); this.setMk(keys.mk);
var request = Restangular.one("auth/sign_in"); var request = Restangular.one("auth/sign_in");
request.user = { password: keys.pw, email: email }; console.log("sending pw", keys.pw);
var params = { password: keys.pw, email: email };
_.merge(request, params);
request.post().then(function (response) { request.post().then(function (response) {
localStorage.setItem("jwt", response.token); localStorage.setItem("jwt", response.token);
callback(response); callback(response);
}).catch(function (response) {
console.log(response.data);
callback(response.data);
}); });
}.bind(this)); }.bind(this));
}.bind(this)); }.bind(this));
@@ -1576,16 +1606,24 @@ var User = function User(json_obj) {
this.setMk(keys.mk); this.setMk(keys.mk);
keys.mk = null; keys.mk = null;
var request = Restangular.one("auth"); var request = Restangular.one("auth");
request.user = _.merge({ password: keys.pw, email: email }, keys); var params = _.merge({ password: keys.pw, email: email }, keys);
_.merge(request, params);
request.post().then(function (response) { request.post().then(function (response) {
localStorage.setItem("jwt", response.token); localStorage.setItem("jwt", response.token);
callback(response); callback(response);
}).catch(function (response) {
console.log(response.data);
callback(response.data);
}); });
}.bind(this)); }.bind(this));
}; };
this.changePassword = function (user, current_password, new_password) { this.changePassword = function (user, current_password, new_password) {
this.getAuthParamsForEmail(email, function (authParams) { this.getAuthParamsForEmail(email, function (authParams) {
if (!authParams) {
callback(null);
return;
}
Neeto.crypto.computeEncryptionKeysForUser(_.merge({ password: current_password, email: user.email }, authParams), function (currentKeys) { Neeto.crypto.computeEncryptionKeysForUser(_.merge({ password: current_password, email: user.email }, authParams), function (currentKeys) {
Neeto.crypto.computeEncryptionKeysForUser(_.merge({ password: new_password, email: user.email }, authParams), function (newKeys) { Neeto.crypto.computeEncryptionKeysForUser(_.merge({ password: new_password, email: user.email }, authParams), function (newKeys) {
var data = {}; var data = {};
@@ -1596,7 +1634,7 @@ var User = function User(json_obj) {
var user = this.user; var user = this.user;
this._performPasswordChange(currentKeys, newKeys, function (response) { this._performPasswordChange(currentKeys, newKeys, function (response) {
if (response && !response.errors) { if (response && !response.error) {
// this.showNewPasswordForm = false; // this.showNewPasswordForm = false;
// reencrypt data with new mk // reencrypt data with new mk
this.reencryptAllItemsAndSave(user, newKeys.mk, currentKeys.mk, function (success) { this.reencryptAllItemsAndSave(user, newKeys.mk, currentKeys.mk, function (success) {
@@ -1623,7 +1661,8 @@ var User = function User(json_obj) {
this._performPasswordChange = function (email, current_keys, new_keys, callback) { this._performPasswordChange = function (email, current_keys, new_keys, callback) {
var request = Restangular.one("auth"); var request = Restangular.one("auth");
request.user = { password: new_keys.pw, password_confirmation: new_keys.pw, current_password: current_keys.pw, email: email }; var params = { password: new_keys.pw, password_confirmation: new_keys.pw, current_password: current_keys.pw, email: email };
_.merge(request, params);
request.patch().then(function (response) { request.patch().then(function (response) {
callback(response); callback(response);
}); });
@@ -1882,10 +1921,11 @@ var User = function User(json_obj) {
localStorage.setItem(key, angular.toJson(value)); localStorage.setItem(key, angular.toJson(value));
}; };
this.localUser = function () { this.loadLocalItemsAndUser = function () {
var user = {}; var user = {};
var items = JSON.parse(localStorage.getItem('items')); var items = JSON.parse(localStorage.getItem('items'));
items = this.mapResponseItemsToLocalModels(items); items = this.mapResponseItemsToLocalModels(items);
Item.sortItemsByDate(items);
modelManager.items = items; modelManager.items = items;
user.items = items; user.items = items;
user.shouldMerge = true; user.shouldMerge = true;
@@ -1999,246 +2039,6 @@ var User = function User(json_obj) {
}; };
} }
}); });
;
var ItemManager = function () {
function ItemManager() {
_classCallCheck(this, ItemManager);
}
_createClass(ItemManager, [{
key: 'referencesForItemId',
value: function referencesForItemId(itemId) {
return _.find(this.items, { uuid: itemId });
}
}, {
key: 'resolveReferences',
value: function resolveReferences() {
this.items.forEach(function (item) {
// build out references, safely handle broken references
item.content.references = _.reduce(item.content.references, function (accumulator, reference) {
var item = this.referencesForItemId(reference.uuid);
if (item) {
accumulator.push(item);
}
return accumulator;
}.bind(this), []);
}.bind(this));
}
}, {
key: 'itemsForContentType',
value: function itemsForContentType(contentType) {
return this.items.filter(function (item) {
return item.content_type == contentType;
});
}
}, {
key: 'addItem',
value: function addItem(item) {
this.items.push(item);
}
// returns dirty item references that need saving
}, {
key: 'deleteItem',
value: function deleteItem(item) {
var dirty = [];
_.remove(this.items, item);
var length = item.content.references.length;
// note that references are deleted in this for loop, so you have to be careful how you iterate
for (var i = 0; i < length; i++) {
var referencedItem = item.content.references[0];
// console.log("removing references between items", referencedItem, item);
var _dirty = this.removeReferencesBetweenItems(referencedItem, item);
dirty = dirty.concat(_dirty);
}
return dirty;
}
}, {
key: 'removeReferencesBetweenItems',
value: function removeReferencesBetweenItems(itemOne, itemTwo) {
itemOne.removeReference(itemTwo);
itemTwo.removeReference(itemOne);
return [itemOne, itemTwo];
}
}, {
key: 'createReferencesBetweenItems',
value: function createReferencesBetweenItems(itemOne, itemTwo) {
itemOne.addReference(itemTwo);
itemTwo.addReference(itemOne);
return [itemOne, itemTwo];
}
}, {
key: 'items',
set: function set(items) {
this._items = items;
this.resolveReferences();
},
get: function get() {
return this._items;
}
}]);
return ItemManager;
}();
angular.module('app.frontend').service('itemManager', ItemManager);
;angular.module('app.frontend').service('markdownRenderer', function ($sce) {
marked.setOptions({
breaks: true,
sanitize: true
});
this.renderedContentForText = function (text) {
if (!text || text.length == 0) {
return "";
}
return marked(text);
};
this.renderHtml = function (html_code) {
return $sce.trustAsHtml(html_code);
};
});
;
var ModelManager = function (_ItemManager) {
_inherits(ModelManager, _ItemManager);
function ModelManager() {
_classCallCheck(this, ModelManager);
var _this5 = _possibleConstructorReturn(this, (ModelManager.__proto__ || Object.getPrototypeOf(ModelManager)).call(this));
_this5.notes = [];
_this5.groups = [];
_this5.dirtyItems = [];
return _this5;
}
_createClass(ModelManager, [{
key: 'addDirtyItems',
value: function addDirtyItems(items) {
if (!(items instanceof Array)) {
items = [items];
}
this.dirtyItems = this.dirtyItems.concat(items);
this.dirtyItems = _.uniq(this.dirtyItems);
}
}, {
key: 'clearDirtyItems',
value: function clearDirtyItems() {
this.dirtyItems = [];
}
}, {
key: 'addNote',
value: function addNote(note) {
if (!_.find(this.notes, { uuid: note.uuid })) {
this.notes.unshift(note);
this.addItem(note);
}
}
}, {
key: 'addTag',
value: function addTag(tag) {
this.tags.unshift(tag);
this.addItem(tag);
}
}, {
key: 'addTagToNote',
value: function addTagToNote(tag, note) {
var dirty = this.createReferencesBetweenItems(tag, note);
this.refreshRelationshipsForTag(tag);
this.refreshRelationshipsForNote(note);
this.addDirtyItems(dirty);
}
}, {
key: 'refreshRelationshipsForTag',
value: function refreshRelationshipsForTag(tag) {
tag.notes = tag.referencesMatchingContentType("Note");
tag.notes.sort(function (a, b) {
return new Date(b.created_at) - new Date(a.created_at);
});
}
}, {
key: 'refreshRelationshipsForNote',
value: function refreshRelationshipsForNote(note) {
note.tags = note.referencesMatchingContentType("Tag");
}
}, {
key: 'removeTagFromNote',
value: function removeTagFromNote(tag, note) {
var dirty = this.removeReferencesBetweenItems(tag, note);
this.addDirtyItems(dirty);
}
}, {
key: 'deleteNote',
value: function deleteNote(note) {
var dirty = this.deleteItem(note);
_.remove(this.notes, note);
this.addDirtyItems(dirty);
}
}, {
key: 'deleteTag',
value: function deleteTag(tag) {
var dirty = this.deleteItem(tag);
_.remove(this.tags, tag);
this.addDirtyItems(dirty);
}
}, {
key: 'filteredNotes',
value: function filteredNotes() {
return Note.filterDummyNotes(this.notes);
}
}, {
key: 'items',
set: function set(items) {
_set(ModelManager.prototype.__proto__ || Object.getPrototypeOf(ModelManager.prototype), 'items', items, this);
this.notes = this.itemsForContentType("Note");
this.notes.forEach(function (note) {
note.updateReferencesLocalMapping();
});
this.tags = this.itemsForContentType("Tag");
this.tags.forEach(function (tag) {
tag.updateReferencesLocalMapping();
});
},
get: function get() {
return _get(ModelManager.prototype.__proto__ || Object.getPrototypeOf(ModelManager.prototype), 'items', this);
}
}, {
key: 'filteredNotes',
get: function get() {
return Note.filterDummyNotes(this.notes);
}
}]);
return ModelManager;
}(ItemManager);
angular.module('app.frontend').service('modelManager', ModelManager);
;angular.module('app.frontend').service('serverSideValidation', function ($sce) {
// Show validation errors in form.
this.showErrors = function (formErrors, form) {
angular.forEach(formErrors, function (errors, key) {
if (typeof form[key] !== 'undefined') {
form[key].$setDirty();
form[key].$setValidity('server', false);
form[key].$error.server = $sce.trustAsHtml(errors.join(', '));
}
});
};
// Get validation errors from server response and show them in form.
this.parseErrors = function (response, form) {
if (response.status === 422) {
this.showErrors(response.data, form);
}
};
});
;angular.module('app.frontend').directive('mbAutofocus', ['$timeout', function ($timeout) { ;angular.module('app.frontend').directive('mbAutofocus', ['$timeout', function ($timeout) {
return { return {
restrict: 'A', restrict: 'A',
@@ -2580,6 +2380,253 @@ angular.module('app.frontend').directive('typewrite', ['$timeout', function ($ti
} }
}; };
}]); }]);
;angular.module('app.frontend').filter('appDate', function ($filter) {
return function (input) {
return input ? $filter('date')(new Date(input), 'MM/dd/yyyy', 'UTC') : '';
};
}).filter('appDateTime', function ($filter) {
return function (input) {
return input ? $filter('date')(new Date(input), 'MM/dd/yyyy h:mm a') : '';
};
});
;
var ItemManager = function () {
function ItemManager() {
_classCallCheck(this, ItemManager);
}
_createClass(ItemManager, [{
key: 'referencesForItemId',
value: function referencesForItemId(itemId) {
return _.find(this.items, { uuid: itemId });
}
}, {
key: 'resolveReferences',
value: function resolveReferences() {
this.items.forEach(function (item) {
// build out references, safely handle broken references
item.content.references = _.reduce(item.content.references, function (accumulator, reference) {
var item = this.referencesForItemId(reference.uuid);
if (item) {
accumulator.push(item);
}
return accumulator;
}.bind(this), []);
}.bind(this));
}
}, {
key: 'itemsForContentType',
value: function itemsForContentType(contentType) {
return this.items.filter(function (item) {
return item.content_type == contentType;
});
}
}, {
key: 'addItem',
value: function addItem(item) {
this.items.push(item);
}
// returns dirty item references that need saving
}, {
key: 'deleteItem',
value: function deleteItem(item) {
var dirty = [];
_.remove(this.items, item);
var length = item.content.references.length;
// note that references are deleted in this for loop, so you have to be careful how you iterate
for (var i = 0; i < length; i++) {
var referencedItem = item.content.references[0];
// console.log("removing references between items", referencedItem, item);
var _dirty = this.removeReferencesBetweenItems(referencedItem, item);
dirty = dirty.concat(_dirty);
}
return dirty;
}
}, {
key: 'removeReferencesBetweenItems',
value: function removeReferencesBetweenItems(itemOne, itemTwo) {
itemOne.removeReference(itemTwo);
itemTwo.removeReference(itemOne);
return [itemOne, itemTwo];
}
}, {
key: 'createReferencesBetweenItems',
value: function createReferencesBetweenItems(itemOne, itemTwo) {
itemOne.addReference(itemTwo);
itemTwo.addReference(itemOne);
return [itemOne, itemTwo];
}
}, {
key: 'items',
set: function set(items) {
this._items = items;
this.resolveReferences();
},
get: function get() {
return this._items;
}
}]);
return ItemManager;
}();
angular.module('app.frontend').service('itemManager', ItemManager);
;angular.module('app.frontend').service('markdownRenderer', function ($sce) {
marked.setOptions({
breaks: true,
sanitize: true
});
this.renderedContentForText = function (text) {
if (!text || text.length == 0) {
return "";
}
return marked(text);
};
this.renderHtml = function (html_code) {
return $sce.trustAsHtml(html_code);
};
});
;
var ModelManager = function (_ItemManager) {
_inherits(ModelManager, _ItemManager);
function ModelManager() {
_classCallCheck(this, ModelManager);
var _this5 = _possibleConstructorReturn(this, (ModelManager.__proto__ || Object.getPrototypeOf(ModelManager)).call(this));
_this5.notes = [];
_this5.groups = [];
_this5.dirtyItems = [];
return _this5;
}
_createClass(ModelManager, [{
key: 'addDirtyItems',
value: function addDirtyItems(items) {
if (!(items instanceof Array)) {
items = [items];
}
this.dirtyItems = this.dirtyItems.concat(items);
this.dirtyItems = _.uniq(this.dirtyItems);
}
}, {
key: 'clearDirtyItems',
value: function clearDirtyItems() {
this.dirtyItems = [];
}
}, {
key: 'addNote',
value: function addNote(note) {
if (!_.find(this.notes, { uuid: note.uuid })) {
this.notes.unshift(note);
this.addItem(note);
}
}
}, {
key: 'addTag',
value: function addTag(tag) {
this.tags.unshift(tag);
this.addItem(tag);
}
}, {
key: 'addTagToNote',
value: function addTagToNote(tag, note) {
var dirty = this.createReferencesBetweenItems(tag, note);
this.refreshRelationshipsForTag(tag);
this.refreshRelationshipsForNote(note);
this.addDirtyItems(dirty);
}
}, {
key: 'refreshRelationshipsForTag',
value: function refreshRelationshipsForTag(tag) {
tag.notes = tag.referencesMatchingContentType("Note");
Item.sortItemsByDate(tag.notes);
}
}, {
key: 'refreshRelationshipsForNote',
value: function refreshRelationshipsForNote(note) {
note.tags = note.referencesMatchingContentType("Tag");
}
}, {
key: 'removeTagFromNote',
value: function removeTagFromNote(tag, note) {
var dirty = this.removeReferencesBetweenItems(tag, note);
this.addDirtyItems(dirty);
}
}, {
key: 'deleteNote',
value: function deleteNote(note) {
var dirty = this.deleteItem(note);
_.remove(this.notes, note);
this.addDirtyItems(dirty);
}
}, {
key: 'deleteTag',
value: function deleteTag(tag) {
var dirty = this.deleteItem(tag);
_.remove(this.tags, tag);
this.addDirtyItems(dirty);
}
}, {
key: 'filteredNotes',
value: function filteredNotes() {
return Note.filterDummyNotes(this.notes);
}
}, {
key: 'items',
set: function set(items) {
_set(ModelManager.prototype.__proto__ || Object.getPrototypeOf(ModelManager.prototype), 'items', items, this);
this.notes = this.itemsForContentType("Note");
this.notes.forEach(function (note) {
note.updateReferencesLocalMapping();
});
this.tags = this.itemsForContentType("Tag");
this.tags.forEach(function (tag) {
tag.updateReferencesLocalMapping();
});
},
get: function get() {
return _get(ModelManager.prototype.__proto__ || Object.getPrototypeOf(ModelManager.prototype), 'items', this);
}
}, {
key: 'filteredNotes',
get: function get() {
return Note.filterDummyNotes(this.notes);
}
}]);
return ModelManager;
}(ItemManager);
angular.module('app.frontend').service('modelManager', ModelManager);
;angular.module('app.frontend').service('serverSideValidation', function ($sce) {
// Show validation errors in form.
this.showErrors = function (formErrors, form) {
angular.forEach(formErrors, function (errors, key) {
if (typeof form[key] !== 'undefined') {
form[key].$setDirty();
form[key].$setValidity('server', false);
form[key].$error.server = $sce.trustAsHtml(errors.join(', '));
}
});
};
// Get validation errors from server response and show them in form.
this.parseErrors = function (response, form) {
if (response.status === 422) {
this.showErrors(response.data, form);
}
};
});
},{}]},{},[1]); },{}]},{},[1]);

File diff suppressed because one or more lines are too long