diff --git a/VERSION b/VERSION index 8e27bef..3e2c625 100644 --- a/VERSION +++ b/VERSION @@ -1,7 +1,17 @@ -v0.2.5 +v0.2.6 Changelog +<<<<<<< HEAD + * Settings Updates + * Choose to hide items on filter + * Choose boards when creating/editing a user + * Comments can be edited + * Dates show up on comments and attachments without reload + * Opening login page redirects to boards if a valid token is found + +======= * Markdown support in comments * Cursor on default input field * Show app version +>>>>>>> master diff --git a/api/itemRoutes.php b/api/itemRoutes.php index 52f4ca3..4ca2b75 100644 --- a/api/itemRoutes.php +++ b/api/itemRoutes.php @@ -120,7 +120,7 @@ $app->post('/items/:itemId/comment', function($itemId) use ($app, $jsonResponse) $item->ownComment[] = $comment; R::store($item); - logAction($user->username . ' added a comment to item ' . $item->title, null, $comment, $itemId); + 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)); } @@ -128,6 +128,28 @@ $app->post('/items/:itemId/comment', function($itemId) use ($app, $jsonResponse) $app->response->setBody($jsonResponse->asJson()); })->conditions(['itemId' => '\d+']); +// Update an existing comment +$app->post('/comments/:commentId', function($commentId) use ($app, $jsonResponse) { + $data = json_decode($app->environment['slim.input']); + + if(validateToken()) { + $user = getUser(); + $comment = R::load('comment', $commentId); + $before = $comment->export(); + + $comment->text = $data->text; + $comment->userId = $user->id; + $comment->timestamp = time(); + $comment->isEdited = true; + R::store($comment); + + 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)); + } + $app->response->setBody($jsonResponse->asJson()); +})->conditions(['commentId' => '\d+']); + // Remove a comment from an item. $app->post('/items/:itemId/comment/remove', function($itemId) use ($app, $jsonResponse) { $data = json_decode($app->environment['slim.input']); diff --git a/api/userRoutes.php b/api/userRoutes.php index 71d9929..5e23f4f 100644 --- a/api/userRoutes.php +++ b/api/userRoutes.php @@ -157,6 +157,9 @@ $app->post('/users', function() use($app, $jsonResponse) { R::store($user); addUserToBoard($data->defaultBoard, $user); + foreach($data->boardAccess as $board) { + addUserToBoard($board, $user); + } $actor = getUser(); logAction($actor->username . ' added user ' . $user->username, null, $user->export()); @@ -188,6 +191,9 @@ $app->post('/users/update', function() use($app, $jsonResponse) { R::store($user); addUserToBoard($data->defaultBoard, $user); + foreach($data->boardAccess as $board) { + addUserToBoard($board, $user); + } logAction($actor->username . ' updated user ' . $user->username, $before, $user->export()); $jsonResponse->addAlert('success', 'User updated.'); diff --git a/css/styles.css b/css/styles.css index 6ec8df1..4536437 100644 --- a/css/styles.css +++ b/css/styles.css @@ -46,7 +46,9 @@ a:hover.fa { .datePast { text-shadow: 0px 0px 3px rgba(255, 81, 28, 1); } - +.unbold { + font-weight: normal; +} .fa-edit, .fa-trash-o { cursor: pointer; } @@ -367,6 +369,13 @@ span.fa { .view-item-comment-form { overflow: hidden; } +.edit-item-comment-form { + overflow: hidden; +} +.edit-item-comment-form button { + margin-left: 5px; + margin-top: 5px; +} .detail { display: block; font-size: 80%; @@ -382,6 +391,19 @@ p.detail { .links a { margin-left: 3px; } +.commentText { + display: inline-block; + width: 95%; + overflow: hidden; +} +.commentActions { + position: absolute; + top: .8em; + right: .4em; +} +.commentActions a { + margin-left: 3px; +} #add-comment { margin-top: 5px; } diff --git a/js/app.js b/js/app.js index 4012ea4..6757d85 100644 --- a/js/app.js +++ b/js/app.js @@ -62,6 +62,9 @@ function($rootScope, $location, $window, AuthenticationService) { !$window.localStorage.token) { $location.path('/'); } + if (nextRoute !== null && nextRoute.controller === 'LoginCtrl' && $window.localStorage.token) { + $location.path('/boards'); + } }); $rootScope.$on('$routeChangeSuccess', function(event, route, previousRoute) { diff --git a/js/controllers/boards.js b/js/controllers/boards.js index c7e6648..678be7e 100644 --- a/js/controllers/boards.js +++ b/js/controllers/boards.js @@ -28,7 +28,8 @@ function ($scope, $routeParams, $location, $interval, $window, $scope.boardId = $routeParams.boardId; $scope.filter = { user: null, - category: null + category: null, + hide: false }; $scope.filterChanged = function() { diff --git a/js/controllers/boardsItemView.js b/js/controllers/boardsItemView.js index cce27d9..10dd8b0 100644 --- a/js/controllers/boardsItemView.js +++ b/js/controllers/boardsItemView.js @@ -2,6 +2,7 @@ taskBoardControllers.controller('ItemViewBoardCtrl', ['$scope', '$window', 'BoardService', function ($scope, $window, BoardService) { $scope.viewItem = {}; + $scope.comment = {}; $scope.toggle = { sidebar: false }; @@ -38,6 +39,9 @@ function ($scope, $window, BoardService) { updateItem = function(item) { $scope.viewItem = item; $scope.viewItem.laneName = $scope.laneNames[item.lane_id]; + convertDates($scope.viewItem.ownComment); + convertDates($scope.viewItem.ownAttachment); + convertDates($scope.viewItem.ownActivity); }; $scope.openItem = function(item, openModal) { @@ -54,9 +58,6 @@ function ($scope, $window, BoardService) { $scope.viewItem.ownAttachment = []; } - convertDates($scope.viewItem.ownComment); - convertDates($scope.viewItem.ownAttachment); - convertDates($scope.viewItem.ownActivity); $scope.fileReset = true; if (openModal) { @@ -179,7 +180,7 @@ function ($scope, $window, BoardService) { $scope.addItemComment = function(comment) { if (comment === "" || undefined === comment) { - $scope.alerts.showAlert({ type: 'error', text:'Comment cannot be empty.' }); + $scope.alerts.showAlert({ type: 'error', text: 'Comment cannot be empty.' }); return; } @@ -194,6 +195,28 @@ function ($scope, $window, BoardService) { $scope.comment.text = ""; }; + $scope.beginEditComment = function(commentId, comment) { + $scope.comment.isEdit = true; + $scope.comment.editText = comment; + $scope.comment.id = commentId; + }; + + $scope.editComment = function(commentId, comment) { + $scope.comment.isEdit = false; + if (comment === '' || undefined === comment) { + $scope.alerts.showAlert({ type: 'error', text: 'Comment cannot be empty.' }); + return; + } + + $scope.viewItem.disabled = true; + BoardService.updateItemComment(commentId, comment) + .success(function(data) { + updateItem(data.data[0]); + $scope.loadBoards(); + $scope.viewItem.disabled = false; + }); + }; + $scope.addItemAttachment = function() { if ($scope.itemUpload === undefined || $scope.itemUpload.name === '') { $scope.alerts.showAlert({ type: 'error', text: 'Select a file before uploading.' }); diff --git a/js/controllers/settingsAutoActions.js b/js/controllers/settingsAutoActions.js index 6c84f69..7a421d5 100644 --- a/js/controllers/settingsAutoActions.js +++ b/js/controllers/settingsAutoActions.js @@ -165,9 +165,9 @@ function ($scope, $interval, BoardService) { .success(function(data) { updateAutoActions(data.data); $scope.loadingActions = false; - $scope.loadActions(); }); }; + $interval($scope.loadActions, 2000); // Wait until boards are loaded to load the actions. $scope.$watch('loadingBoards', function() { diff --git a/js/services/board.js b/js/services/board.js index 902bf2d..994f8b1 100644 --- a/js/services/board.js +++ b/js/services/board.js @@ -102,6 +102,12 @@ function($http) { }); }, + updateItemComment: function(commentId, comment) { + return $http.post('api/comments/' + commentId, { + text: comment + }); + }, + removeItemComment: function(itemId, commentId) { return $http.post('api/items/' + itemId + '/comment/remove', { id: commentId diff --git a/js/services/user.js b/js/services/user.js index 080cc9b..a97cad9 100644 --- a/js/services/user.js +++ b/js/services/user.js @@ -54,6 +54,7 @@ function($http) { username: formData.username, password: formData.password, defaultBoard: formData.defaultBoard, + boardAccess: formData.boardAccess, isAdmin: formData.isAdmin }); }, @@ -64,6 +65,7 @@ function($http) { newUsername: formData.username, password: formData.password, defaultBoard: formData.defaultBoard, + boardAccess: formData.boardAccess, isAdmin: formData.isAdmin }); }, diff --git a/partials/board.html b/partials/board.html index 01e0769..c36961b 100644 --- a/partials/board.html +++ b/partials/board.html @@ -7,6 +7,9 @@

