commit
dd0597e18d
16
VERSION
16
VERSION
@ -1,10 +1,12 @@
|
|||||||
v0.2.6
|
v0.2.7
|
||||||
|
|
||||||
Changelog
|
Changelog
|
||||||
|
|
||||||
* Settings Updates
|
* Additional way to dismiss Activity Log in Settings
|
||||||
* Choose to hide items on filter
|
* Image attachments resize to fit preview pane
|
||||||
* Choose boards when creating/editing a user
|
* Uploaded files show username immediately
|
||||||
* Comments can be edited
|
* Items can be moved to collapsed columns with new context menu item
|
||||||
* Dates show up on comments and attachments without reload
|
* Automatic actions bugfix.
|
||||||
* Opening login page redirects to boards if a valid token is found
|
* The color picker has a pre-defined palette to choose from
|
||||||
|
* Edit Board Columns and Categories input box bugfix
|
||||||
|
|
||||||
|
@ -496,6 +496,9 @@ p.detail {
|
|||||||
.list-group-item.small {
|
.list-group-item.small {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
}
|
}
|
||||||
|
.list-group.editable > .list-group-item {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
td > .list-group {
|
td > .list-group {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@ -572,6 +575,10 @@ input.custom-inline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* #FilesPage */
|
/* #FilesPage */
|
||||||
|
.files-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.files-widget {
|
.files-widget {
|
||||||
border: 1px solid rgba(51,51,51,.2);
|
border: 1px solid rgba(51,51,51,.2);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
@ -580,6 +587,10 @@ input.custom-inline {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.files-widget .widget-content {
|
||||||
|
height: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
.files-widget .small.pull-right {
|
.files-widget .small.pull-right {
|
||||||
margin-top: .3em;
|
margin-top: .3em;
|
||||||
}
|
}
|
||||||
@ -593,7 +604,6 @@ input.custom-inline {
|
|||||||
border: 1px solid rgba(51,51,51,.3);
|
border: 1px solid rgba(51,51,51,.3);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 500px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* #3rdParty */
|
/* #3rdParty */
|
||||||
@ -633,6 +643,9 @@ input.custom-inline {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
|
||||||
}
|
}
|
||||||
|
.sp-container {
|
||||||
|
width: 202px;
|
||||||
|
}
|
||||||
.sp-disabled {
|
.sp-disabled {
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
}
|
}
|
||||||
@ -650,6 +663,10 @@ input.custom-inline {
|
|||||||
.sp-cf:before, .sp-cf:after {
|
.sp-cf:before, .sp-cf:after {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
.sp-palette-container {
|
||||||
|
border: none;
|
||||||
|
padding-bottom: 290px;
|
||||||
|
}
|
||||||
/* jQueryUI Datepicker */
|
/* jQueryUI Datepicker */
|
||||||
.ui-datepicker {
|
.ui-datepicker {
|
||||||
border: 1px solid rgba(51,51,51,.5);
|
border: 1px solid rgba(51,51,51,.5);
|
||||||
|
@ -53,7 +53,7 @@ function($routeProvider, $httpProvider) {
|
|||||||
// Custom handlers for route authentication and rejection of invalid board id
|
// Custom handlers for route authentication and rejection of invalid board id
|
||||||
taskBoard.run(['$rootScope', '$location', '$window', 'AuthenticationService',
|
taskBoard.run(['$rootScope', '$location', '$window', 'AuthenticationService',
|
||||||
function($rootScope, $location, $window, AuthenticationService) {
|
function($rootScope, $location, $window, AuthenticationService) {
|
||||||
$rootScope.version = 'v0.2.6';
|
$rootScope.version = 'v0.2.7';
|
||||||
|
|
||||||
$rootScope.$on('$routeChangeStart', function(event, nextRoute, currentRoute) {
|
$rootScope.$on('$routeChangeStart', function(event, nextRoute, currentRoute) {
|
||||||
// Redirect to default path if authentication is required but not present.
|
// Redirect to default path if authentication is required but not present.
|
||||||
|
@ -71,6 +71,13 @@ function ($scope, $routeParams, $location, $interval, $window,
|
|||||||
$scope.deleteItem();
|
$scope.deleteItem();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.changeItemLane = function() {
|
||||||
|
$scope.itemFormData.loadItem($scope.contextItem);
|
||||||
|
$scope.itemFormData.isAdd = false;
|
||||||
|
|
||||||
|
$scope.submitItem($scope.itemFormData);
|
||||||
|
};
|
||||||
|
|
||||||
$scope.contextItem = {}; // Needs to exist prior to onContextMenu call.
|
$scope.contextItem = {}; // Needs to exist prior to onContextMenu call.
|
||||||
$scope.onContextMenu = function(laneId, item) {
|
$scope.onContextMenu = function(laneId, item) {
|
||||||
$scope.contextItem = item;
|
$scope.contextItem = item;
|
||||||
|
@ -68,7 +68,7 @@ function ($scope, BoardService) {
|
|||||||
allowEmpty: false,
|
allowEmpty: false,
|
||||||
localStorageKey: 'taskboard.colorPalette',
|
localStorageKey: 'taskboard.colorPalette',
|
||||||
showPalette: true,
|
showPalette: true,
|
||||||
palette: [[]],
|
palette: [ ['#fff', '#ececec', '#ffffe0', '#ffe0fa', '#bee7f4', '#c3f4b5', '#debee8', '#ffdea9', '#ffbaba'] ],
|
||||||
showSelectionPalette: true,
|
showSelectionPalette: true,
|
||||||
showButtons: false,
|
showButtons: false,
|
||||||
showInput: true,
|
showInput: true,
|
||||||
@ -120,6 +120,7 @@ function ($scope, BoardService) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
$scope.$parent.submitItem = $scope.submitItem;
|
||||||
|
|
||||||
var isSuccess = function(data) {
|
var isSuccess = function(data) {
|
||||||
$scope.alerts.showAlerts(data.alerts);
|
$scope.alerts.showAlerts(data.alerts);
|
||||||
|
@ -27,20 +27,12 @@ function ($scope, $interval, BoardService) {
|
|||||||
{ id: 2, action: 'Set item assignee' },
|
{ id: 2, action: 'Set item assignee' },
|
||||||
{ id: 3, action: 'Clear item due date' }
|
{ id: 3, action: 'Clear item due date' }
|
||||||
];
|
];
|
||||||
|
|
||||||
$scope.actionOptions = {
|
$scope.actionOptions = {
|
||||||
triggers: [
|
triggers: [
|
||||||
{
|
{ id: 0, trigger: 'Item moves to column' },
|
||||||
id: 0,
|
{ id: 1, trigger: 'Item assigned to user' },
|
||||||
trigger: 'Item moves to column'
|
{ id: 2, trigger: 'Item set to category' }
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
trigger: 'Item assigned to user'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
trigger: 'Item set to category'
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -63,10 +55,21 @@ function ($scope, $interval, BoardService) {
|
|||||||
getCategories = function(boardData) {
|
getCategories = function(boardData) {
|
||||||
var categories = [{ id: '0', name: 'Uncategorized' }];
|
var categories = [{ id: '0', name: 'Uncategorized' }];
|
||||||
|
|
||||||
if (boardData) {
|
if (boardData && boardData.ownCategory) {
|
||||||
boardData.ownCategory.forEach(function(category) {
|
boardData.ownCategory.forEach(function(category) {
|
||||||
categories.push(category);
|
categories.push(category);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
$scope.actionOptions.triggers.forEach(function(trigger, index) {
|
||||||
|
if (trigger.id === 2) {
|
||||||
|
$scope.actionOptions.triggers.splice(index, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$scope.actionTypes.forEach(function(type, index) {
|
||||||
|
if (type.id === 1) {
|
||||||
|
$scope.actionTypes.splice(index, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return categories;
|
return categories;
|
||||||
},
|
},
|
||||||
@ -144,16 +147,29 @@ function ($scope, $interval, BoardService) {
|
|||||||
|
|
||||||
updateAutoActions = function(actions) {
|
updateAutoActions = function(actions) {
|
||||||
if (!actions) {
|
if (!actions) {
|
||||||
|
$scope.actions = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var mappedActions = [];
|
var mappedActions = [];
|
||||||
actions.forEach(function(action) {
|
actions.forEach(function(action) {
|
||||||
|
var actionTrigger, actionType;
|
||||||
|
$scope.actionOptions.triggers.forEach(function(trigger) {
|
||||||
|
if (trigger.id === parseInt(action.trigger_id)) {
|
||||||
|
actionTrigger = trigger.trigger;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$scope.actionTypes.forEach(function(type) {
|
||||||
|
if (type.id === parseInt(action.action_id)) {
|
||||||
|
actionType = type.action;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
mappedActions.push({
|
mappedActions.push({
|
||||||
id: action.id,
|
id: action.id,
|
||||||
board: $scope.boardLookup[action.board_id],
|
board: $scope.boardLookup[action.board_id],
|
||||||
trigger: $scope.actionOptions.triggers[action.trigger_id].trigger + getSecondaryText(action),
|
trigger: actionTrigger + getSecondaryText(action),
|
||||||
action: $scope.actionTypes[action.action_id].action + getActionText(action)
|
action: actionType + getActionText(action)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -256,7 +272,7 @@ function ($scope, $interval, BoardService) {
|
|||||||
allowEmpty: false,
|
allowEmpty: false,
|
||||||
localStorageKey: 'taskboard.colorPalette',
|
localStorageKey: 'taskboard.colorPalette',
|
||||||
showPalette: true,
|
showPalette: true,
|
||||||
palette: [[]],
|
palette: [ ['#fff', '#ececec', '#ffffe0', '#ffe0fa', '#bee7f4', '#c3f4b5', '#debee8', '#ffdea9', '#ffbaba'] ],
|
||||||
showSelectionPalette: true,
|
showSelectionPalette: true,
|
||||||
showButtons: false,
|
showButtons: false,
|
||||||
showInput: true,
|
showInput: true,
|
||||||
@ -274,13 +290,13 @@ function ($scope, $interval, BoardService) {
|
|||||||
$scope.actionData.color = null;
|
$scope.actionData.color = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check every 250ms to see if a board has been chosen.
|
// Check every 500ms to see if a board has been chosen.
|
||||||
var updateIfBoardChosen = function() {
|
var updateIfBoardChosen = function() {
|
||||||
if ($scope.actionData.board !== null) {
|
if ($scope.actionData.board !== null) {
|
||||||
$interval.cancel($scope.interval); // Stop checking once it has.
|
$interval.cancel($scope.interval);
|
||||||
$scope.getTriggerWord();
|
$scope.getTriggerWord();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
$scope.interval = $interval(updateIfBoardChosen, 250);
|
$scope.interval = $interval(updateIfBoardChosen, 500);
|
||||||
$scope.$on('$destroy', function () { $interval.cancel($scope.interval); });
|
$scope.$on('$destroy', function () { $interval.cancel($scope.interval); });
|
||||||
}]);
|
}]);
|
||||||
|
@ -105,6 +105,15 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li role="presentation" class="divider"></li>
|
<li role="presentation" class="divider"></li>
|
||||||
|
<li role="presentation">
|
||||||
|
Move to Column:<br />
|
||||||
|
<select class="form-control"
|
||||||
|
data-ng-model="contextItem.lane_id"
|
||||||
|
data-ng-change="changeItemLane()"
|
||||||
|
data-ng-options="lane.id as lane.name for lane in currentBoard.ownLane | orderBy:'position':false">
|
||||||
|
</select>
|
||||||
|
</li>
|
||||||
|
<li role="presentation" class="divider"></li>
|
||||||
<li role="presentation">
|
<li role="presentation">
|
||||||
<a role="menuitem" style="cursor: default" data-ng-click="openAddItem()">
|
<a role="menuitem" style="cursor: default" data-ng-click="openAddItem()">
|
||||||
Add New Item
|
Add New Item
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
data-ng-repeat="attachment in viewItem.ownAttachment" data-ng-class-even="'alternate'">
|
data-ng-repeat="attachment in viewItem.ownAttachment" data-ng-class-even="'alternate'">
|
||||||
{{ attachment.name }}
|
{{ attachment.name }}
|
||||||
<span class="detail">
|
<span class="detail">
|
||||||
Uploaded by {{ userNames[attachment.userId] }} on {{ attachment.date }}
|
Uploaded by {{ userNames[attachment.user_id] }} on {{ attachment.date }}
|
||||||
</span>
|
</span>
|
||||||
<span class="links pull-right" data-ng-show="attachmentDeleting[attachment.id]">
|
<span class="links pull-right" data-ng-show="attachmentDeleting[attachment.id]">
|
||||||
<span class="fa fa-refresh fa-spin"></span> Deleting...
|
<span class="fa fa-refresh fa-spin"></span> Deleting...
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<div include-replace="partials/header.html"></div>
|
<div include-replace="partials/header.html"></div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div id="widget-container">
|
<div id="widget-container">
|
||||||
<div class="col-md-8 col-md-offset-2 min-padding">
|
<div class="col-md-10 col-md-offset-1 min-padding files-wrapper">
|
||||||
<div class="files-widget">
|
<div class="files-widget">
|
||||||
<h3 data-ng-if="!file.loading && !file.error" class="widget-header">
|
<h3 data-ng-if="!file.loading && !file.error" class="widget-header">
|
||||||
Attachment: {{ file.data.name }}
|
Attachment: {{ file.data.name }}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
<div data-include-replace="partials/header.html"></div>
|
<div data-include-replace="partials/header.html"></div>
|
||||||
<div class="slide-menu" data-ng-class="{ 'slide-menu-open': slide.open }" data-ng-if="currentUser.isAdmin">
|
<div class="slide-menu" data-ng-class="{ 'slide-menu-open': slide.open }" data-ng-if="currentUser.isAdmin">
|
||||||
<div class="settings-widget">
|
<div class="settings-widget">
|
||||||
<h3 class="widget-header text-center">Activity Log</h3>
|
<h3 class="widget-header text-center">Activity Log
|
||||||
|
<a class="pull-left fa fa-times small" data-ng-click="slide.open = false"></a>
|
||||||
|
</h3>
|
||||||
<div class="widget-content">
|
<div class="widget-content">
|
||||||
<p data-ng-if="actionsLoading" class="text-center">
|
<p data-ng-if="actionsLoading" class="text-center">
|
||||||
<span class="fa fa-refresh fa-spin"></span> Loading...
|
<span class="fa fa-refresh fa-spin"></span> Loading...
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
<div class="form-group half-width" data-ng-class="{ 'has-error': boardFormData.lanesError }">
|
<div class="form-group half-width" data-ng-class="{ 'has-error': boardFormData.lanesError }">
|
||||||
<form role="form" class="form form-inline" data-ng-submit="boardFormData.addLane()">
|
<form role="form" class="form form-inline" data-ng-submit="boardFormData.addLane()">
|
||||||
<h5>Columns</h5>
|
<h5>Columns</h5>
|
||||||
<ul class="list-group lanes">
|
<ul class="list-group lanes editable">
|
||||||
<li class="list-group-item small" data-ng-class="{ disabled: boardFormData.isSaving }"
|
<li class="list-group-item small" data-ng-class="{ disabled: boardFormData.isSaving }"
|
||||||
data-ng-repeat="lane in boardFormData.lanes | orderBy:'position':false">
|
data-ng-repeat="lane in boardFormData.lanes | orderBy:'position':false">
|
||||||
<span class="fa fa-arrows-v"></span>
|
<span class="fa fa-arrows-v"></span>
|
||||||
@ -42,7 +42,7 @@
|
|||||||
<div class="form-group half-width" data-ng-class="{ 'has-error': boardFormData.categoriesError }">
|
<div class="form-group half-width" data-ng-class="{ 'has-error': boardFormData.categoriesError }">
|
||||||
<form role="form" class="form form-inline" data-ng-submit="boardFormData.addCategory()">
|
<form role="form" class="form form-inline" data-ng-submit="boardFormData.addCategory()">
|
||||||
<h5>Categories</h5>
|
<h5>Categories</h5>
|
||||||
<ul class="list-group">
|
<ul class="list-group editable">
|
||||||
<li class="list-group-item small" data-ng-class="{ disabled: boardFormData.isSaving }"
|
<li class="list-group-item small" data-ng-class="{ disabled: boardFormData.isSaving }"
|
||||||
data-ng-repeat="category in boardFormData.categories | orderBy:'category':false">
|
data-ng-repeat="category in boardFormData.categories | orderBy:'category':false">
|
||||||
<span class="item-text">{{ category.name }}</span>
|
<span class="item-text">{{ category.name }}</span>
|
||||||
|
@ -35,6 +35,10 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div data-ng-if="currentUser.isAdmin == '1'">
|
<div data-ng-if="currentUser.isAdmin == '1'">
|
||||||
<p><a role="button" id="addUser" class="btn btn-info" data-toggle="modal" data-target=".userModal" data-ng-click="userFormData.reset()" title="Add User"><span class="fa fa-plus"></span> Add User</a></p>
|
<p>
|
||||||
|
<a role="button" id="addUser" class="btn btn-info" data-toggle="modal" data-target=".userModal" data-ng-click="userFormData.reset()" title="Add User">
|
||||||
|
<span class="fa fa-plus"></span> Add User
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
13
readme.md
13
readme.md
@ -29,6 +29,8 @@ A web server running PHP with sqlite enabled. Developed and tested under Apache2
|
|||||||
|
|
||||||
The server must have `sqlite` and `php5-sqlite` installed, as well as the `rewrite` and `expires` Apache modules.
|
The server must have `sqlite` and `php5-sqlite` installed, as well as the `rewrite` and `expires` Apache modules.
|
||||||
|
|
||||||
|
**Note:** For Apache v2.3.9 and later, virtual host for a site should have [`AllowOverride All`](http://httpd.apache.org/docs/2.4/mod/core.html#allowoverride) for TaskBoard root directory. Otherwise, .htaccess files will be completely ignored.
|
||||||
|
|
||||||
###Install
|
###Install
|
||||||
Installing TaskBoard is as easy as 1, 2, 3!
|
Installing TaskBoard is as easy as 1, 2, 3!
|
||||||
|
|
||||||
@ -99,12 +101,13 @@ Count was done from parent directory of TaskBoard as `./cloc-1.62.pl TaskBoard -
|
|||||||
|
|
||||||
Language | Files | Blank Lines | Comments | Code
|
Language | Files | Blank Lines | Comments | Code
|
||||||
-------------------|-------:|-------------:|---------:|---------:
|
-------------------|-------:|-------------:|---------:|---------:
|
||||||
Javascript | 23 | 194 | 34 | 1915
|
Javascript | 23 | 198 | 34 | 1935
|
||||||
HTML | 17 | 10 | 10 | 1015
|
HTML | 17 | 10 | 10 | 1030
|
||||||
PHP | 6 | 156 | 58 | 875
|
PHP | 6 | 156 | 58 | 875
|
||||||
CSS | 1 | 11 | 33 | 636
|
CSS | 1 | 13 | 33 | 651
|
||||||
Bourne Again Shell | 4 | 10 | 0 | 53
|
Bourne Again Shell | 4 | 10 | 0 | 53
|
||||||
XML | 1 | 0 | 0 | 12
|
XML | 1 | 0 | 0 | 12
|
||||||
__SUM:__ | __52__ | __381__ | __135__ | __4506__
|
__SUM:__ | __52__ | __387__ | __135__ | __4556__
|
||||||
|
|
||||||
|
Counts Last Updated: Dec. 4, 2014
|
||||||
|
|
||||||
Counts Last Updated: Nov. 10, 2014
|
|
||||||
|
Reference in New Issue
Block a user