Merge pull request #133 from nextcloud/archive-boards

Archive boards
This commit is contained in:
Julius Härtl
2017-06-08 22:46:04 +02:00
committed by GitHub
46 changed files with 1002 additions and 173 deletions

View File

@@ -67,7 +67,7 @@ pipeline:
matrix:
TESTS: syntax-php7.0
php5.6:
image: nextcloudci/php5.6:php5.6-3
image: nextcloudci/php5.6:php5.6-7
environment:
- APP_NAME=deck
- CORE_BRANCH=master
@@ -93,7 +93,7 @@ pipeline:
matrix:
TESTS: php5.6
php7.0:
image: nextcloudci/php7.0:php7.0-7
image: nextcloudci/php7.0:php7.0-8
environment:
- APP_NAME=deck
- CORE_BRANCH=master
@@ -170,4 +170,4 @@ matrix:
- TESTS: jsbuild
- TESTS: integration
branches: [ master, stable* ]

View File

@@ -38,6 +38,14 @@
<type>boolean</type>
<default>false</default>
</field>
<field>
<name>deleted_at</name>
<type>integer</type>
<default>0</default>
<length>8</length>
<notnull>false</notnull>
<unsigned>true</unsigned>
</field>
</declaration>
</table>
<table>

View File

@@ -16,7 +16,7 @@
💥 This is still alpha software: it may not be stable enough for production!
</description>
<version>0.1.4.1</version>
<version>0.1.4.2</version>
<licence>agpl</licence>
<author>Julius Härtl</author>
<namespace>Deck</namespace>
@@ -30,6 +30,9 @@
<dependencies>
<nextcloud min-version="11" max-version="13" />
</dependencies>
<background-jobs>
<job>OCA\Deck\Cron\DeleteCron</job>
</background-jobs>
<repair-steps>
<post-migration>
<step>OCA\Deck\Migration\UnknownUsers</step>

View File

