Merge branch 'dev'
This commit is contained in:
commit
e54f488a0d
12
VERSION
12
VERSION
@ -1,7 +1,17 @@
|
|||||||
v0.2.5
|
v0.2.6
|
||||||
|
|
||||||
Changelog
|
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
|
* Markdown support in comments
|
||||||
* Cursor on default input field
|
* Cursor on default input field
|
||||||
* Show app version
|
* Show app version
|
||||||
|
>>>>>>> master
|
||||||
|
@ -120,7 +120,7 @@ $app->post('/items/:itemId/comment', function($itemId) use ($app, $jsonResponse)
|
|||||||
$item->ownComment[] = $comment;
|
$item->ownComment[] = $comment;
|
||||||
R::store($item);
|
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->addAlert('success', 'Comment added to item ' . $item->title . '.');
|
||||||
$jsonResponse->addBeans(R::load('item', $itemId));
|
$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());
|
$app->response->setBody($jsonResponse->asJson());
|
||||||
})->conditions(['itemId' => '\d+']);
|
})->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.
|
// Remove a comment from an item.
|
||||||
$app->post('/items/:itemId/comment/remove', function($itemId) use ($app, $jsonResponse) {
|
$app->post('/items/:itemId/comment/remove', function($itemId) use ($app, $jsonResponse) {
|
||||||
$data = json_decode($app->environment['slim.input']);
|
$data = json_decode($app->environment['slim.input']);
|
||||||
|
@ -157,6 +157,9 @@ $app->post('/users', function() use($app, $jsonResponse) {
|
|||||||
|
|
||||||
R::store($user);
|
R::store($user);
|
||||||
addUserToBoard($data->defaultBoard, $user);
|
addUserToBoard($data->defaultBoard, $user);
|
||||||
|
foreach($data->boardAccess as $board) {
|
||||||
|
addUserToBoard($board, $user);
|
||||||
|
}
|
||||||
|
|
||||||
$actor = getUser();
|
$actor = getUser();
|
||||||
logAction($actor->username . ' added user ' . $user->username, null, $user->export());
|
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);
|
R::store($user);
|
||||||
addUserToBoard($data->defaultBoard, $user);
|
addUserToBoard($data->defaultBoard, $user);
|
||||||
|
foreach($data->boardAccess as $board) {
|
||||||
|
addUserToBoard($board, $user);
|
||||||
|
}
|
||||||
|
|
||||||
logAction($actor->username . ' updated user ' . $user->username, $before, $user->export());
|
logAction($actor->username . ' updated user ' . $user->username, $before, $user->export());
|
||||||
$jsonResponse->addAlert('success', 'User updated.');
|
$jsonResponse->addAlert('success', 'User updated.');
|
||||||
|
@ -46,7 +46,9 @@ a:hover.fa {
|
|||||||
.datePast {
|
.datePast {
|
||||||
text-shadow: 0px 0px 3px rgba(255, 81, 28, 1);
|
text-shadow: 0px 0px 3px rgba(255, 81, 28, 1);
|
||||||
}
|
}
|
||||||
|
.unbold {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
.fa-edit, .fa-trash-o {
|
.fa-edit, .fa-trash-o {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@ -367,6 +369,13 @@ span.fa {
|
|||||||
.view-item-comment-form {
|
.view-item-comment-form {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
.edit-item-comment-form {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.edit-item-comment-form button {
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
.detail {
|
.detail {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
@ -382,6 +391,19 @@ p.detail {
|
|||||||
.links a {
|
.links a {
|
||||||
margin-left: 3px;
|
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 {
|
#add-comment {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,9 @@ function($rootScope, $location, $window, AuthenticationService) {
|
|||||||
!$window.localStorage.token) {
|
!$window.localStorage.token) {
|
||||||
$location.path('/');
|
$location.path('/');
|
||||||
}
|
}
|
||||||
|
if (nextRoute !== null && nextRoute.controller === 'LoginCtrl' && $window.localStorage.token) {
|
||||||
|
$location.path('/boards');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$rootScope.$on('$routeChangeSuccess', function(event, route, previousRoute) {
|
$rootScope.$on('$routeChangeSuccess', function(event, route, previousRoute) {
|
||||||
|
@ -28,7 +28,8 @@ function ($scope, $routeParams, $location, $interval, $window,
|
|||||||
$scope.boardId = $routeParams.boardId;
|
$scope.boardId = $routeParams.boardId;
|
||||||
$scope.filter = {
|
$scope.filter = {
|
||||||
user: null,
|
user: null,
|
||||||
category: null
|
category: null,
|
||||||
|
hide: false
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.filterChanged = function() {
|
$scope.filterChanged = function() {
|
||||||
|
@ -2,6 +2,7 @@ taskBoardControllers.controller('ItemViewBoardCtrl',
|
|||||||
['$scope', '$window', 'BoardService',
|
['$scope', '$window', 'BoardService',
|
||||||
function ($scope, $window, BoardService) {
|
function ($scope, $window, BoardService) {
|
||||||
$scope.viewItem = {};
|
$scope.viewItem = {};
|
||||||
|
$scope.comment = {};
|
||||||
$scope.toggle = {
|
$scope.toggle = {
|
||||||
sidebar: false
|
sidebar: false
|
||||||
};
|
};
|
||||||
@ -38,6 +39,9 @@ function ($scope, $window, BoardService) {
|
|||||||
updateItem = function(item) {
|
updateItem = function(item) {
|
||||||
$scope.viewItem = item;
|
$scope.viewItem = item;
|
||||||
$scope.viewItem.laneName = $scope.laneNames[item.lane_id];
|
$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) {
|
$scope.openItem = function(item, openModal) {
|
||||||
@ -54,9 +58,6 @@ function ($scope, $window, BoardService) {
|
|||||||
$scope.viewItem.ownAttachment = [];
|
$scope.viewItem.ownAttachment = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
convertDates($scope.viewItem.ownComment);
|
|
||||||
convertDates($scope.viewItem.ownAttachment);
|
|
||||||
convertDates($scope.viewItem.ownActivity);
|
|
||||||
$scope.fileReset = true;
|
$scope.fileReset = true;
|
||||||
|
|
||||||
if (openModal) {
|
if (openModal) {
|
||||||
@ -194,6 +195,28 @@ function ($scope, $window, BoardService) {
|
|||||||
$scope.comment.text = "";
|
$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() {
|
$scope.addItemAttachment = function() {
|
||||||
if ($scope.itemUpload === undefined || $scope.itemUpload.name === '') {
|
if ($scope.itemUpload === undefined || $scope.itemUpload.name === '') {
|
||||||
$scope.alerts.showAlert({ type: 'error', text: 'Select a file before uploading.' });
|
$scope.alerts.showAlert({ type: 'error', text: 'Select a file before uploading.' });
|
||||||
|
@ -165,9 +165,9 @@ function ($scope, $interval, BoardService) {
|
|||||||
.success(function(data) {
|
.success(function(data) {
|
||||||
updateAutoActions(data.data);
|
updateAutoActions(data.data);
|
||||||
$scope.loadingActions = false;
|
$scope.loadingActions = false;
|
||||||
$scope.loadActions();
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
$interval($scope.loadActions, 2000);
|
||||||
|
|
||||||
// Wait until boards are loaded to load the actions.
|
// Wait until boards are loaded to load the actions.
|
||||||
$scope.$watch('loadingBoards', function() {
|
$scope.$watch('loadingBoards', function() {
|
||||||
|
@ -102,6 +102,12 @@ function($http) {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateItemComment: function(commentId, comment) {
|
||||||
|
return $http.post('api/comments/' + commentId, {
|
||||||
|
text: comment
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
removeItemComment: function(itemId, commentId) {
|
removeItemComment: function(itemId, commentId) {
|
||||||
return $http.post('api/items/' + itemId + '/comment/remove', {
|
return $http.post('api/items/' + itemId + '/comment/remove', {
|
||||||
id: commentId
|
id: commentId
|
||||||
|
@ -54,6 +54,7 @@ function($http) {
|
|||||||
username: formData.username,
|
username: formData.username,
|
||||||
password: formData.password,
|
password: formData.password,
|
||||||
defaultBoard: formData.defaultBoard,
|
defaultBoard: formData.defaultBoard,
|
||||||
|
boardAccess: formData.boardAccess,
|
||||||
isAdmin: formData.isAdmin
|
isAdmin: formData.isAdmin
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -64,6 +65,7 @@ function($http) {
|
|||||||
newUsername: formData.username,
|
newUsername: formData.username,
|
||||||
password: formData.password,
|
password: formData.password,
|
||||||
defaultBoard: formData.defaultBoard,
|
defaultBoard: formData.defaultBoard,
|
||||||
|
boardAccess: formData.boardAccess,
|
||||||
isAdmin: formData.isAdmin
|
isAdmin: formData.isAdmin
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -7,6 +7,9 @@
|
|||||||
</select>
|
</select>
|
||||||
</p>
|
</p>
|
||||||
<p class="pull-right form-group form-inline" data-ng-if="!currentBoard.loading">
|
<p class="pull-right form-group form-inline" data-ng-if="!currentBoard.loading">
|
||||||
|
<label class="unbold">Hide Filtered Items:
|
||||||
|
<input type="checkbox" data-ng-model="filter.hide" data-ng-change="filterChanged()">
|
||||||
|
</label>
|
||||||
User Filter:
|
User Filter:
|
||||||
<select class="form-control" data-ng-model="filter.user" data-ng-change="filterChanged()"
|
<select class="form-control" data-ng-model="filter.user" data-ng-change="filterChanged()"
|
||||||
data-ng-options="user.id as user.username for user in currentBoard.sharedUser">
|
data-ng-options="user.id as user.username for user in currentBoard.sharedUser">
|
||||||
@ -49,7 +52,8 @@
|
|||||||
</h3>
|
</h3>
|
||||||
<div class="itemContainer">
|
<div class="itemContainer">
|
||||||
<div><!-- Needed to fix sortable behavior when there are no items in a column. --></div>
|
<div><!-- Needed to fix sortable behavior when there are no items in a column. --></div>
|
||||||
<div class="boardItem clearfix" data-ng-class="{'filtered': item.filtered}"
|
<div class="boardItem clearfix"
|
||||||
|
data-ng-class="{'filtered': item.filtered, 'hidden': item.filtered && filter.hide}"
|
||||||
data-ng-repeat="item in lane.ownItem | orderBy:'position':false"
|
data-ng-repeat="item in lane.ownItem | orderBy:'position':false"
|
||||||
data-ng-dblclick="openItem(item)"
|
data-ng-dblclick="openItem(item)"
|
||||||
style="background-color: {{ item.color }}"
|
style="background-color: {{ item.color }}"
|
||||||
@ -120,3 +124,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -86,21 +86,41 @@
|
|||||||
<div class="list-group-item"
|
<div class="list-group-item"
|
||||||
data-ng-repeat="comment in viewItem.ownComment | orderBy:'timestamp':comments.sorting"
|
data-ng-repeat="comment in viewItem.ownComment | orderBy:'timestamp':comments.sorting"
|
||||||
data-ng-class-even="'alternate'">
|
data-ng-class-even="'alternate'">
|
||||||
<p class="comment">
|
<div class="comment">
|
||||||
<span data-ng-bind-html="markedComment(comment.text)"></span>
|
<span class="commentText" data-ng-bind-html="markedComment(comment.text)"></span>
|
||||||
<a class="fa fa-trash-o pull-right"
|
<span class="commentActions">
|
||||||
|
<a class="fa fa-edit"
|
||||||
|
data-ng-if="currentUser.userId == comment.user_id || currentUser.isAdmin == 1"
|
||||||
|
data-ng-click="beginEditComment(comment.id, comment.text)"></a>
|
||||||
|
<a class="fa fa-trash-o"
|
||||||
data-ng-if="currentUser.userId == comment.user_id || currentUser.isAdmin == 1"
|
data-ng-if="currentUser.userId == comment.user_id || currentUser.isAdmin == 1"
|
||||||
data-ng-click="deleteComment(comment.id)"></a>
|
data-ng-click="deleteComment(comment.id)"></a>
|
||||||
</p>
|
</span>
|
||||||
<p class="detail">Posted by {{ userNames[comment.user_id] }} on {{ comment.date }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p class="detail">
|
||||||
|
<span data-ng-if="!comment.is_edited == 1">Posted</span>
|
||||||
|
<span data-ng-if="comment.is_edited == 1">Edited</span>
|
||||||
|
by {{ userNames[comment.user_id] }} on {{ comment.date }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="edit-item-comment-form" data-ng-if="comment.isEdit">
|
||||||
|
<h4>Edit Comment</h4>
|
||||||
|
<div class="form-group">
|
||||||
|
<textarea id="editComment" class="form-control" rows="4" data-ng-model="comment.editText"></textarea>
|
||||||
|
<button id="edit-comment" class="btn btn-info pull-right"
|
||||||
|
data-ng-class="{ 'disabled': viewItem.disabled }"
|
||||||
|
data-ng-click="editComment(comment.id, comment.editText)"><span class="fa fa-comment-o"></span> Edit Comment</button>
|
||||||
|
<button id="cancel-edit-comment" class="btn btn-default pull-right"
|
||||||
|
data-ng-click="comment.isEdit = false"><span class="fa fa-times"></span> Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="view-item-comment-form">
|
<div class="view-item-comment-form">
|
||||||
<h4>Add Comment</h4>
|
<h4>Add Comment</h4>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<textarea id="itemComment" class="form-control" rows="4" data-ng-model="comment.text"></textarea>
|
<textarea id="itemComment" class="form-control" rows="4" data-ng-model="comment.text"></textarea>
|
||||||
<button type="submit" id="add-comment" class="btn btn-info pull-right"
|
<button id="add-comment" class="btn btn-info pull-right"
|
||||||
data-ng-class="{ 'disabled': viewItem.disabled }"
|
data-ng-class="{ 'disabled': viewItem.disabled }"
|
||||||
data-ng-click="addItemComment(comment.text)"><span class="fa fa-comment-o"></span> Submit Comment</button>
|
data-ng-click="addItemComment(comment.text)"><span class="fa fa-comment-o"></span> Submit Comment</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -35,6 +35,11 @@
|
|||||||
<option value="">None</option>
|
<option value="">None</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group" data-ng-if="boardNames.length">
|
||||||
|
<h5>Board Access</h5>
|
||||||
|
<select class="form-control" data-ng-model="userFormData.boardAccess" data-ng-options="board.id as board.name for board in boardNames" multiple>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" data-ng-model="userFormData.isAdmin">
|
<input type="checkbox" data-ng-model="userFormData.isAdmin">
|
||||||
@ -42,8 +47,12 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="submit" id="modalAddUser" class="btn btn-info" data-ng-click="addUser(userFormData)" data-ng-if="userFormData.isAdd"><span class="fa fa-plus"></span> Add User</button>
|
<button type="submit" id="modalAddUser" class="btn btn-info" data-ng-click="addUser(userFormData)" data-ng-if="userFormData.isAdd">
|
||||||
<button type="submit" id="modalEditUser" class="btn btn-info" data-ng-click="editUser(userFormData)" data-ng-if="!userFormData.isAdd"><span class="fa fa-save"></span> Save User</button>
|
<span class="fa fa-plus"></span> Add User
|
||||||
|
</button>
|
||||||
|
<button type="submit" id="modalEditUser" class="btn btn-info" data-ng-click="editUser(userFormData)" data-ng-if="!userFormData.isAdd">
|
||||||
|
<span class="fa fa-save"></span> Save User
|
||||||
|
</button>
|
||||||
<a class="btn btn-default" data-ng-click="userFormData.cancel()">Cancel</a>
|
<a class="btn btn-default" data-ng-click="userFormData.cancel()">Cancel</a>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
15
readme.md
15
readme.md
@ -27,6 +27,8 @@ The goal of TaskBoard is to provide a simple and clean interface to a functional
|
|||||||
###Requirements
|
###Requirements
|
||||||
A web server running PHP with sqlite enabled. Developed and tested under Apache2 running PHP 5.5+.
|
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
|
###Install
|
||||||
Installing TaskBoard is as easy as 1, 2, 3!
|
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
|
Language | Files | Blank Lines | Comments | Code
|
||||||
-------------------|-------:|-------------:|---------:|---------:
|
-------------------|-------:|-------------:|---------:|---------:
|
||||||
Javascript | 22 | 187 | 34 | 1853
|
Javascript | 23 | 194 | 34 | 1915
|
||||||
HTML | 17 | 7 | 8 | 936
|
HTML | 17 | 10 | 10 | 1015
|
||||||
PHP | 6 | 150 | 57 | 849
|
PHP | 6 | 156 | 58 | 875
|
||||||
CSS | 1 | 12 | 33 | 609
|
CSS | 1 | 11 | 33 | 636
|
||||||
Bourne Again Shell | 4 | 10 | 0 | 53
|
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
|
||||||
|
Reference in New Issue
Block a user