+ User Filter: + + + +

Add Comment

-
diff --git a/partials/settingsUserModal.html b/partials/settingsUserModal.html index fe16c19..8aaeba1 100644 --- a/partials/settingsUserModal.html +++ b/partials/settingsUserModal.html @@ -35,6 +35,11 @@
+
+
Board Access
+ +
diff --git a/readme.md b/readme.md index a0f78dd..bbf440b 100644 --- a/readme.md +++ b/readme.md @@ -27,6 +27,8 @@ The goal of TaskBoard is to provide a simple and clean interface to a functional ###Requirements A web server running PHP with sqlite enabled. Developed and tested under Apache2 running PHP 5.5+. +The server must have `sqlite` and `php5-sqlite` installed, as well as the `rewrite` and `expires` Apache modules. + ###Install Installing TaskBoard is as easy as 1, 2, 3! @@ -97,11 +99,12 @@ Count was done from parent directory of TaskBoard as `./cloc-1.62.pl TaskBoard - Language | Files | Blank Lines | Comments | Code -------------------|-------:|-------------:|---------:|---------: -Javascript | 22 | 187 | 34 | 1853 -HTML | 17 | 7 | 8 | 936 -PHP | 6 | 150 | 57 | 849 -CSS | 1 | 12 | 33 | 609 +Javascript | 23 | 194 | 34 | 1915 +HTML | 17 | 10 | 10 | 1015 +PHP | 6 | 156 | 58 | 875 +CSS | 1 | 11 | 33 | 636 Bourne Again Shell | 4 | 10 | 0 | 53 -__SUM:__ | __50__ | __366__ | __132__ | __4300__ +XML | 1 | 0 | 0 | 12 +__SUM:__ | __52__ | __381__ | __135__ | __4506__ -Counts Last Updated: Oct. 18, 2014 +Counts Last Updated: Nov. 10, 2014