@@ -31,6 +31,7 @@ return [
['name' => 'board#read', 'url' => '/boards/{boardId}', 'verb' => 'GET'],
['name' => 'board#update', 'url' => '/boards/{boardId}', 'verb' => 'PUT'],
['name' => 'board#delete', 'url' => '/boards/{boardId}', 'verb' => 'DELETE'],
['name' => 'board#deleteUndo', 'url' => '/boards/{boardId}/deleteUndo', 'verb' => 'POST'],
['name' => 'board#getUserPermissions', 'url' => '/boards/{boardId}/permissions', 'verb' => 'GET'],
['name' => 'board#addAcl', 'url' => '/boards/{boardId}/acl', 'verb' => 'POST'],
['name' => 'board#updateAcl', 'url' => '/boards/{boardId}/acl', 'verb' => 'PUT'],

View File

@@ -65,6 +65,14 @@ button.button-inline:hover {
* Navigation sidebar
*/
.app-navigation-entry-menu ul {
flex-direction: row;
}
.app-navigation-entry-utils-menu-button {
display: block !important;
}
.app-navigation-entry-utils-menu-share {
display: flex !important;
padding: 14px;
@@ -318,9 +326,9 @@ button.button-inline:hover {
.popovermenu {
z-index: 999;
opacity: 1;
margin-top: 25px;
margin-right: 3px;
display: block;
margin-top: 25px;
margin-right: 0px;
}
.popovermenu.hidden {
@@ -614,23 +622,30 @@ button.button-inline:hover {
.colorselect {
overflow: hidden;
clear: both;
padding-top: 4px;
padding-left: 4px;
border-radius:3px;
flex-direction: row;
min-width: 240px;
height: 34px;
display: flex;
margin: 3px 3px 3px 0;
}
.colorselect .color {
opacity: 0.7;
width: 27px;
height: 27px;
float: left;
margin-right: 2px;
height: 100%;
flex-grow: 1;
border: none;
}
.colorselect .selected {
background-image: url(../../../core/img/actions/checkmark.svg);
background-position: center center;
background-repeat: no-repeat;
opacity: 1.0;
border: 1px solid #333333;
}
.colorselect .dark.selected {
background-image: url(../../../core/img/actions/checkmark-white.svg);
}
.labels .colorselect {
@@ -662,6 +677,16 @@ button.button-inline:hover {
cursor: pointer;
display: block;
}
#boardlist .app-popover-menu-utils {
width: 30px;
display: inline;
position: relative;
}
.popovermenu ul {
display: flex !important;
flex-direction: column;
}
#boardlist td {
padding: 10px;
@@ -680,15 +705,34 @@ button.button-inline:hover {
.cell-board-title {
width: 50%;
}
#boardlist .colorselect,
#boardlist input {
float: left;
#boardlist tr.deleted td * {
opacity: 0.5;
}
#boardlist .colorselect {
#boardlist td form {
display: flex;
width: 100%;
}
#boardlist td .colorselect {
flex-grow: 1;
}
#boardlist td input[type=text] {
flex-grow: 2;
}
#boardlist td input[type=submit] {
width: 32px;
}
#boardlist td .app-popover-menu-utils {
float: right;
}
#boardlist .popovermenu {
margin-top: 5px;
}
@@ -925,6 +969,10 @@ button.button-inline:hover {
* Custom icons
*/
.icon-deck {
background-image: url(../img/deck.svg);
}
.icon-group {
background-image: url('../../../settings/img/users.svg');
}

1
img/deck.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M432.3 448.1h-348c-13.2 0-24-10.8-24-24V264.6c0-13.2 10.8-24 24-24h348c13.2 0 24 10.8 24 24v159.5c0 13.2-10.8 24-24 24zM380.4 89.8H127.8c-7.7 0-14-6.3-14-14v-6.3c0-7.7 6.3-14 14-14h252.6c7.7 0 14 6.3 14 14v6.3c0 7.7-6.3 14-14 14zm19.4 61.8H110.6c-7.7 0-14-6.3-14-14v-6.3c0-7.7 6.3-14 14-14h289.2c7.7 0 14 6.3 14 14v6.3c0 7.7-6.3 14-14 14zm21.6 61.4H94.6c-7.7 0-14-6.3-14-14v-6.3c0-7.7 6.3-14 14-14h326.8c7.7 0 14 6.3 14 14v6.3c0 7.7-6.3 14-14 14z"/></svg>

After

Width:  |  Height:  |  Size: 527 B

View File

@@ -33,36 +33,40 @@ app.config(function ($provide, $routeProvider, $interpolateProvider, $httpProvid
});
markdownItConverterProvider.use(markdownitLinkTarget);
$urlRouterProvider.otherwise("/");
$urlRouterProvider.otherwise('/');
$stateProvider
.state('list', {
url: "/",
templateUrl: "/boardlist.mainView.html",
controller: 'ListController'
url: '/:filter',
templateUrl: '/boardlist.mainView.html',
controller: 'ListController',
reloadOnSearch: false,
params: {
filter: { value: '', dynamic: true }
}
})
.state('board', {
url: "/board/:boardId/:filter",
templateUrl: "/board.html",
url: '/board/:boardId/:filter',
templateUrl: '/board.html',
controller: 'BoardController',
params: {
filter: { value: '', dynamic: true }
}
})
.state('board.detail', {
url: "/detail/",
url: '/detail/',
reloadOnSearch : false,
views: {
"sidebarView": {
templateUrl: "/board.sidebarView.html"
'sidebarView': {
templateUrl: '/board.sidebarView.html'
}
}
})
.state('board.card', {
url: "/card/:cardId",
url: '/card/:cardId',
views: {
"sidebarView": {
templateUrl: "/card.sidebarView.html",
'sidebarView': {
templateUrl: '/card.sidebarView.html',
controller: 'CardController'
}
}

View File

@@ -20,11 +20,17 @@
*
*/
app.run(function ($document, $rootScope, $transitions) {
app.run(function ($document, $rootScope, $transitions, BoardService) {
'use strict';
$document.click(function (event) {
$rootScope.$broadcast('documentClicked', event);
});
$transitions.onEnter({from: 'list'}, function($state, $transition$) {
BoardService.unsetCurrrent();
});
$transitions.onEnter({to: 'list'}, function($state, $transition$) {
BoardService.unsetCurrrent();
});
$transitions.onEnter({to: 'board.card'}, function ($state, $transition$) {
$rootScope.sidebar.show = true;
});
@@ -40,9 +46,6 @@ app.run(function ($document, $rootScope, $transitions) {
$transitions.onExit({from: 'board.detail'}, function ($state) {
$rootScope.sidebar.show = false;
});
$transitions.onEnter({to: 'board.archive'}, function ($state) {
//BoardController.loadArchived();
});
$('link[rel="shortcut icon"]').attr(
'href',

View File

@@ -80,7 +80,7 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
}
};
$scope.checkCanEdit = function () {
return !$scope.archived;
return !BoardService.getCurrent().archived;
};
// filter cards here, as ng-sortable will not work nicely with html-inline filters
@@ -114,7 +114,6 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
// Handle initial Loading
BoardService.fetchOne($scope.id).then(function (data) {
BoardService.getPermissions();
$scope.statusservice.releaseWaiting();
}, function (error) {
$scope.statusservice.setError('Error occured', error);

View File

@@ -48,9 +48,10 @@ app.controller('CardController', function ($scope, $rootScope, $routeParams, $lo
}
};
$scope.cardEditDescriptionShow = function($event) {
if(BoardService.isArchived() || CardService.getCurrent().archived) {
return false;
}
var node = $event.target.nodeName;
console.log($event);
console.log(BoardService);
if($scope.card.archived || !$scope.boardservice.canEdit()) {
console.log(node);
} else {

View File

@@ -1,4 +1,3 @@
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
@@ -21,11 +20,15 @@
*
*/
app.controller('ListController', function ($scope, $location, $filter, BoardService, $element, $timeout) {
/* global app angular */
app.controller('ListController', function ($scope, $location, $filter, BoardService, $element, $timeout, $stateParams, $state) {
$scope.boards = [];
$scope.newBoard = {};
$scope.status = {
deleteUndo: []
deleteUndo: [],
filter: $stateParams.filter ? $stateParams.filter : '',
sidebar: false
};
$scope.colors = ['0082c9', '00c9c6','00c906', 'c92b00', 'F1DB50', '7C31CC', '3A3B3D', 'CACBCD'];
$scope.boardservice = BoardService;
@@ -42,13 +45,42 @@ app.controller('ListController', function ($scope, $location, $filter, BoardServ
$scope.filterData = function () {
angular.copy($scope.boardservice.getData(), $scope.boardservice.sorted);
$scope.boardservice.sorted = $filter('orderBy')($scope.boardservice.sorted, 'title');
angular.copy($scope.boardservice.sorted, $scope.boardservice.sidebar);
$scope.boardservice.sidebar = $filter('orderBy')($scope.boardservice.sidebar, 'title');
$scope.boardservice.sidebar = $filter('cardFilter')($scope.boardservice.sidebar, {archived: false});
if ($scope.status.filter === 'archived') {
var filter = {};
filter[$scope.status.filter] = true;
$scope.boardservice.sorted = $filter('cardFilter')($scope.boardservice.sorted, filter);
} else if ($scope.status.filter === 'shared') {
$scope.boardservice.sorted = $filter('cardFilter')($scope.boardservice.sorted, {archived: false});
$scope.boardservice.sorted = $filter('boardFilterAcl')($scope.boardservice.sorted);
} else {
$scope.boardservice.sorted = $filter('cardFilter')($scope.boardservice.sorted, {archived: false});
}
$scope.boardservice.sorted = $filter('orderBy')($scope.boardservice.sorted, ['deletedAt', 'title']);
};
$scope.$watchCollection(function(){
return $state.params;
}, function(){
$scope.status.filter = $state.params.filter;
$scope.filterData();
});
$scope.selectColor = function(color) {
$scope.newBoard.color = color;
};
$scope.gotoBoard = function(board) {
if(board.deletedAt > 0) {
return false;
}
return $state.go('board', {boardId: board.id});
};
$scope.boardCreate = function() {
if(!$scope.newBoard.title || !$scope.newBoard.color) {
$scope.status.addBoard=false;
@@ -72,26 +104,30 @@ app.controller('ListController', function ($scope, $location, $filter, BoardServ
board.status.edit = false;
};
$scope.boardArchive = function (board) {
board.archived = true;
BoardService.update(board).then(function(data) {
$scope.filterData();
});
};
$scope.boardUnarchive = function (board) {
board.archived = false;
BoardService.update(board).then(function(data) {
$scope.filterData();
});
};
$scope.boardDelete = function(board) {
var boardId = board.id;
$scope.status.deleteUndo[boardId] = 10;
$scope.boardDeleteCountdown = function () {
console.log($scope.status);
if($scope.status.deleteUndo[boardId] > 0) {
$scope.status.deleteUndo[boardId]--;
$timeout($scope.boardDeleteCountdown, 1000);
}
if($scope.status.deleteUndo[boardId] === 0) {
BoardService.delete(board.id).then(function (data) {
$scope.filterData();
});
}
};
$timeout($scope.boardDeleteCountdown, 1000);
BoardService.delete(board.id).then(function (data) {
$scope.filterData();
});
};
$scope.boardDeleteUndo = function (board) {
delete $scope.status.deleteUndo[board.id];
BoardService.deleteUndo(board.id).then(function (data) {
$scope.filterData();
});
};
});

View File

@@ -0,0 +1,33 @@
/*
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
app.filter('boardFilterAcl', function() {
return function(boards) {
var _result = [];
angular.forEach(boards, function(board){
if(board.acl !== null && Object.keys(board.acl).length > 0) {
_result.push(board);
}
});
return _result;
};
});

View File

@@ -24,15 +24,16 @@
app.filter('cardFilter', function() {
return function(cards, rules) {
var _result = {};
var _result = [];
angular.forEach(cards, function(card){
var _card = card;
angular.some(rules, function(rule, condition) {
if(_card[rule]===condition) {
var keys = Object.keys(rules);
keys.some(function(key, condition) {
if(_card[key]===rules[key]) {
_result.push(_card);
}
});
});
return result;
return _result;
};
});

View File

@@ -24,7 +24,7 @@ app.filter('textColorFilter', function() {
return function (hex) {
// RGB2HLS by Garry Tan
// http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
var result = /^([A-Fa-f\d]{2})([A-Fa-f\d]{2})([A-Fa-f\d]{2})$/i.exec(hex);
var result = /^#?([A-Fa-f\d]{2})([A-Fa-f\d]{2})([A-Fa-f\d]{2})$/i.exec(hex);
var color = result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),

View File

@@ -145,7 +145,13 @@ app.factory('ApiService', function($http, $q){
return this.data[this.id];
};
ApiService.prototype.getData = function() {
ApiService.prototype.unsetCurrrent = function () {
this.id = null;
};
ApiService.prototype.getData = function() {
return $.map(this.data, function(value, index) {
return [value];
});

View File

@@ -26,6 +26,33 @@ app.factory('BoardService', function(ApiService, $http, $q){
};
BoardService.prototype = angular.copy(ApiService.prototype);
BoardService.prototype.delete = function (id) {
var deferred = $q.defer();
var self = this;
$http.delete(this.baseUrl + '/' + id).then(function (response) {
self.data[id].deletedAt = response.data.deletedAt;
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Deleting ' + self.endpoint + ' failed');
});
return deferred.promise;
};
BoardService.prototype.deleteUndo = function (id) {
var deferred = $q.defer();
var self = this;
var _id = id;
$http.post(this.baseUrl + '/' + id + '/deleteUndo').then(function (response) {
self.data[_id].deletedAt = 0;
console.log(self.data[_id]);
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Deleting ' + self.endpoint + ' failed');
});
return deferred.promise;
};
BoardService.prototype.searchUsers = function (search) {
var deferred = $q.defer();
var self = this;
@@ -151,44 +178,43 @@ app.factory('BoardService', function(ApiService, $http, $q){
return deferred.promise;
};
BoardService.prototype.getPermissions = function() {
var board = this.getCurrent();
var deferred = $q.defer();
$http.get(this.baseUrl + '/' + board.id + '/permissions').then(function (response) {
board.permissions = response.data;
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error fetching board permissions ' + board);
});
};
BoardService.prototype.canRead = function() {
if(!this.getCurrent() || !this.getCurrent().permissions) {
return false;
}
return this.getCurrent().permissions['PERMISSION_READ'];
}
};
BoardService.prototype.canEdit = function() {
if(!this.getCurrent() || !this.getCurrent().permissions) {
return false;
}
return this.getCurrent().permissions['PERMISSION_EDIT'];
}
};
BoardService.prototype.canManage = function() {
BoardService.prototype.canManage = function(board) {
if(board !== null && board !== undefined) {
return board.permissions['PERMISSION_MANAGE'];
}
if(!this.getCurrent() || !this.getCurrent().permissions) {
return false;
}
return this.getCurrent().permissions['PERMISSION_MANAGE'];
}
};
BoardService.prototype.canShare = function() {
if(!this.getCurrent() || !this.getCurrent().permissions) {
return false;
}
return this.getCurrent().permissions['PERMISSION_SHARE'];
}
};
BoardService.prototype.isArchived = function () {
if(!this.getCurrent() || this.getCurrent().archived) {
return true;
}
return false;
};
service = new BoardService($http, 'boards', $q);
return service;

View File

@@ -23,12 +23,12 @@
namespace OCA\Deck;
class CardArchivedException extends \Exception {
class ArchivedItemException extends \Exception {
/**
* Constructor
* @param string $msg the error message
*/
public function __construct($msg = "") {
public function __construct($msg = "Operation not allowed. Item is archived.") {
parent::__construct($msg);
}
}

View File

@@ -96,8 +96,8 @@ class BoardController extends Controller {
* @param $color
* @return \OCP\AppFramework\Db\Entity
*/
public function update($id, $title, $color) {
return $this->boardService->update($id, $title, $color);
public function update($id, $title, $color, $archived) {
return $this->boardService->update($id, $title, $color, $archived);
}
/**
@@ -108,6 +108,14 @@ class BoardController extends Controller {
public function delete($boardId) {
return $this->boardService->delete($boardId);
}
/**
* @NoAdminRequired
* @param $boardId
* @return \OCP\AppFramework\Db\Entity
*/
public function deleteUndo($boardId) {
return $this->boardService->deleteUndo($boardId);
}
/**
* @NoAdminRequired

49
lib/Cron/DeleteCron.php Normal file
View File

@@ -0,0 +1,49 @@
<?php
/**
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* Created by PhpStorm.
* User: jus
* Date: 16.05.17
* Time: 12:34
*/
namespace OCA\Deck\Cron;
use OC\BackgroundJob\Job;
use OCA\Deck\Db\BoardMapper;
class DeleteCron extends Job {
public function __construct(BoardMapper $boardMapper) {
$this->boardMapper = $boardMapper;
}
protected function run($argument) {
$boards = $this->boardMapper->findToDelete();
foreach ($boards as $board) {
$this->boardMapper->delete($board);
}
}
}

View File

@@ -34,15 +34,19 @@ class Board extends RelationalEntity implements JsonSerializable {
protected $archived = false;
protected $labels = [];
protected $acl = [];
protected $permissions = [];
protected $shared;
protected $deletedAt = 0;
public function __construct() {
$this->addType('id', 'integer');
$this->addType('shared', 'integer');
$this->addType('archived', 'boolean');
$this->addType('deletedAt', 'integer');
$this->addRelation('labels');
$this->addRelation('acl');
$this->addRelation('shared');
$this->addRelation('permissions');
$this->addResolvable('owner');
$this->shared = -1;
}

View File

@@ -59,7 +59,7 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
* @return \OCP\AppFramework\Db\Entity if not found
*/
public function find($id, $withLabels = false, $withAcl = false) {
$sql = 'SELECT id, title, owner, color, archived FROM `*PREFIX*deck_boards` ' .
$sql = 'SELECT id, title, owner, color, archived, deleted_at FROM `*PREFIX*deck_boards` ' .
'WHERE `id` = ?';
$board = $this->findEntity($sql, [$id]);
@@ -87,8 +87,8 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
* @return array
*/
public function findAllByUser($userId, $limit = null, $offset = null) {
$sql = 'SELECT id, title, owner, color, archived, 0 as shared FROM `*PREFIX*deck_boards` WHERE owner = ? UNION ' .
'SELECT boards.id, title, owner, color, archived, 1 as shared FROM `*PREFIX*deck_boards` as boards ' .
$sql = 'SELECT id, title, owner, color, archived, deleted_at, 0 as shared FROM `*PREFIX*deck_boards` WHERE owner = ? UNION ' .
'SELECT boards.id, title, owner, color, archived, deleted_at, 1 as shared FROM `*PREFIX*deck_boards` as boards ' .
'JOIN `*PREFIX*deck_board_acl` as acl ON boards.id=acl.board_id WHERE acl.participant=? AND acl.type=? AND boards.owner != ?';
$entries = $this->findEntities($sql, [$userId, $userId, Acl::PERMISSION_TYPE_USER, $userId], $limit, $offset);
/* @var Board $entry */
@@ -112,7 +112,7 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
if (count($groups) <= 0) {
return [];
}
$sql = 'SELECT boards.id, title, owner, color, archived, 2 as shared FROM `*PREFIX*deck_boards` as boards ' .
$sql = 'SELECT boards.id, title, owner, color, archived, deleted_at, 2 as shared FROM `*PREFIX*deck_boards` as boards ' .
'INNER JOIN `*PREFIX*deck_board_acl` as acl ON boards.id=acl.board_id WHERE owner != ? AND type=? AND (';
for ($i = 0; $i < count($groups); $i++) {
$sql .= 'acl.participant = ? ';
@@ -135,6 +135,15 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
return $this->findEntities($sql, []);
}
public function findToDelete() {
// add buffer of 5 min
$timeLimit = time()-(60*5);
$sql = 'SELECT id, title, owner, color, archived, deleted_at FROM `*PREFIX*deck_boards` ' .
'WHERE `deleted_at` > 0 AND `deleted_at` < ?';
\OC::$server->getLogger()->error($sql);
return $this->findEntities($sql, [$timeLimit]);
}
public function delete(/** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */
\OCP\AppFramework\Db\Entity $entity) {
// delete acl

View File

@@ -23,9 +23,12 @@
namespace OCA\Deck\Service;
use OCA\Deck\ArchivedItemException;
use OCA\Deck\Db\Acl;
use OCA\Deck\Db\AclMapper;
use OCA\Deck\Db\IPermissionMapper;
use OCA\Deck\Db\Label;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\IL10N;
use OCA\Deck\Db\Board;
use OCA\Deck\Db\BoardMapper;
@@ -61,6 +64,13 @@ class BoardService {
$this->boardMapper->mapAcl($acl);
}
}
$permissions = $this->permissionService->matchPermissions($item);
$item->setPermissions([
'PERMISSION_READ' => $permissions[Acl::PERMISSION_READ],
'PERMISSION_EDIT' => $permissions[Acl::PERMISSION_EDIT],
'PERMISSION_MANAGE' => $permissions[Acl::PERMISSION_MANAGE],
'PERMISSION_SHARE' => $permissions[Acl::PERMISSION_SHARE]
]);
$result[$item->getId()] = $item;
}
}
@@ -77,9 +87,50 @@ class BoardService {
$this->boardMapper->mapAcl($acl);
}
}
$permissions = $this->permissionService->matchPermissions($board);
$board->setPermissions([
'PERMISSION_READ' => $permissions[Acl::PERMISSION_READ],
'PERMISSION_EDIT' => $permissions[Acl::PERMISSION_EDIT],
'PERMISSION_MANAGE' => $permissions[Acl::PERMISSION_MANAGE],
'PERMISSION_SHARE' => $permissions[Acl::PERMISSION_SHARE]
]);
return $board;
}
public function isArchived($mapper, $id) {
try {
if ($mapper instanceof IPermissionMapper) {
$boardId = $mapper->findBoardId($id);
} else {
$boardId = $id;
}
if ($boardId === null) {
return false;
}
} catch (DoesNotExistException $exception) {
return false;
}
$board = $this->find($boardId);
return $board->getArchived();
}
public function isDeleted($mapper, $id) {
try {
if ($mapper instanceof IPermissionMapper) {
$boardId = $mapper->findBoardId($id);
} else {
$boardId = $id;
}
if ($boardId === null) {
return false;
}
} catch (DoesNotExistException $exception) {
return false;
}
$board = $this->find($boardId);
return $board->getDeletedAt() > 0;
}
public function create($title, $userId, $color) {
@@ -106,20 +157,44 @@ class BoardService {
}
$new_board->setLabels($labels);
$this->boardMapper->mapOwner($new_board);
$permissions = $this->permissionService->matchPermissions($new_board);
$new_board->setPermissions([
'PERMISSION_READ' => $permissions[Acl::PERMISSION_READ],
'PERMISSION_EDIT' => $permissions[Acl::PERMISSION_EDIT],
'PERMISSION_MANAGE' => $permissions[Acl::PERMISSION_MANAGE],
'PERMISSION_SHARE' => $permissions[Acl::PERMISSION_SHARE]
]);
return $new_board;
}
public function delete($id) {
$this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_READ);
return $this->boardMapper->delete($this->find($id));
$board = $this->find($id);
$board->setDeletedAt(time());
$this->boardMapper->update($board);
return $board;
}
public function update($id, $title, $color) {
public function deleteUndo($id) {
$this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_READ);
$board = $this->find($id);
$board->setDeletedAt(0);
$this->boardMapper->update($board);
}
public function deleteForce($id) {
$this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_READ);
$board = $this->find($id);
return $this->boardMapper->delete($board);
}
public function update($id, $title, $color, $archived) {
$this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_MANAGE);
$board = $this->find($id);
$board->setTitle($title);
$board->setColor($color);
$board->setArchived($archived);
$this->boardMapper->mapOwner($board);
return $this->boardMapper->update($board);
}

View File

@@ -26,18 +26,22 @@ namespace OCA\Deck\Service;
use OCA\Deck\Db\Card;
use OCA\Deck\Db\CardMapper;
use OCA\Deck\Db\Acl;
use OCA\Deck\CardArchivedException;
use OCA\Deck\Db\StackMapper;
use OCA\Deck\StatusException;
class CardService {
private $cardMapper;
private $stackMapper;
private $permissionService;
private $boardService;
public function __construct(CardMapper $cardMapper, StackMapper $stackMapper, PermissionService $permissionService) {
public function __construct(CardMapper $cardMapper, StackMapper $stackMapper, PermissionService $permissionService, BoardService $boardService) {
$this->cardMapper = $cardMapper;
$this->stackMapper = $stackMapper;
$this->permissionService = $permissionService;
$this->boardService = $boardService;
}
public function find($cardId) {
@@ -51,6 +55,9 @@ class CardService {
*/
public function create($title, $stackId, $type, $order, $owner) {
$this->permissionService->checkPermission($this->stackMapper, $stackId, Acl::PERMISSION_EDIT);
if($this->boardService->isArchived($this->stackMapper, $stackId)) {
throw new StatusException('Operation not allowed. This board is archived.');
}
$card = new Card();
$card->setTitle($title);
$card->setStackId($stackId);
@@ -63,14 +70,20 @@ class CardService {
public function delete($id) {
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
if($this->boardService->isArchived($this->cardMapper, $id)) {
throw new StatusException('Operation not allowed. This board is archived.');
}
return $this->cardMapper->delete($this->cardMapper->find($id));
}
public function update($id, $title, $stackId, $type, $order, $description, $owner) {
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
if($this->boardService->isArchived($this->cardMapper, $id)) {
throw new StatusException('Operation not allowed. This board is archived.');
}
$card = $this->cardMapper->find($id);
if ($card->getArchived()) {
throw new CardArchivedException();
throw new StatusException('Operation not allowed. This card is archived.');
}
$card->setTitle($title);
$card->setStackId($stackId);
@@ -83,9 +96,12 @@ class CardService {
public function rename($id, $title) {
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
if($this->boardService->isArchived($this->cardMapper, $id)) {
throw new StatusException('Operation not allowed. This board is archived.');
}
$card = $this->cardMapper->find($id);
if ($card->getArchived()) {
throw new CardArchivedException();
throw new StatusException('Operation not allowed. This card is archived.');
}
$card->setTitle($title);
return $this->cardMapper->update($card);
@@ -93,12 +109,15 @@ class CardService {
public function reorder($id, $stackId, $order) {
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
if($this->boardService->isArchived($this->cardMapper, $id)) {
throw new StatusException('Operation not allowed. This board is archived.');
}
$cards = $this->cardMapper->findAll($stackId);
$result = [];
$i = 0;
foreach ($cards as $card) {
if ($card->getArchived()) {
throw new CardArchivedException();
throw new StatusException('Operation not allowed. This card is archived.');
}
if ($card->id === $id) {
$card->setOrder($order);
@@ -121,6 +140,9 @@ class CardService {
public function archive($id) {
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
if($this->boardService->isArchived($this->cardMapper, $id)) {
throw new StatusException('Operation not allowed. This board is archived.');
}
$card = $this->cardMapper->find($id);
$card->setArchived(true);
return $this->cardMapper->update($card);
@@ -128,6 +150,9 @@ class CardService {
public function unarchive($id) {
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
if($this->boardService->isArchived($this->cardMapper, $id)) {
throw new StatusException('Operation not allowed. This board is archived.');
}
$card = $this->cardMapper->find($id);
$card->setArchived(false);
return $this->cardMapper->update($card);
@@ -135,18 +160,24 @@ class CardService {
public function assignLabel($cardId, $labelId) {
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
if($this->boardService->isArchived($this->cardMapper, $cardId)) {
throw new StatusException('Operation not allowed. This board is archived.');
}
$card = $this->cardMapper->find($cardId);
if ($card->getArchived()) {
throw new CardArchivedException();
throw new StatusException('Operation not allowed. This card is archived.');
}
$this->cardMapper->assignLabel($cardId, $labelId);
}
public function removeLabel($cardId, $labelId) {
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
if($this->boardService->isArchived($this->cardMapper, $cardId)) {
throw new StatusException('Operation not allowed. This board is archived.');
}
$card = $this->cardMapper->find($cardId);
if ($card->getArchived()) {
throw new CardArchivedException();
throw new StatusException('Operation not allowed. This card is archived.');
}
$this->cardMapper->removeLabel($cardId, $labelId);
}

View File

@@ -26,15 +26,22 @@ namespace OCA\Deck\Service;
use OCA\Deck\Db\Label;
use OCA\Deck\Db\Acl;
use OCA\Deck\Db\LabelMapper;
use OCA\Deck\StatusException;
class LabelService {
/** @var LabelMapper */
private $labelMapper;
/** @var PermissionService */
private $permissionService;
/** @var BoardService */
private $boardService;
public function __construct(LabelMapper $labelMapper, PermissionService $permissionService) {
public function __construct(LabelMapper $labelMapper, PermissionService $permissionService, BoardService $boardService) {
$this->labelMapper = $labelMapper;
$this->permissionService = $permissionService;
$this->boardService = $boardService;
}
public function find($labelId) {
@@ -44,6 +51,9 @@ class LabelService {
public function create($title, $color, $boardId) {
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_MANAGE);
if($this->boardService->isArchived(null, $boardId)) {
throw new StatusException('Operation not allowed. This board is archived.');
}
$label = new Label();
$label->setTitle($title);
$label->setColor($color);
@@ -53,11 +63,17 @@ class LabelService {
public function delete($id) {
$this->permissionService->checkPermission($this->labelMapper, $id, Acl::PERMISSION_MANAGE);
if($this->boardService->isArchived($this->labelMapper, $id)) {
throw new StatusException('Operation not allowed. This board is archived.');
}
return $this->labelMapper->delete($this->find($id));
}
public function update($id, $title, $color) {
$this->permissionService->checkPermission($this->labelMapper, $id, Acl::PERMISSION_MANAGE);
if($this->boardService->isArchived($this->labelMapper, $id)) {
throw new StatusException('Operation not allowed. This board is archived.');
}
$label = $this->find($id);
$label->setTitle($title);
$label->setColor($color);

View File

@@ -25,6 +25,7 @@ namespace OCA\Deck\Service;
use OCA\Deck\Db\Acl;
use OCA\Deck\Db\AclMapper;
use OCA\Deck\Db\Board;
use OCA\Deck\Db\BoardMapper;
use OCA\Deck\Db\IPermissionMapper;
use OCA\Deck\NoPermissionException;
@@ -50,7 +51,7 @@ class PermissionService {
}
/**
* Get current user permissions for a board
* Get current user permissions for a board by id
*
* @param $boardId
* @return bool|array
@@ -66,6 +67,24 @@ class PermissionService {
];
}
/**
* Get current user permissions for a board
*
* @param Board $board
* @return array|bool
* @internal param $boardId
*/
public function matchPermissions(Board $board) {
$owner = $this->userIsBoardOwner($board->getId());
$acls = $board->getAcl();
return [
Acl::PERMISSION_READ => $owner || $this->userCan($acls, Acl::PERMISSION_READ),
Acl::PERMISSION_EDIT => $owner || $this->userCan($acls, Acl::PERMISSION_EDIT),
Acl::PERMISSION_MANAGE => $owner || $this->userCan($acls, Acl::PERMISSION_MANAGE),
Acl::PERMISSION_SHARE => $owner || $this->userCan($acls, Acl::PERMISSION_SHARE),
];
}
/**
* check permissions for replacing dark magic middleware
*

View File

@@ -31,6 +31,7 @@ use OCA\Deck\Db\LabelMapper;
use OCA\Deck\Db\Stack;
use OCA\Deck\Db\StackMapper;
use OCA\Deck\StatusException;
class StackService {
@@ -39,12 +40,14 @@ class StackService {
private $cardMapper;
private $labelMapper;
private $permissionService;
private $boardService;
public function __construct(StackMapper $stackMapper, CardMapper $cardMapper, LabelMapper $labelMapper, PermissionService $permissionService) {
public function __construct(StackMapper $stackMapper, CardMapper $cardMapper, LabelMapper $labelMapper, PermissionService $permissionService, BoardService $boardService) {
$this->stackMapper = $stackMapper;
$this->cardMapper = $cardMapper;
$this->labelMapper = $labelMapper;
$this->permissionService = $permissionService;
$this->boardService = $boardService;
}
public function findAll($boardId) {
@@ -81,6 +84,9 @@ class StackService {
public function create($title, $boardId, $order) {
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_MANAGE);
if($this->boardService->isArchived(null, $boardId)) {
throw new StatusException('Operation not allowed. This board is archived.');
}
$stack = new Stack();
$stack->setTitle($title);
$stack->setBoardId($boardId);
@@ -96,6 +102,9 @@ class StackService {
public function update($id, $title, $boardId, $order) {
$this->permissionService->checkPermission($this->stackMapper, $id, Acl::PERMISSION_MANAGE);
if($this->boardService->isArchived($this->stackMapper, $id)) {
throw new StatusException('Operation not allowed. This board is archived.');
}
$stack = $this->stackMapper->find($id);
$stack->setTitle($title);
$stack->setBoardId($boardId);

View File

@@ -45,7 +45,7 @@ if(!\OC::$server->getConfig()->getSystemValue('debug', false)) {
'app' => ['App', 'Config', 'Run'],
'controller' => ['AppController', 'BoardController', 'CardController', 'ListController'],
'directive' => ['appnavigationentryutils', 'appPopoverMenuUtils', 'autofocusoninsert', 'avatar', 'elastic', 'search'],
'filters' => ['cardFilter', 'cardSearchFilter', 'iconWhiteFilter', 'lightenColorFilter', 'orderObjectBy', 'relativeDateFilter', 'textColorFilter'],
'filters' => ['boardFilterAcl', 'cardFilter', 'cardSearchFilter', 'iconWhiteFilter', 'lightenColorFilter', 'orderObjectBy', 'relativeDateFilter', 'textColorFilter'],
'service' => ['ApiService', 'BoardService', 'CardService', 'LabelService', 'StackService', 'StatusService'],
];
foreach($js as $folder=>$files) {
@@ -58,7 +58,7 @@ if(!\OC::$server->getConfig()->getSystemValue('debug', false)) {
<div id="app" class="app-deck" data-ng-app="Deck" ng-controller="AppController" ng-cloak>
<div id="app-navigation" data-ng-controller="ListController">
<div id="app-navigation" data-ng-controller="ListController" ng-init="initSidebar()">
<?php print_unescaped($this->inc('part.navigation')); ?>
<?php /* print_unescaped($this->inc('part.settings')); */ ?>
</div>

View File

@@ -64,46 +64,40 @@
</ul>
</div>
<div class="card-assignees" ng-if="c.assignees">
<!-- <div class="avatar" avatar user="{{c.owner}}" size="24"></div>//-->
</div>
<div class="card-controls">
<i class="icon icon-filetype-text" ng-if="c.description" title="{{ c.description }}"></i>
<div class="app-popover-menu-utils">
<div class="app-popover-menu-utils" ng-if="!boardservice.isArchived()">
<button class="button-inline card-options icon-more" ng-model="card"></button>
<div class="popovermenu hidden">
<ul>
<li ng-if="filter!=='archive'">
<a class="menuitem action action-rename permanent"
data-action="Archive"
ng-click="cardArchive(c); $event.stopPropagation();"><span
class="icon icon-archive"></span><span><?php p($l->t('Archive')); ?></span></a>
data-action="Archive"
ng-click="cardArchive(c); $event.stopPropagation();"><span
class="icon icon-archive"></span><span><?php p($l->t('Archive')); ?></span></a>
</li>
<li ng-if="filter==='archive'">
<a class="menuitem action action-rename permanent"
data-action="Unarchive"
ng-click="cardUnarchive(c); $event.stopPropagation();"><span
class="icon icon-archive"></span><span><?php p($l->t('Unarchive')); ?></span></a>
data-action="Unarchive"
ng-click="cardUnarchive(c); $event.stopPropagation();"><span
class="icon icon-archive"></span><span><?php p($l->t('Unarchive')); ?></span></a>
</li>
<li>
<a class="menuitem action action-delete permanent"
data-action="Delete"
ng-click="cardDelete(c)"><span
class="icon icon-delete"></span><span><?php p($l->t('Delete')); ?></span></a>
data-action="Delete"
ng-click="cardDelete(c)"><span
class="icon icon-delete"></span><span><?php p($l->t('Delete')); ?></span></a>
</li>
</ul>
</div>
</div>
</div>
<!--<span class="info due"><i class="fa fa-clock-o" aria-hidden="true"></i> <span>Today</span></span>
<span class="info tasks"><i class="fa fa-list" aria-hidden="true"></i> <span>3/12</span></span>
//-->
</div>
</li>
</ul>
<!-- CREATE CARD //-->
<div class="card create"
style="background-color:#{{ boardservice.getCurrent().color }};" ng-if="boardservice.canEdit() && checkCanEdit() && filter!=='archive'">

View File

@@ -9,6 +9,7 @@
<h2>{{ boardservice.getCurrent().title }}</h2>
</div>
{{board=boardservice.getCurrent();""}}
<ul class="tabHeaders">
<li class="tabHeader" ng-class="{'selected': (status.boardtab==0 || !status.boardtab)}" ng-click="status.boardtab=0"><a><?php p($l->t('Sharing')); ?></a></li>
@@ -90,7 +91,7 @@
<input type="text" class="input-inline" ng-model="newLabel.title" style="color:{{ newLabel.color|textColorFilter }};" autofocus-on-insert maxlength="100" />
</span>
<div class="colorselect">
<div class="color" ng-repeat="c in defaultColors" style="background-color:#{{ c }};" ng-click="newLabel.color=c" ng-class="{'selected': (c == newLabel.color) }"><br /></div>
<div class="color" ng-repeat="c in defaultColors" style="background-color:#{{ c }};" ng-click="newLabel.color=c" ng-class="{'selected': (c == newLabel.color), 'dark': (newBoard.color | textColorFilter) === '#ffffff' }"><br /></div>
</div>
<a ng-click="labelCreate(newLabel)" class="icon"><i class="icon icon-checkmark" ></i></a>
<a ng-click="status.createLabel=false" class="icon icon-close"></a>

View File

@@ -5,31 +5,66 @@
<td class="cell-board-bullet"></td>
<td class="cell-board-title" width="90%"><?php p($l->t('Title')); ?></td>
<td class="cell-board-members"><?php p($l->t('Members')); ?></td>
<td></td>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="b in boardservice.sorted"
ui-sref="board({boardId: b.id})">
<td>
<tr data-ng-repeat="b in boardservice.sorted" ng-class="{deleted: b.deletedAt > 0}">
<td ng-click="gotoBoard(b)">
<span class="board-bullet"
style="background-color:#{{b.color}};"> </span>
</td>
<td><a href="#/board/{{b.id}}">{{ b.title }}</a></td>
<td ng-click="gotoBoard(b)">{{ b.title }}</a></td>
<td>
<div id="assigned-users">
<div class="avatardiv" avatar displayname="{{ b.owner.uid }}" title="{{ b.owner.displayname }}"></div>
<div class="avatardiv" avatar displayname="{{ acl.participant.uid }}" title="{{ acl.participant.uid }}" ng-repeat="acl in b.acl | limitTo: 7"></div>
</div>
</td>
</tr>
<tr>
<td><span class="icon icon-add"></span></td>
<td>
<div class="hint"></div>
<div class="app-popover-menu-utils" ng-if="b.deletedAt == 0">
<button class="icon icon-more button-inline" title="More actions"></button>
<div class="popovermenu bubble hidden">
<ul>
<li ng-if="boardservice.canManage(b) && !b.archived" ng-click="boardArchive(b)">
<a class="menuitem"><span class="icon-archive"></span> <?php p($l->t('Archive board')); ?>
</a>
</li>
<li ng-if="boardservice.canManage(b) && b.archived" ng-click="boardUnarchive(b)">
<a class="menuitem"><span class="icon-archive"></span> <?php p($l->t('Unarchive board')); ?>
</a>
</li>
<li ng-if="boardservice.canManage(b)" ng-click="boardDelete(b)">
<a class="menuitem"><span class="icon-delete"></span> <?php p($l->t('Delete board')); ?>
</a>
</li>
<li ui-sref="board.detail({boardId: b.id})">
<a class="menuitem"><span class="icon-info"></span> <?php p($l->t('Board details')); ?>
</a>
</li>
</ul>
</div>
</div>
<div class="app-popover-menu-utils" ng-if="b.deletedAt > 0">
<button class="icon icon-history button-inline" ng-click="boardDeleteUndo(b)" title="Undo board deletion - Otherwise the board will be deleted during the next cronjob run."></button>
</div>
</td>
</tr>
<tr ng-if="status.filter === '' && !status.addBoard" ng-click="status.addBoard=!status.addBoard">
<td><span class="icon icon-add"></span></td>
<td colspan="3">
<a ng-click="status.addBoard=!status.addBoard"
ng-show="!status.addBoard">
<?php p($l->t('Create new board')); ?>
</a>
<form ng-show="status.addBoard" ng-disabled="isAddingList"
</td>
</tr>
<tr ng-if="status.filter === '' && status.addBoard">
<td><span class="icon icon-add"></span></td>
<td>
<form ng-disabled="isAddingList"
class="ng-pristine ng-valid" ng-submit="boardCreate()">
<input id="newTitle" class="edit ng-valid ng-empty"
type="text" placeholder="<?php p($l->t('New board title')); ?>"
@@ -38,13 +73,13 @@
<div class="color" ng-repeat="c in colors"
style="background-color:#{{ c }};"
ng-click="selectColor(c)"
ng-class="{'selected': (c == newBoard.color) }">
<br/></div>
ng-class="{'selected': (c == newBoard.color), 'dark': (newBoard.color | textColorFilter) === '#ffffff' }"></div>
</div>
<input type="submit" value="" class="icon-checkmark svg">
<input type="submit" value="" class="icon-checkmark svg" />
</form>
</td>
<td></td>
<td></td>
</tr>
</tbody>
</table>

View File

@@ -31,6 +31,7 @@
<div id="labels">
<ui-select multiple tagging="" ng-model="card.labels" theme="select2"
ng-disabled="boardservice.isArchived() || card.archived"
style="width:100%;" title="Choose a label"
placeholder="Add a label"
on-select="labelAssign($item, $model)"

View File

@@ -1,28 +1,24 @@
<ul class="with-icon">
<li><a href="#" class=""><?php p($l->t('All Boards')); ?></a></li>
<li ng-class="{active: status.filter === '' && !boardservice.getCurrent()}"><a ui-sref="list({ filter: ''})" class="icon-deck"><?php p($l->t('All Boards')); ?></a></li>
<li ng-class="{active: status.filter === 'archived' || (boardservice.getCurrent() && boardservice.getCurrent().archived)}"><a ui-sref="list({ filter: 'archived' })" class="icon-archive"><?php p($l->t('Archived boards')); ?></a></li>
<li ng-class="{active: status.filter === 'shared'}"><a ui-sref="list({ filter: 'shared' })" class="icon-share"><?php p($l->t('Shared boards')); ?></a></li>
<li class="with-icon with-menu" ng-class="{active: b.id === boardservice.getCurrent().id}" data-ng-repeat="b in boardservice.sorted">
<li class="with-icon with-menu" ng-class="{active: b.id === boardservice.getCurrent().id}" data-ng-repeat="b in boardservice.sidebar" ng-if="b.deletedAt == 0">
<span class="board-bullet" style="background-color:#{{b.color}};" ng-if="!b.status.edit"> </span>
<a href="#!/board/{{b.id}}/" ng-if="!b.status.edit">{{ b.title }}</a>
<div class="app-navigation-entry-utils" ng-show="!b.status.edit" style="position:absolute;">
<ul>
<li class="app-navigation-entry-utils-counter board-delete-undo" ng-show="status.deleteUndo[b.id]" ng-click="boardDeleteUndo(b)" title="Click to undo">Deleting in {{ status.deleteUndo[b.id] }}s &nbsp; X</li>
<li class="app-navigation-entry-utils-menu-share svg" ng-show="b.shared>0"><i class="icon icon-share" title="<?php p($l->t('Shared with you')); ?>"> </i></li>
<li class="app-navigation-entry-utils-menu-button svg" ng-show="!status.deleteUndo[b.id]"><button class="icon-more"></button></li>
</ul>
</div>
<div class="app-navigation-entry-menu app-navigation-noclose" ng-show="!b.status.edit">
<ul>
<li ng-show="b.owner.uid===user"><button class="icon-rename svg" title="<?php p($l->t('edit')); ?>" ng-click="b.status.edit=true"></button></li>
<li ng-show="b.owner.uid===user"><button class="icon-delete svg" title="<?php p($l->t('delete')); ?>" ng-click="boardDelete(b)"></button></li>
<li ng-show="b.owner.uid!==user && false"><button class="icon-delete svg" title="<?php p($l->t('remove share')); ?>" ng-click="boardRemoveShare(b)"></button></li>
<li ng-show="boardservice.canManage(b)"><button class="icon-rename svg" title="<?php p($l->t('Edit board')); ?>" ng-click="b.status.edit=true"></button></li>
<li ng-show="boardservice.canManage(b)"><button class="icon-archive svg" title="<?php p($l->t('Move board to archive')); ?>" ng-click="boardArchive(b)"></button></li>
</ul>
</div>
<div class="app-navigation-entry-deleted" ng-show="false">
<div class="app-navigation-entry-deleted-description">Deleted X</div>
<button class="app-navigation-entry-deleted-button icon-history svg" title="Undo"></button>
</div>
<div class="app-navigation-entry-edit" ng-show="b.status.edit">
<form ng-disabled="isAddingList" class="ng-pristine ng-valid" ng-submit="boardUpdate(b)">
<input id="newTitle" class="edit ng-valid ng-empty" type="text" autofocus-on-insert ng-model="b.title" maxlength="100">
@@ -43,7 +39,7 @@
<input id="newTitle" class="edit ng-valid ng-empty" type="text" placeholder="<?php p($l->t('New board title')); ?>" autofocus-on-insert ng-model="newBoard.title" maxlength="100">
<input type="submit" value="" class="action icon-checkmark svg">
<div class="colorselect">
<div class="color" ng-repeat="c in colors" style="background-color:#{{ c }};" ng-click="selectColor(c)" ng-class="{'selected': (c == newBoard.color) }"><br /></div>
<div class="color" ng-repeat="c in colors" style="background-color:#{{ c }};" ng-click="selectColor(c)" ng-class="{'selected': (c == newBoard.color), 'dark': (newBoard.color | textColorFilter) === '#ffffff' }"><br /></div>
</div>
</form>
</div>

View File

@@ -24,7 +24,7 @@
/**
* @group DB
*/
class BoardDatabaseTest extends \PHPUnit_Framework_TestCase
class BoardDatabaseTest extends \Test\TestCase
{
const TEST_USER1 = "test-share-user1";
const TEST_USER2 = "test-share-user2";
@@ -61,6 +61,7 @@ class BoardDatabaseTest extends \PHPUnit_Framework_TestCase
\OC::$server->getGroupManager()->addBackend($groupBackend);
}
public function setUp() {
parent::setUp();
\OC::$server->getUserSession()->login(self::TEST_USER1, self::TEST_USER1);
$this->boardService = \OC::$server->query("\OCA\Deck\Service\BoardService");
}
@@ -76,9 +77,10 @@ class BoardDatabaseTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($actual->getTitle(), $board->getTitle());
$this->assertEquals($actual->getColor(), $board->getColor());
$this->assertEquals($actual->getOwner(), $board->getOwner());
$this->boardService->delete($id);
$this->boardService->deleteForce($id);
}
public function tearDown() {
parent::tearDown();
}
}

View File

@@ -0,0 +1,72 @@
<?php
/**
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Deck\Cron;
use OCA\Deck\Db\Board;
use OCA\Deck\Db\BoardMapper;
class DeleteCronTest extends \Test\TestCase {
/** @var BoardMapper|\PHPUnit_Framework_MockObject_MockObject */
protected $boardMapper;
/** @var DeleteCron */
protected $deleteCron;
public function setUp() {
parent::setUp();
$this->boardMapper = $this->createMock(BoardMapper::class);
$this->deleteCron = new DeleteCron($this->boardMapper);
}
protected function getBoard($id) {
$board = new Board();
$board->setId($id);
return $board;
}
public function testDeleteCron() {
$boards = [
$this->getBoard(1),
$this->getBoard(2),
$this->getBoard(3),
$this->getBoard(4),
];
$this->boardMapper->expects($this->once())
->method('findToDelete')
->willReturn($boards);
$this->boardMapper->expects($this->at(1))
->method('delete')
->with($boards[0]);
$this->boardMapper->expects($this->at(2))
->method('delete')
->with($boards[1]);
$this->boardMapper->expects($this->at(3))
->method('delete')
->with($boards[2]);
$this->boardMapper->expects($this->at(4))
->method('delete')
->with($boards[3]);
$this->invokePrivate($this->deleteCron, 'run', [null]);
}
}

View File

@@ -23,6 +23,7 @@
namespace OCA\Deck\Db;
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\IUserManager;
use Test\AppFramework\Db\MapperTestUtility;
@@ -32,10 +33,15 @@ use Test\AppFramework\Db\MapperTestUtility;
*/
class BoardMapperTest extends MapperTestUtility {
/** @var IDBConnection */
private $dbConnection;
/** @var AclMapper|\PHPUnit_Framework_MockObject_MockObject */
private $aclMapper;
/** @var BoardMapper */
private $boardMapper;
/** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */
private $userManager;
/** @var IGroupManager|\PHPUnit_Framework_MockObject_MockObject */
private $groupManager;
// Data
@@ -72,7 +78,7 @@ class BoardMapperTest extends MapperTestUtility {
$this->aclMapper->insert($this->getAcl('user','user1', false, false, false, $this->boards[2]->getId()))
];
foreach ($this->acls as $acl) {
foreach ($this->acls as $acl) {
$acl->resetUpdatedFields();
}
foreach ($this->boards as $board) {
@@ -101,14 +107,55 @@ class BoardMapperTest extends MapperTestUtility {
public function testFind() {
$actual = $this->boardMapper->find($this->boards[0]->getId());
$expected = $this->boards[0];
/** @var Board $expected */
$expected = clone $this->boards[0];
$expected->setShared(-1);
$expected->resetUpdatedFields();
$this->assertEquals($expected, $actual);
}
public function testFindAllByUser() {
$actual = $this->boardMapper->findAllByUser('user1');
$expected = [
$this->boards[0],
$this->boards[1],
$this->boards[2]
];
foreach ($expected as $e) {
foreach ($actual as $a) {
if($e->getId() === $a->getId()) {
$this->assertEquals($e->getTitle(), $a->getTitle());
}
}
}
}
public function testFindAll() {
$actual = $this->boardMapper->findAll();
$this->assertEquals($this->boards[0]->getId(), $actual[0]->getId());
$this->assertEquals($this->boards[1]->getId(), $actual[1]->getId());
$this->assertEquals($this->boards[2]->getId(), $actual[2]->getId());
}
public function testFindAllToDelete() {
$this->boards[0]->setDeletedAt(1);
$this->boards[0] = $this->boardMapper->update($this->boards[0]);
$actual = $this->boardMapper->findToDelete();
$this->boards[0]->resetUpdatedFields();
$this->assertEquals([$this->boards[0]], $actual);
$this->boards[0]->setDeletedAt(0);
$this->boardMapper->update($this->boards[0]);
}
public function testFindWithLabels() {
$actual = $this->boardMapper->find($this->boards[0]->getId(), true, false);
$expected = $this->boards[0];
$this->assertEquals($expected, $actual);
/** @var Board $expected */
$expected = $this->boards[0];
$this->assertEquals($expected->getLabels(), $actual->getLabels());
}
public function testFindWithAcl() {
$actual = $this->boardMapper->find($this->boards[0]->getId(), false, true);
$expected = [

View File

@@ -2,7 +2,9 @@
namespace OCA\Deck\Db;
class BoardTest extends \PHPUnit_Framework_TestCase {
use PHPUnit\Framework\TestCase;
class BoardTest extends TestCase {
private function createBoard() {
$board = new Board();
$board->setId(1);
@@ -21,6 +23,8 @@ class BoardTest extends \PHPUnit_Framework_TestCase {
'owner' => "admin",
'color' => "000000",
'labels' => array(),
'permissions' => [],
'deletedAt' => 0,
'acl' => array(),
'archived' => false
], $board->jsonSerialize());
@@ -35,6 +39,8 @@ class BoardTest extends \PHPUnit_Framework_TestCase {
'owner' => "admin",
'color' => "000000",
'labels' => array("foo", "bar"),
'permissions' => [],
'deletedAt' => 0,
'acl' => array(),
'archived' => false
], $board->jsonSerialize());
@@ -56,6 +62,8 @@ class BoardTest extends \PHPUnit_Framework_TestCase {
'owner' => "admin",
'color' => "000000",
'labels' => array(),
'permissions' => [],
'deletedAt' => 0,
'acl' => array(),
'archived' => false,
'shared' => 1,

View File

@@ -0,0 +1,58 @@
<?php
/**
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Deck\Db;
use OCP\IGroup;
class GroupTest extends \Test\TestCase {
public function testGroupObjectSerialize() {
/** @var IGroup $group */
$group = $this->createMock(IGroup::class);
$group->expects($this->any())
->method('getGID')
->willReturn('mygroup');
$groupRelationalObject = new Group($group);
$expected = [
'uid' => 'mygroup',
'displayname' => 'mygroup'
];
$this->assertEquals($expected, $groupRelationalObject->getObjectSerialization());
}
public function testGroupJSONSerialize() {
/** @var IGroup $group */
$group = $this->createMock(IGroup::class);
$group->expects($this->any())
->method('getGID')
->willReturn('mygroup');
$groupRelationalObject = new Group($group);
$expected = [
'uid' => 'mygroup',
'displayname' => 'mygroup',
'primaryKey' => 'mygroup'
];
$this->assertEquals($expected, $groupRelationalObject->jsonSerialize());
}
}

View File

@@ -0,0 +1,64 @@
<?php
/**
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Deck\Db;
use OCP\IUser;
class UserTest extends \Test\TestCase {
public function testGroupObjectSerialize() {
/** @var IUser $user */
$user = $this->createMock(IUser::class);
$user->expects($this->any())
->method('getUID')
->willReturn('myuser');
$user->expects($this->any())
->method('getDisplayName')
->willReturn('myuser displayname');
$userRelationalObject = new User($user);
$expected = [
'uid' => 'myuser',
'displayname' => 'myuser displayname'
];
$this->assertEquals($expected, $userRelationalObject->getObjectSerialization());
}
public function testGroupJSONSerialize() {
/** @var IUser $user */
$user = $this->createMock(IUser::class);
$user->expects($this->any())
->method('getUID')
->willReturn('myuser');
$user->expects($this->any())
->method('getDisplayName')
->willReturn('myuser displayname');
$userRelationalObject = new User($user);
$expected = [
'uid' => 'myuser',
'displayname' => 'myuser displayname',
'primaryKey' => 'myuser'
];
$this->assertEquals($expected, $userRelationalObject->jsonSerialize());
}
}

View File

@@ -23,7 +23,7 @@
namespace OCA\Deck\Db;
use OCA\Deck\CardArchivedException;
use OCA\Deck\ArchivedItemException;
use OCA\Deck\Controller\PageController;
use OCA\Deck\NoPermissionException;
use OCA\Deck\NotFoundException;
@@ -45,7 +45,7 @@ class ExceptionsTest extends \PHPUnit_Framework_TestCase {
}
public function testCardArchivedException() {
$e = new CardArchivedException('foo');
$e = new ArchivedItemException('foo');
$this->assertEquals('foo', $e->getMessage());
}

View File

@@ -0,0 +1,128 @@
<?php
/**
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Deck\Migration;
use OCA\Deck\Db\Acl;
use OCA\Deck\Db\AclMapper;
use OCA\Deck\Db\Board;
use OCA\Deck\Db\BoardMapper;
use OCP\IGroupManager;
use OCP\IUserManager;
use OCP\Migration\IOutput;
class UnknownUserTest extends \Test\TestCase {
/** @var IUserManager */
private $userManager;
/** @var IGroupManager */
private $groupManager;
/** @var AclMapper */
private $aclMapper;
/** @var BoardMapper */
private $boardMapper;
/** @var UnknownUsers */
private $unknownUsers;
public function setUp() {
parent::setUp();
$this->userManager = $this->createMock(IUserManager::class);
$this->groupManager = $this->createMock(IGroupManager::class);
$this->aclMapper = $this->createMock(AclMapper::class);
$this->boardMapper = $this->createMock(BoardMapper::class);
$this->unknownUsers = new UnknownUsers($this->userManager, $this->groupManager, $this->aclMapper, $this->boardMapper);
}
public function testGetName() {
$this->assertEquals('Delete orphaned ACL rules', $this->unknownUsers->getName());
}
public function testRun() {
/** @var IOutput $output */
$output = $this->createMock(IOutput::class);
$boards = [
$this->getBoard(1,'Test', 'admin'),
];
$acl = [
$this->getAcl('user', 'existing', 1),
$this->getAcl('user', 'not existing', 1),
$this->getAcl('group', 'existing', 1),
$this->getAcl('group', 'not existing', 1),
];
$this->aclMapper->expects($this->at(0))
->method('findAll')
->with(1)
->willReturn($acl);
$this->userManager->expects($this->at(0))
->method('get')
->with('existing')
->willReturn(true);
$this->userManager->expects($this->at(1))
->method('get')
->with('not existing')
->willReturn(null);
$this->groupManager->expects($this->at(0))
->method('get')
->with('existing')
->willReturn(true);
$this->groupManager->expects($this->at(1))
->method('get')
->with('not existing')
->willReturn(null);
$this->boardMapper->expects($this->once())
->method('findAll')
->willReturn($boards);
$this->aclMapper->expects($this->at(1))
->method('delete')
->with($acl[1]);
$this->aclMapper->expects($this->at(2))
->method('delete')
->with($acl[3]);
$this->unknownUsers->run($output);
}
/** @return Acl */
public function getAcl($type='user', $participant='admin', $boardId=123) {
$acl = new Acl();
$acl->setParticipant($participant);
$acl->setType($type);
$acl->setBoardId($boardId);
return $acl;
}
/** @return Board */
public function getBoard($id, $title, $owner) {
$board = new Board();
$board->setId($id);
$board->setTitle($title);
$board->setOwner($owner);
return $board;
}
}

View File

@@ -53,6 +53,7 @@ class BoardServiceTest extends \Test\TestCase {
private $userId = 'admin';
public function setUp() {
parent::setUp();
$this->l10n = $this->createMock(L10N::class);
$this->aclMapper = $this->createMock(AclMapper::class);
$this->boardMapper = $this->createMock(BoardMapper::class);
@@ -134,11 +135,12 @@ class BoardServiceTest extends \Test\TestCase {
->method('update')
->with($board)
->willReturn($board);
$b = $this->service->update(123, 'MyNewNameBoard', 'ffffff');
$b = $this->service->update(123, 'MyNewNameBoard', 'ffffff', false);
$this->assertEquals($b->getTitle(), 'MyNewNameBoard');
$this->assertEquals($b->getOwner(), 'admin');
$this->assertEquals($b->getColor(), 'ffffff');
$this->assertEquals($b->getArchived(), false);
}
public function testDelete() {
@@ -147,10 +149,7 @@ class BoardServiceTest extends \Test\TestCase {
$this->boardMapper->expects($this->once())
->method('find')
->willReturn($board);
$this->boardMapper->expects($this->once())
->method('delete')
->willReturn(1);
$this->assertEquals(1, $this->service->delete(123));
$this->assertEquals($board, $this->service->delete(123));
}
public function testAddAcl() {

View File

@@ -27,9 +27,10 @@ namespace OCA\Deck\Service;
use OCA\Deck\Db\Card;
use OCA\Deck\Db\CardMapper;
use OCA\Deck\Db\StackMapper;
use OCA\Deck\CardArchivedException;
use OCA\Deck\StatusException;
use Test\TestCase;
class CardServiceTest extends \PHPUnit_Framework_TestCase {
class CardServiceTest extends TestCase {
/** @var CardService|\PHPUnit_Framework_MockObject_MockObject */
private $cardService;
@@ -39,16 +40,19 @@ class CardServiceTest extends \PHPUnit_Framework_TestCase {
private $stackMapper;
/** @var PermissionService|\PHPUnit_Framework_MockObject_MockObject */
private $permissionService;
/** @var BoardService|\PHPUnit_Framework_MockObject_MockObject */
private $boardService;
public function setUp() {
parent::setUp();
$this->cardMapper = $this->getMockBuilder(CardMapper::class)
->disableOriginalConstructor()->getMock();
$this->stackMapper = $this->getMockBuilder(StackMapper::class)
->disableOriginalConstructor()->getMock();
$this->permissionService = $this->getMockBuilder(PermissionService::class)
->disableOriginalConstructor()->getMock();
$this->cardService = new CardService($this->cardMapper, $this->stackMapper, $this->permissionService);
$this->boardService = $this->createMock(BoardService::class);
$this->cardService = new CardService($this->cardMapper, $this->stackMapper, $this->permissionService, $this->boardService);
}
public function testFind() {
@@ -108,7 +112,7 @@ class CardServiceTest extends \PHPUnit_Framework_TestCase {
$card->setArchived(true);
$this->cardMapper->expects($this->once())->method('find')->willReturn($card);
$this->cardMapper->expects($this->never())->method('update');
$this->setExpectedException(CardArchivedException::class);
$this->setExpectedException(StatusException::class);
$this->cardService->update(123, 'newtitle', 234, 'text', 999, 'foo', 'admin');
}
@@ -128,7 +132,7 @@ class CardServiceTest extends \PHPUnit_Framework_TestCase {
$card->setArchived(true);
$this->cardMapper->expects($this->once())->method('find')->willReturn($card);
$this->cardMapper->expects($this->never())->method('update');
$this->setExpectedException(CardArchivedException::class);
$this->setExpectedException(StatusException::class);
$this->cardService->rename(123, 'newtitle');
}
@@ -168,7 +172,7 @@ class CardServiceTest extends \PHPUnit_Framework_TestCase {
$card->setArchived(true);
$this->cardMapper->expects($this->once())->method('findAll')->willReturn([$card]);
$this->cardMapper->expects($this->never())->method('update')->willReturnCallback(function($c) { return $c; });
$this->setExpectedException(CardArchivedException::class);
$this->setExpectedException(StatusException::class);
$actual = $this->cardService->reorder(123, 234, 1);
}
public function testArchive() {
@@ -204,7 +208,7 @@ class CardServiceTest extends \PHPUnit_Framework_TestCase {
$card->setArchived(true);
$this->cardMapper->expects($this->once())->method('find')->willReturn($card);
$this->cardMapper->expects($this->never())->method('assignLabel');
$this->setExpectedException(CardArchivedException::class);
$this->setExpectedException(StatusException::class);
$this->cardService->assignLabel(123, 999);
}
@@ -221,7 +225,7 @@ class CardServiceTest extends \PHPUnit_Framework_TestCase {
$card->setArchived(true);
$this->cardMapper->expects($this->once())->method('find')->willReturn($card);
$this->cardMapper->expects($this->never())->method('removeLabel');
$this->setExpectedException(CardArchivedException::class);
$this->setExpectedException(StatusException::class);
$this->cardService->removeLabel(123, 999);
}

View File

@@ -26,8 +26,9 @@ namespace OCA\Deck\Service;
use OCA\Deck\Db\Label;
use OCA\Deck\Db\LabelMapper;
use Test\TestCase;
class LabelServiceTest extends \PHPUnit_Framework_TestCase {
class LabelServiceTest extends TestCase {
/** @var LabelMapper|\PHPUnit_Framework_MockObject_MockObject */
private $labelMapper;
@@ -35,15 +36,20 @@ class LabelServiceTest extends \PHPUnit_Framework_TestCase {
private $permissionService;
/** @var LabelService */
private $labelService;
/** @var BoardService|\PHPUnit_Framework_MockObject_MockObject */
private $boardService;
public function setUp() {
parent::setUp();
$this->labelMapper = $this->getMockBuilder(LabelMapper::class)
->disableOriginalConstructor()->getMock();
$this->permissionService = $this->getMockBuilder(PermissionService::class)
->disableOriginalConstructor()->getMock();
$this->boardService = $this->createMock(BoardService::class);
$this->labelService = new LabelService(
$this->labelMapper,
$this->permissionService
$this->permissionService,
$this->boardService
);
}

View File

@@ -46,6 +46,7 @@ class PermissionServiceTest extends \PHPUnit_Framework_TestCase {
private $userId = 'admin';
public function setUp() {
parent::setUp();
$this->logger = $this->request = $this->getMockBuilder(ILogger::class)
->disableOriginalConstructor()
->getMock();

View File

@@ -31,8 +31,15 @@ use OCA\Deck\Db\Label;
use OCA\Deck\Db\LabelMapper;
use OCA\Deck\Db\Stack;
use OCA\Deck\Db\StackMapper;
use \Test\TestCase;
class StackServiceTest extends \PHPUnit_Framework_TestCase {
/**
* Class StackServiceTest
*
* @package OCA\Deck\Service
* @group DB
*/
class StackServiceTest extends TestCase {
/** @var StackService */
private $stackService;
@@ -44,8 +51,11 @@ class StackServiceTest extends \PHPUnit_Framework_TestCase {
private $labelMapper;
/** @var \PHPUnit_Framework_MockObject_MockObject|PermissionService */
private $permissionService;
/** @var BoardService|\PHPUnit_Framework_MockObject_MockObject */
private $boardService;
public function setUp() {
parent::setUp();
$this->stackMapper = $this->getMockBuilder(StackMapper::class)
->disableOriginalConstructor()->getMock();
$this->cardMapper = $this->getMockBuilder(CardMapper::class)
@@ -54,12 +64,14 @@ class StackServiceTest extends \PHPUnit_Framework_TestCase {
->disableOriginalConstructor()->getMock();
$this->permissionService = $this->getMockBuilder(PermissionService::class)
->disableOriginalConstructor()->getMock();
$this->boardService = $this->createMock(BoardService::class);
$this->stackService = new StackService(
$this->stackMapper,
$this->cardMapper,
$this->labelMapper,
$this->permissionService
$this->permissionService,
$this->boardService
);
}
@@ -160,6 +172,9 @@ class StackServiceTest extends \PHPUnit_Framework_TestCase {
$this->assertEquals($stack, $result);
}
/**
* @group DB
*/
public function testReorder() {
$this->permissionService->expects($this->once())->method('checkPermission');
$a = $this->createStack(1, 0);

View File

@@ -107,9 +107,9 @@ class BoardControllerTest extends \PHPUnit_Framework_TestCase {
public function testUpdate() {
$this->boardService->expects($this->once())
->method('update')
->with(1, 2, 3)
->with(1, 2, 3, false)
->willReturn(1);
$this->assertEquals(1, $this->controller->update(1, 2, 3));
$this->assertEquals(1, $this->controller->update(1, 2, 3, false));
}
public function testDelete() {
@@ -120,6 +120,14 @@ class BoardControllerTest extends \PHPUnit_Framework_TestCase {
$this->assertEquals(1, $this->controller->delete(123));
}
public function testDeleteUndo() {
$this->boardService->expects($this->once())
->method('deleteUndo')
->with(123)
->willReturn(1);
$this->assertEquals(1, $this->controller->deleteUndo(123));
}
public function testGetUserPermissions() {
$acl = [
Acl::PERMISSION_READ => true,