diff --git a/.idea/deployment.xml b/.idea/deployment.xml
new file mode 100644
index 0000000..0f6bfd7
--- /dev/null
+++ b/.idea/deployment.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/api/api.php b/api/api.php
index f46c75b..a728379 100644
--- a/api/api.php
+++ b/api/api.php
@@ -3,6 +3,7 @@ require_once('lib/Slim/Slim.php');
require_once('lib/rb.php');
require_once('lib/password.php');
require_once('lib/JWT.php');
+require_once('lib/PHPMailer/PHPMailerAutoload.php');
require_once('jsonResponse.php');
@@ -51,6 +52,8 @@ $app->get('/authenticate', function() use($app, $jsonResponse) {
$app->response->setBody($jsonResponse->asJson());
});
+require_once('mailFactory.php');
+
require_once('userRoutes.php');
require_once('boardRoutes.php');
require_once('itemRoutes.php');
diff --git a/api/boardRoutes.php b/api/boardRoutes.php
index 35fea6f..63f2179 100644
--- a/api/boardRoutes.php
+++ b/api/boardRoutes.php
@@ -19,6 +19,15 @@ $app->post('/boards', function() use($app, $jsonResponse) {
logAction($actor->username . ' added board ' . $board->name, null, $board->export());
$jsonResponse->addBeans(getBoards());
$jsonResponse->addAlert('success', 'New board ' . $board->name . ' created.');
+
+ foreach($board->sharedUser as $user) {
+ $body = getNewBoardEmailBody($board->id, $user->username, $board->name);
+ $subject = 'New board created!';
+ $recipient = $user->username;
+ $email = $user->email;
+
+ sendEmail($email, $recipient, $subject, $body);
+ }
}
$app->response->setBody($jsonResponse->asJson());
});
@@ -37,6 +46,15 @@ $app->post('/boards/update', function() use($app, $jsonResponse) {
logAction($actor->username . ' updated board ' . $board->name, $before, $board->export());
}
$jsonResponse->addBeans(getBoards());
+
+ foreach($board->sharedUser as $user) {
+ $body = geEditBoardEmailBody($board->id, $user->username, $board->name);
+ $subject = 'Board updated!';
+ $recipient = $user->username;
+ $email = $user->email;
+
+ sendEmail($email, $recipient, $subject, $body);
+ }
}
$app->response->setBody($jsonResponse->asJson());
});
diff --git a/api/helpers.php b/api/helpers.php
index 473d11c..d332420 100644
--- a/api/helpers.php
+++ b/api/helpers.php
@@ -66,6 +66,30 @@ function getUser() {
return null;
}
+function getUserByID($id) {
+ try {
+ $user = R::load('user', $id);
+
+ if ($user->id) {
+ return $user;
+ }
+ } catch (Exception $e) {}
+
+ return null;
+}
+
+function getLaneByID($id) {
+ try {
+ $lane = R::load('lane', $id);
+
+ if ($lane->id) {
+ return $lane;
+ }
+ } catch (Exception $e) {}
+
+ return null;
+}
+
// Get all users.
function getUsers($sanitize = true) {
try {
@@ -240,6 +264,21 @@ function updateUsername($user, $data) {
return $user;
}
+// Change email if available.
+function updateEmail($user, $data) {
+ global $jsonResponse;
+ $emailTaken = R::findOne('user', ' username = ?', [$data->newEmail]);
+
+ if (null != $user && null == $emailTaken) {
+ $user->email = $data->newEmail;
+ $jsonResponse->addAlert('success', 'Email updated.');
+ } else {
+ $jsonResponse->addAlert('error', 'Email already in use.');
+ }
+
+ return $user;
+}
+
// Validate a provided JWT.
function validateToken($requireAdmin = false) {
global $jsonResponse, $app;
@@ -333,6 +372,7 @@ function createInitialUser() {
$admin->defaultBoard = null;
$admin->salt = password_hash($admin->username . time(), PASSWORD_BCRYPT);
$admin->password = password_hash('admin', PASSWORD_BCRYPT, array('salt' => $admin->salt));
+ $admin->email = '';
R::store($admin);
}
diff --git a/api/itemRoutes.php b/api/itemRoutes.php
index 4ca2b75..4baa3b9 100644
--- a/api/itemRoutes.php
+++ b/api/itemRoutes.php
@@ -29,6 +29,27 @@ $app->post('/boards/:id/items', function($id) use($app, $jsonResponse) {
} else {
$jsonResponse->addAlert('error', 'Failed to create board item.');
}
+
+ foreach($board->sharedUser as $user) {
+ $actor = getUser();
+ $body = getNewItemEmailBody(
+ $board->id,
+ $actor->username,
+ $board->name,
+ $item->title,
+ $item->description,
+ getUserByID($item->assignee)->username,
+ $item->category,
+ $item->dueDate,
+ $item->points,
+ $item->position
+ );
+ $subject = 'New item created!';
+ $recipient = $user->username;
+ $email = $user->email;
+
+ sendEmail($email, $recipient, $subject, $body);
+ }
}
}
$app->response->setBody($jsonResponse->asJson());
@@ -62,6 +83,30 @@ $app->post('/items/:itemId', function($itemId) use ($app, $jsonResponse) {
logAction($user->username . ' updated item ' . $item->title, $before, $item->export(), $itemId);
$jsonResponse->addAlert('success', 'Updated item ' . $item->title . '.');
$jsonResponse->addBeans(getBoards());
+
+ $lane = R::load('lane', $item->lane_id);
+ $board = R::load('board', $lane->boardId);
+
+ foreach($board->sharedUser as $user) {
+ $actor = getUser();
+ $body = getEditItemEmailBody(
+ $board->id,
+ $actor->username,
+ $board->name,
+ $item->title,
+ $item->description,
+ getUserByID($item->assignee)->username,
+ $item->category,
+ $item->dueDate,
+ $item->points,
+ $item->position
+ );
+ $subject = 'Item edited';
+ $recipient = $user->username;
+ $email = $user->email;
+
+ sendEmail($email, $recipient, $subject, $body);
+ }
}
}
$app->response->setBody($jsonResponse->asJson());
@@ -123,6 +168,24 @@ $app->post('/items/:itemId/comment', function($itemId) use ($app, $jsonResponse)
logAction($user->username . ' added a comment to item ' . $item->title, null, $comment->export(), $itemId);
$jsonResponse->addAlert('success', 'Comment added to item ' . $item->title . '.');
$jsonResponse->addBeans(R::load('item', $itemId));
+
+ $lane = R::load('lane', $item->lane_id);
+ $board = R::load('board', $lane->boardId);
+
+ foreach($board->sharedUser as $user) {
+ $body = getNewCommentEmailBody(
+ $board->id,
+ $user->username,
+ $board->name,
+ $item->title,
+ $comment->text
+ );
+ $subject = 'New comment';
+ $recipient = $user->username;
+ $email = $user->email;
+
+ sendEmail($email, $recipient, $subject, $body);
+ }
}
}
$app->response->setBody($jsonResponse->asJson());
@@ -146,6 +209,25 @@ $app->post('/comments/:commentId', function($commentId) use ($app, $jsonResponse
logAction($user->username . ' edited comment ' . $comment->id, $before, $comment->export(), $comment->id);
$jsonResponse->addAlert('success', 'Comment edited.');
$jsonResponse->addBeans(R::load('item', $comment->item_id));
+
+ $item = R::load('item', $comment->item_id);
+ $lane = R::load('lane', $item->lane_id);
+ $board = R::load('board', $lane->boardId);
+
+ foreach($board->sharedUser as $user) {
+ $body = getEditCommentEmailBody(
+ $board->id,
+ $user->username,
+ $board->name,
+ $item->title,
+ $comment->text
+ );
+ $subject = 'Edit comment';
+ $recipient = $user->username;
+ $email = $user->email;
+
+ sendEmail($email, $recipient, $subject, $body);
+ }
}
$app->response->setBody($jsonResponse->asJson());
})->conditions(['commentId' => '\d+']);
diff --git a/api/mailConfig.php b/api/mailConfig.php
new file mode 100644
index 0000000..2b13ece
--- /dev/null
+++ b/api/mailConfig.php
@@ -0,0 +1,9 @@
+isSMTP();
+ $mail->Host = $MAIL_HOST;
+ $mail->SMTPAuth = true;
+ $mail->Username = $MAIL_USERNAME;
+ $mail->Password = $MAIL_PASSWORD;
+ $mail->SMTPSecure = $MAIL_SMTPSECURE;
+ $mail->Port = $MAIL_PORT;
+
+ $mail->From = $MAIL_FROM;
+ $mail->FromName = $MAIL_FROMNAME;
+
+ return $mail;
+}
+
+function sendEmail($email, $recipient, $subject, $body) {
+ $mail = createMailObject();
+ $mail->addAddress($email, $recipient); // Add a recipient
+ $mail->isHTML(true);
+
+ $mail->Subject = $subject;
+ $mail->Body = $body;
+
+ $mail->send();
+}
+
+function getNewBoardEmailBody($boardid, $username, $boardname) {
+ $message = file_get_contents('mail_templates/newBoard.html');
+ $message = str_replace('%boardid%', $boardid, $message);
+ $message = str_replace('%username%', $username, $message);
+ $message = str_replace('%boardname%', $boardname, $message);
+ return $message;
+}
+
+function getEditBoardEmailBody($boardid, $username, $boardname) {
+ $message = file_get_contents('mail_templates/editBoard.html');
+ $message = str_replace('%boardid%', $boardid, $message);
+ $message = str_replace('%username%', $username, $message);
+ $message = str_replace('%boardname%', $boardname, $message);
+ return $message;
+}
+
+function getNewItemEmailBody($boardid, $username, $boardname, $title, $description, $assignee, $category, $dueDate, $points, $position)
+{
+ $message = file_get_contents('mail_templates/newItem.html');
+ $message = str_replace('%boardid%', $boardid, $message);
+ $message = str_replace('%username%', $username, $message);
+ $message = str_replace('%boardname%', $boardname, $message);
+ $message = str_replace('%title%', $title, $message);
+ $message = str_replace('%description%', $description, $message);
+ $message = str_replace('%assignee%', $assignee, $message);
+ $message = str_replace('%category%', $category, $message);
+ $message = str_replace('%duedate%', $dueDate, $message);
+ $message = str_replace('%points%', $points, $message);
+ $message = str_replace('%position%', $position, $message);
+ return $message;
+}
+
+function getEditItemEmailBody($boardid, $username, $boardname, $title, $description, $assignee, $category, $dueDate, $points, $position)
+{
+ $message = file_get_contents('mail_templates/editItem.html');
+ $message = str_replace('%boardid%', $boardid, $message);
+ $message = str_replace('%username%', $username, $message);
+ $message = str_replace('%boardname%', $boardname, $message);
+ $message = str_replace('%title%', $title, $message);
+ $message = str_replace('%description%', $description, $message);
+ $message = str_replace('%assignee%', $assignee, $message);
+ $message = str_replace('%category%', $category, $message);
+ $message = str_replace('%duedate%', $dueDate, $message);
+ $message = str_replace('%points%', $points, $message);
+ $message = str_replace('%position%', $position, $message);
+ return $message;
+}
+
+function getNewCommentEmailBody($boardid, $username, $boardname, $title, $comment)
+{
+ $message = file_get_contents('mail_templates/newComment.html');
+ $message = str_replace('%boardid%', $boardid, $message);
+ $message = str_replace('%username%', $username, $message);
+ $message = str_replace('%boardname%', $boardname, $message);
+ $message = str_replace('%title%', $title, $message);
+ $message = str_replace('%comment%', $comment, $message);
+ return $message;
+}
+
+function getEditCommentEmailBody($boardid, $username, $boardname, $title, $comment)
+{
+ $message = file_get_contents('mail_templates/editComment.html');
+ $message = str_replace('%boardid%', $boardid, $message);
+ $message = str_replace('%username%', $username, $message);
+ $message = str_replace('%boardname%', $boardname, $message);
+ $message = str_replace('%title%', $title, $message);
+ $message = str_replace('%comment%', $comment, $message);
+ return $message;
+}
\ No newline at end of file
diff --git a/api/mail_templates/editBoard.html b/api/mail_templates/editBoard.html
new file mode 100644
index 0000000..4c476cb
--- /dev/null
+++ b/api/mail_templates/editBoard.html
@@ -0,0 +1,7 @@
+
+
+
+%username% updated board %boardname%!
+Navigate to board!
+
+
\ No newline at end of file
diff --git a/api/mail_templates/editComment.html b/api/mail_templates/editComment.html
new file mode 100644
index 0000000..787ef01
--- /dev/null
+++ b/api/mail_templates/editComment.html
@@ -0,0 +1,10 @@
+
+
+
+%username% edited comment at board %boardname%:
+%title%
+%comment%
+
+Navigate to board!
+
+
\ No newline at end of file
diff --git a/api/mail_templates/editItem.html b/api/mail_templates/editItem.html
new file mode 100644
index 0000000..159e8a3
--- /dev/null
+++ b/api/mail_templates/editItem.html
@@ -0,0 +1,15 @@
+
+
+
+%username% edited item at board %boardname%:
+%title%
+%description%
+%duedate%
+%assignee%
+%category%
+%points%
+%position%
+Navigate to board!
+
+
+
\ No newline at end of file
diff --git a/api/mail_templates/newBoard.html b/api/mail_templates/newBoard.html
new file mode 100644
index 0000000..f593e8b
--- /dev/null
+++ b/api/mail_templates/newBoard.html
@@ -0,0 +1,7 @@
+
+
+
+%username% created new board %boardname%!
+Navigate to board!
+
+
\ No newline at end of file
diff --git a/api/mail_templates/newComment.html b/api/mail_templates/newComment.html
new file mode 100644
index 0000000..41561df
--- /dev/null
+++ b/api/mail_templates/newComment.html
@@ -0,0 +1,10 @@
+
+
+
+%username% created new comment at board %boardname%:
+%title%
+%comment%
+
+Navigate to board!
+
+
\ No newline at end of file
diff --git a/api/mail_templates/newItem.html b/api/mail_templates/newItem.html
new file mode 100644
index 0000000..78e047f
--- /dev/null
+++ b/api/mail_templates/newItem.html
@@ -0,0 +1,15 @@
+
+
+
+%username% created new item at board %boardname%:
+%title%
+%description%
+%duedate%
+%assignee%
+%category%
+%points%
+%position%
+Navigate to board!
+
+
+
\ No newline at end of file
diff --git a/api/userRoutes.php b/api/userRoutes.php
index b0fcc7d..811cdfd 100644
--- a/api/userRoutes.php
+++ b/api/userRoutes.php
@@ -86,6 +86,24 @@ $app->post('/updateusername', function() use($app, $jsonResponse) {
$app->response->setBody($jsonResponse->asJson());
});
+// Update current user's email if not taken.
+$app->post('/updateemail', function() use($app, $jsonResponse) {
+ $data = json_decode($app->environment['slim.input']);
+
+ if (validateToken()) {
+ $user = getUser();
+ $before = $user->export();
+ $user = updateEmail($user, $data);
+ R::store($user);
+
+ if ($jsonResponse->alerts[0]['type'] == 'success') {
+ logAction($before['username'] . ' changed email to ' . $user->email, $before, $user->export());
+ }
+ $jsonResponse->addBeans(getUsers());
+ }
+ $app->response->setBody($jsonResponse->asJson());
+});
+
// Update current user's default board.
$app->post('/updateboard', function() use($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
@@ -123,6 +141,7 @@ $app->get('/users/current', function() use($app, $jsonResponse) {
'userId' => $user->id,
'username' => $user->username,
'isAdmin' => $user->isAdmin,
+ 'email' => $user->email,
'defaultBoard' => $user->defaultBoard
];
}
@@ -151,6 +170,7 @@ $app->post('/users', function() use($app, $jsonResponse) {
$user = R::dispense('user');
$user->username = $data->username;
$user->isAdmin = $data->isAdmin;
+ $user->email = $data->email;
$user->defaultBoard = $data->defaultBoard;
$user->salt = password_hash($data->username . time(), PASSWORD_BCRYPT);
$user->password = password_hash($data->password, PASSWORD_BCRYPT, array('salt' => $user->salt));
@@ -187,6 +207,7 @@ $app->post('/users/update', function() use($app, $jsonResponse) {
$user->password = password_hash($data->password, PASSWORD_BCRYPT, array('salt' => $user->salt));
}
$user->isAdmin = $data->isAdmin;
+ $user->email = $data->email;
$user->defaultBoard = $data->defaultBoard;
R::store($user);
diff --git a/js/controllers/settingsUser.js b/js/controllers/settingsUser.js
index 88303d1..b22b9e5 100644
--- a/js/controllers/settingsUser.js
+++ b/js/controllers/settingsUser.js
@@ -117,6 +117,40 @@ function ($scope, $interval, UserService) {
}
};
+ $scope.emailFormData = {
+ newEmail: '',
+ emailError: false,
+ isSaving: false,
+ setAlert: function(message) {
+ this.isSaving = false;
+ this.emailError = true;
+ $scope.alerts.showAlert({ 'type': 'error', 'text': message });
+ },
+ reset: function() {
+ this.newEmail = '';
+ this.emailError = false;
+ this.isSaving = false;
+ }
+ };
+ $scope.changeEmail = function(newEmailFormData) {
+ $scope.emailFormData.isSaving = true;
+
+ if (newEmailFormData.newEmail === '') {
+ newEmailFormData.setAlert('Email cannot be blank.');
+ newEmailFormData.isSaving = false;
+ } else {
+ UserService.changeEmail(newEmailFormData.newEmail)
+ .success(function(data) {
+ $scope.alerts.showAlerts(data.alerts);
+ $scope.updateUsers(data.data);
+ $scope.loadCurrentUser();
+
+ newEmailFormData.isSaving = false;
+ newEmailFormData.newUsername = '';
+ });
+ }
+ };
+
$scope.updatingDefaultBoard = false;
$scope.setDefaultBoard = function() {
$scope.updatingDefaultBoard = true;
diff --git a/js/controllers/settingsUserForm.js b/js/controllers/settingsUserForm.js
index 49090a2..7599b2e 100644
--- a/js/controllers/settingsUserForm.js
+++ b/js/controllers/settingsUserForm.js
@@ -7,11 +7,13 @@ function ($scope, UserService) {
isAdd: true,
username: '',
password: '',
+ email: '',
verifyPass: '',
defaultBoard: null,
isAdmin: false,
passError: false,
usernameError: false,
+ emailError: false,
isSaving: false,
setUser: function(user) {
this.reset();
@@ -19,6 +21,7 @@ function ($scope, UserService) {
this.isAdd = false;
this.userId = user.id;
this.username = user.username;
+ this.email = user.email;
this.defaultBoard = user.default_board;
this.isAdmin = user.is_admin == '1';
},
@@ -29,6 +32,7 @@ function ($scope, UserService) {
this.isAdd = true;
this.username = '';
this.password = '';
+ this.email = '';
this.verifyPass = '';
this.defaultBoard = null;
this.isAdmin = false;
diff --git a/js/services/user.js b/js/services/user.js
index a97cad9..891b47f 100644
--- a/js/services/user.js
+++ b/js/services/user.js
@@ -35,6 +35,12 @@ function($http) {
});
},
+ changeEmail: function(newEmail) {
+ return $http.post('api/updateemail', {
+ newEmail: newEmail
+ });
+ },
+
changeDefaultBoard: function(newDefaultBoard) {
return $http.post('api/updateboard', {
defaultBoard: newDefaultBoard
@@ -53,6 +59,7 @@ function($http) {
return $http.post('api/users', {
username: formData.username,
password: formData.password,
+ email: formData.email,
defaultBoard: formData.defaultBoard,
boardAccess: formData.boardAccess,
isAdmin: formData.isAdmin
@@ -64,6 +71,7 @@ function($http) {
userId: formData.userId,
newUsername: formData.username,
password: formData.password,
+ email: formData.email,
defaultBoard: formData.defaultBoard,
boardAccess: formData.boardAccess,
isAdmin: formData.isAdmin
diff --git a/partials/settingsModifyUser.html b/partials/settingsModifyUser.html
index dd438f2..cc15301 100644
--- a/partials/settingsModifyUser.html
+++ b/partials/settingsModifyUser.html
@@ -41,4 +41,16 @@
+
diff --git a/partials/settingsUserModal.html b/partials/settingsUserModal.html
index 8aaeba1..ffd5a0a 100644
--- a/partials/settingsUserModal.html
+++ b/partials/settingsUserModal.html
@@ -23,6 +23,10 @@
+
+
Change Email
+
+
Select Default Board
- User | Admin | Default Board | Actions |
+ User | Email | Admin | Default Board | Actions |
@@ -15,6 +15,7 @@
{{ user.username }} |
+ {{ user.email }} |
|