Merge pull request #557 from nextcloud/feature/undo-delete-cards-and-stacks
Feature/undo delete cards and stacks
This commit is contained in:
@@ -77,6 +77,14 @@
|
||||
<length>8</length>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
<field>
|
||||
<name>deleted_at</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<length>8</length>
|
||||
<notnull>false</notnull>
|
||||
<unsigned>true</unsigned>
|
||||
</field>
|
||||
<index>
|
||||
<name>deck_stacks_board_id_index</name>
|
||||
<field>
|
||||
@@ -167,6 +175,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>
|
||||
<index>
|
||||
<name>deck_cards_stack_id_index</name>
|
||||
<field>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
- 🚀 Get your project organized
|
||||
|
||||
</description>
|
||||
<version>0.5.0-dev1</version>
|
||||
<version>0.5.0-dev2</version>
|
||||
<licence>agpl</licence>
|
||||
<author>Julius Härtl</author>
|
||||
<namespace>Deck</namespace>
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
* @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/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
return [
|
||||
@@ -43,6 +43,7 @@ return [
|
||||
['name' => 'stack#update', 'url' => '/stacks/{stackId}', 'verb' => 'PUT'],
|
||||
['name' => 'stack#reorder', 'url' => '/stacks/{stackId}/reorder', 'verb' => 'PUT'],
|
||||
['name' => 'stack#delete', 'url' => '/stacks/{stackId}', 'verb' => 'DELETE'],
|
||||
['name' => 'stack#deleted', 'url' => '/{boardId}/stacks/deleted', 'verb' => 'GET'],
|
||||
['name' => 'stack#archived', 'url' => '/stacks/{boardId}/archived', 'verb' => 'GET'],
|
||||
|
||||
// cards
|
||||
@@ -50,6 +51,7 @@ return [
|
||||
['name' => 'card#create', 'url' => '/cards', 'verb' => 'POST'],
|
||||
['name' => 'card#update', 'url' => '/cards/{cardId}', 'verb' => 'PUT'],
|
||||
['name' => 'card#delete', 'url' => '/cards/{cardId}', 'verb' => 'DELETE'],
|
||||
['name' => 'card#deleted', 'url' => '/{boardId}/cards/deleted', 'verb' => 'GET'],
|
||||
['name' => 'card#rename', 'url' => '/cards/{cardId}/rename', 'verb' => 'PUT'],
|
||||
['name' => 'card#reorder', 'url' => '/cards/{cardId}/reorder', 'verb' => 'PUT'],
|
||||
['name' => 'card#archive', 'url' => '/cards/{cardId}/archive', 'verb' => 'PUT'],
|
||||
|
||||
@@ -1170,6 +1170,16 @@ input.input-inline {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.board-detail__deleted-list__item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
* {
|
||||
flex-basis: 20%;
|
||||
}
|
||||
}
|
||||
|
||||
#board-detail-labels {
|
||||
ul li {
|
||||
input {
|
||||
@@ -1215,12 +1225,18 @@ input.input-inline {
|
||||
|
||||
.tabHeaders {
|
||||
clear: both;
|
||||
overflow: hidden;
|
||||
overflow: initial;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.tabsContainer {
|
||||
margin-top: 15px;
|
||||
height: 100%;
|
||||
|
||||
.tab {
|
||||
height: 100%;
|
||||
overflow: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-select-offscreen {
|
||||
|
||||
@@ -4,20 +4,20 @@
|
||||
* @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/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
import app from '../app/App.js';
|
||||
@@ -42,6 +42,15 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
|
||||
$scope.board = BoardService.getCurrent();
|
||||
$scope.uploader = FileService.uploader;
|
||||
|
||||
$scope.$watch(function() {
|
||||
return $state.current;
|
||||
}, function(currentState) {
|
||||
if(currentState.name === 'board.detail') {
|
||||
CardService.fetchDeleted($scope.id);
|
||||
StackService.fetchDeleted($scope.id);
|
||||
}
|
||||
});
|
||||
|
||||
// workaround for $stateParams changes not being propagated
|
||||
$scope.$watch(function() {
|
||||
return $state.params;
|
||||
@@ -186,20 +195,58 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
|
||||
});
|
||||
};
|
||||
|
||||
$scope.stackDelete = function (stack) {
|
||||
$scope.stackservice.delete(stack.id);
|
||||
};
|
||||
|
||||
$scope.stackUndoDelete = function (deletedStack) {
|
||||
return StackService.undoDelete(deletedStack);
|
||||
};
|
||||
|
||||
$scope.cardDelete = function (card) {
|
||||
OC.dialogs.confirm(t('deck', 'Are you sure you want to delete this card with all of its data?'), t('deck', 'Delete'), function(state) {
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
CardService.delete(card.id).then(function () {
|
||||
StackService.removeCard(card);
|
||||
});
|
||||
CardService.delete(card.id).then(function () {
|
||||
StackService.removeCard(card);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.cardOrCardAndStackUndoDelete = function (deletedCard) {
|
||||
var associatedDeletedStack = $scope.stackservice.deleted[deletedCard.stackId];
|
||||
if(associatedDeletedStack !== undefined) {
|
||||
$scope.cardAndStackUndoDeleteAskForConfirmation(deletedCard, associatedDeletedStack);
|
||||
} else {
|
||||
$scope.cardUndoDelete(deletedCard);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.cardAndStackUndoDeleteAskForConfirmation = function(deletedCard, associatedDeletedStack) {
|
||||
OC.dialogs.confirm(
|
||||
t('deck', 'The associated stack is deleted as well, it will be restored as well.'),
|
||||
t('deck', 'Restore associated stack'),
|
||||
function(state) {
|
||||
if (state) {
|
||||
$scope.cardAndStackUndoDelete(deletedCard, associatedDeletedStack);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
$scope.cardAndStackUndoDelete = function(deletedCard, associatedDeletedStack) {
|
||||
$scope.stackUndoDelete(associatedDeletedStack).then(function() {
|
||||
$scope.cardUndoDelete(deletedCard);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.cardUndoDelete = function(deletedCard) {
|
||||
CardService.undoDelete(deletedCard).then(function() {
|
||||
StackService.addCard(deletedCard);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.cardArchive = function (card) {
|
||||
CardService.archive(card);
|
||||
StackService.removeCard(card);
|
||||
};
|
||||
|
||||
$scope.isCurrentUserAssigned = function (card) {
|
||||
if (! CardService.get(card.id).assignedUsers) {
|
||||
return false;
|
||||
@@ -209,6 +256,7 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
|
||||
});
|
||||
return userList.length === 1;
|
||||
};
|
||||
|
||||
$scope.cardAssignToMe = function (card) {
|
||||
CardService.assignUser(card, OC.getCurrentUser().uid)
|
||||
.then(
|
||||
@@ -217,6 +265,7 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
|
||||
// TODO: remove this jquery call. Fix and use appPopoverMenuUtils instead
|
||||
$('.popovermenu').addClass('hidden');
|
||||
};
|
||||
|
||||
$scope.cardUnassignFromMe = function (card) {
|
||||
CardService.unassignUser(card, OC.getCurrentUser().uid);
|
||||
StackService.updateCard(card);
|
||||
@@ -235,6 +284,7 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
|
||||
BoardService.getCurrent().labels.splice(i, 1);
|
||||
// TODO: remove from cards
|
||||
};
|
||||
|
||||
$scope.labelCreate = function (label) {
|
||||
label.boardId = $scope.id;
|
||||
LabelService.create(label).then(function (data) {
|
||||
@@ -254,12 +304,14 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
|
||||
BoardService.addAcl(sharee);
|
||||
$scope.status.addSharee = null;
|
||||
};
|
||||
|
||||
$scope.aclDelete = function (acl) {
|
||||
BoardService.deleteAcl(acl).then(function(data) {
|
||||
$scope.loadDefault();
|
||||
$scope.refreshData();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.aclUpdate = function (acl) {
|
||||
BoardService.updateAcl(acl);
|
||||
};
|
||||
@@ -383,5 +435,4 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
|
||||
}
|
||||
return card.attachmentCount;
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@@ -4,35 +4,48 @@
|
||||
* @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/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
import app from '../app/App.js';
|
||||
|
||||
/** global: oc_defaults */
|
||||
app.factory('ApiService', function ($http, $q) {
|
||||
var ApiService = function (http, endpoint) {
|
||||
// Consider renaming endpoint to resource
|
||||
this.endpoint = endpoint;
|
||||
this.baseUrl = OC.generateUrl('/apps/deck/' + endpoint);
|
||||
this.baseUrl = this.generateUrl(this.endpoint);
|
||||
this.http = http;
|
||||
this.q = $q;
|
||||
this.data = {};
|
||||
this.deleted = {};
|
||||
this.id = null;
|
||||
this.sorted = [];
|
||||
};
|
||||
|
||||
ApiService.prototype.generateUrl = function(path) {
|
||||
return OC.generateUrl('/apps/deck/' + path);
|
||||
};
|
||||
|
||||
ApiService.prototype.tryAllThenDeleted = function(id) {
|
||||
let object = this.data[id];
|
||||
if (object === undefined) {
|
||||
object = this.deleted[id];
|
||||
}
|
||||
return object;
|
||||
};
|
||||
|
||||
ApiService.prototype.fetchAll = function () {
|
||||
var deferred = $q.defer();
|
||||
var self = this;
|
||||
@@ -48,6 +61,30 @@ app.factory('ApiService', function ($http, $q) {
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
ApiService.prototype.fetchDeleted = function (scopeId) {
|
||||
var deferred = $q.defer();
|
||||
var self = this;
|
||||
$http.get(this.generateUrl(scopeId + '/' + this.endpoint + '/deleted')).then(function (response) {
|
||||
var objects = response.data;
|
||||
objects.forEach(function (obj) {
|
||||
if(self.deleted[obj.id] !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.deleted[obj.id] = obj;
|
||||
|
||||
if(self.afterFetch !== undefined) {
|
||||
self.afterFetch(obj);
|
||||
}
|
||||
});
|
||||
deferred.resolve(objects);
|
||||
}, function (error) {
|
||||
deferred.reject('Fetching ' + self.endpoint + ' failed');
|
||||
});
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
|
||||
ApiService.prototype.fetchOne = function (id) {
|
||||
|
||||
this.id = id;
|
||||
@@ -104,21 +141,41 @@ app.factory('ApiService', function ($http, $q) {
|
||||
var self = this;
|
||||
|
||||
$http.delete(this.baseUrl + '/' + id).then(function (response) {
|
||||
self.remove(id);
|
||||
self.deleted[id] = self.data[id];
|
||||
delete self.data[id];
|
||||
|
||||
let deletedAt = response.data.deletedAt;
|
||||
if (deletedAt !== undefined) {
|
||||
self.deleted[id].deletedAt = deletedAt;
|
||||
}
|
||||
|
||||
deferred.resolve(response.data);
|
||||
|
||||
}, function (error) {
|
||||
deferred.reject('Deleting ' + self.endpoint + ' failed');
|
||||
});
|
||||
return deferred.promise;
|
||||
|
||||
};
|
||||
|
||||
ApiService.prototype.undoDelete = function(entity) {
|
||||
var self = this;
|
||||
entity.deletedAt = 0;
|
||||
|
||||
var promise = this.update(entity);
|
||||
|
||||
promise.then(() => {
|
||||
self.data[entity.id] = entity;
|
||||
delete this.deleted[entity.id];
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
// methods for managing data
|
||||
ApiService.prototype.clear = function () {
|
||||
this.data = {};
|
||||
};
|
||||
|
||||
ApiService.prototype.add = function (entity) {
|
||||
var element = this.data[entity.id];
|
||||
if (element === undefined) {
|
||||
@@ -132,11 +189,7 @@ app.factory('ApiService', function ($http, $q) {
|
||||
element.status = {};
|
||||
}
|
||||
};
|
||||
ApiService.prototype.remove = function (id) {
|
||||
if (this.data[id] !== undefined) {
|
||||
delete this.data[id];
|
||||
}
|
||||
};
|
||||
|
||||
ApiService.prototype.addAll = function (entities) {
|
||||
var self = this;
|
||||
angular.forEach(entities, function (entity) {
|
||||
|
||||
@@ -4,20 +4,20 @@
|
||||
* @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/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
import app from '../app/App.js';
|
||||
|
||||
|
||||
@@ -4,20 +4,20 @@
|
||||
* @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/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
import app from '../app/App.js';
|
||||
|
||||
@@ -27,6 +27,11 @@ app.factory('StackService', function (ApiService, CardService, $http, $q) {
|
||||
ApiService.call(this, $http, ep, $q);
|
||||
};
|
||||
StackService.prototype = angular.copy(ApiService.prototype);
|
||||
|
||||
StackService.prototype.afterFetch = function(stack) {
|
||||
CardService.addAll(stack.cards);
|
||||
};
|
||||
|
||||
StackService.prototype.fetchAll = function (boardId) {
|
||||
var deferred = $q.defer();
|
||||
var self = this;
|
||||
@@ -129,27 +134,6 @@ app.factory('StackService', function (ApiService, CardService, $http, $q) {
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME: Should not show popup but proper undo mechanism
|
||||
StackService.prototype.delete = function (id) {
|
||||
var deferred = $q.defer();
|
||||
var self = this;
|
||||
|
||||
OC.dialogs.confirm(t('deck', 'Are you sure you want to delete the stack with all of its data?'), t('deck', 'Delete'), function(state) {
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
$http.delete(self.baseUrl + '/' + id).then(function (response) {
|
||||
self.remove(id);
|
||||
deferred.resolve(response.data);
|
||||
|
||||
}, function (error) {
|
||||
deferred.reject('Deleting ' + self.endpoint + ' failed');
|
||||
});
|
||||
});
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
var service = new StackService($http, 'stacks', $q);
|
||||
return service;
|
||||
});
|
||||
|
||||
|
||||
@@ -89,10 +89,11 @@ class CardController extends Controller {
|
||||
* @param $order
|
||||
* @param $description
|
||||
* @param $duedate
|
||||
* @param $deletedAt
|
||||
* @return \OCP\AppFramework\Db\Entity
|
||||
*/
|
||||
public function update($id, $title, $stackId, $type, $order, $description, $duedate) {
|
||||
return $this->cardService->update($id, $title, $stackId, $type, $order, $description, $this->userId, $duedate);
|
||||
public function update($id, $title, $stackId, $type, $order, $description, $duedate, $deletedAt) {
|
||||
return $this->cardService->update($id, $title, $stackId, $type, $order, $description, $this->userId, $duedate, $deletedAt);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,6 +105,15 @@ class CardController extends Controller {
|
||||
return $this->cardService->delete($cardId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* @param $boardId
|
||||
* @return \OCP\AppFramework\Db\Entity
|
||||
*/
|
||||
public function deleted($boardId) {
|
||||
return $this->cardService->fetchDeleted($boardId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* @param $cardId
|
||||
|
||||
@@ -74,10 +74,11 @@ class StackController extends Controller {
|
||||
* @param $title
|
||||
* @param $boardId
|
||||
* @param $order
|
||||
* @param $deletedAt
|
||||
* @return \OCP\AppFramework\Db\Entity
|
||||
*/
|
||||
public function update($id, $title, $boardId, $order) {
|
||||
return $this->stackService->update($id, $title, $boardId, $order);
|
||||
public function update($id, $title, $boardId, $order, $deletedAt) {
|
||||
return $this->stackService->update($id, $title, $boardId, $order, $deletedAt);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,4 +99,14 @@ class StackController extends Controller {
|
||||
public function delete($stackId) {
|
||||
return $this->stackService->delete($stackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* @param $boardId
|
||||
* @return \OCP\AppFramework\Db\Entity
|
||||
*/
|
||||
public function deleted($boardId) {
|
||||
return $this->stackService->fetchDeleted($boardId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
* @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;
|
||||
@@ -42,6 +42,7 @@ class Card extends RelationalEntity {
|
||||
protected $archived = false;
|
||||
protected $duedate;
|
||||
protected $notified = false;
|
||||
protected $deletedAt = 0;
|
||||
|
||||
private $databaseType = 'sqlite';
|
||||
|
||||
@@ -58,6 +59,7 @@ class Card extends RelationalEntity {
|
||||
$this->addType('createdAt', 'integer');
|
||||
$this->addType('archived', 'boolean');
|
||||
$this->addType('notified', 'boolean');
|
||||
$this->addType('deletedAt', 'integer');
|
||||
$this->addRelation('labels');
|
||||
$this->addRelation('assignedUsers');
|
||||
$this->addRelation('attachments');
|
||||
|
||||
@@ -120,10 +120,17 @@ class CardMapper extends DeckMapper implements IPermissionMapper {
|
||||
|
||||
public function findAll($stackId, $limit = null, $offset = null) {
|
||||
$sql = 'SELECT * FROM `*PREFIX*deck_cards`
|
||||
WHERE `stack_id` = ? AND NOT archived ORDER BY `order`';
|
||||
WHERE `stack_id` = ? AND NOT archived AND deleted_at = 0 ORDER BY `order`';
|
||||
return $this->findEntities($sql, [$stackId], $limit, $offset);
|
||||
}
|
||||
|
||||
public function findDeleted($boardId, $limit = null, $offset = null) {
|
||||
$sql = 'SELECT c.* FROM `*PREFIX*deck_cards` c
|
||||
INNER JOIN `*PREFIX*deck_stacks` s ON s.id = c.stack_id
|
||||
WHERE `s`.`board_id` = ? AND NOT c.archived AND NOT c.deleted_at = 0 ORDER BY `c`.`order`';
|
||||
return $this->findEntities($sql, [$boardId], $limit, $offset);
|
||||
}
|
||||
|
||||
public function findAllArchived($stackId, $limit = null, $offset = null) {
|
||||
$sql = 'SELECT * FROM `*PREFIX*deck_cards` WHERE `stack_id`=? AND archived ORDER BY `last_modified`';
|
||||
return $this->findEntities($sql, [$stackId], $limit, $offset);
|
||||
@@ -197,4 +204,4 @@ class CardMapper extends DeckMapper implements IPermissionMapper {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
* @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;
|
||||
|
||||
@@ -27,12 +27,14 @@ class Stack extends RelationalEntity {
|
||||
|
||||
protected $title;
|
||||
protected $boardId;
|
||||
protected $deletedAt = 0;
|
||||
protected $cards = array();
|
||||
protected $order;
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('id', 'integer');
|
||||
$this->addType('boardId', 'integer');
|
||||
$this->addType('deletedAt', 'integer');
|
||||
$this->addType('order', 'integer');
|
||||
}
|
||||
|
||||
@@ -47,4 +49,4 @@ class Stack extends RelationalEntity {
|
||||
}
|
||||
return $json;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,10 +51,17 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
|
||||
|
||||
|
||||
public function findAll($boardId, $limit = null, $offset = null) {
|
||||
$sql = 'SELECT * FROM `*PREFIX*deck_stacks` WHERE `board_id` = ? ORDER BY `order`';
|
||||
$sql = 'SELECT * FROM `*PREFIX*deck_stacks` WHERE `board_id` = ? AND deleted_at = 0 ORDER BY `order`';
|
||||
return $this->findEntities($sql, [$boardId], $limit, $offset);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function findDeleted($boardId, $limit = null, $offset = null) {
|
||||
$sql = 'SELECT * FROM `*PREFIX*deck_stacks` s
|
||||
WHERE `s`.`board_id` = ? AND NOT s.deleted_at = 0';
|
||||
return $this->findEntities($sql, [$boardId], $limit, $offset);
|
||||
}
|
||||
|
||||
|
||||
public function delete(Entity $entity) {
|
||||
// delete cards on stack
|
||||
@@ -73,4 +80,4 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
|
||||
$entity = $this->find($stackId);
|
||||
return $entity->getBoardId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,4 +256,4 @@ class BoardService {
|
||||
return $this->aclMapper->delete($acl);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ use OCA\Deck\Db\CardMapper;
|
||||
use OCA\Deck\Db\Acl;
|
||||
use OCA\Deck\Db\StackMapper;
|
||||
use OCA\Deck\Notification\NotificationHelper;
|
||||
use OCA\Deck\Db\BoardMapper;
|
||||
use OCA\Deck\Db\LabelMapper;
|
||||
use OCA\Deck\NotFoundException;
|
||||
use OCA\Deck\StatusException;
|
||||
|
||||
@@ -38,6 +40,8 @@ class CardService {
|
||||
|
||||
private $cardMapper;
|
||||
private $stackMapper;
|
||||
private $boardMapper;
|
||||
private $labelMapper;
|
||||
private $permissionService;
|
||||
private $boardService;
|
||||
private $notificationHelper;
|
||||
@@ -45,9 +49,22 @@ class CardService {
|
||||
private $attachmentService;
|
||||
private $currentUser;
|
||||
|
||||
public function __construct(CardMapper $cardMapper, StackMapper $stackMapper, PermissionService $permissionService, BoardService $boardService, NotificationHelper $notificationHelper, AssignedUsersMapper $assignedUsersMapper, AttachmentService $attachmentService, $userId) {
|
||||
public function __construct(
|
||||
CardMapper $cardMapper,
|
||||
StackMapper $stackMapper,
|
||||
BoardMapper $boardMapper,
|
||||
LabelMapper $labelMapper,
|
||||
PermissionService $permissionService,
|
||||
BoardService $boardService,
|
||||
NotificationHelper $notificationHelper,
|
||||
AssignedUsersMapper $assignedUsersMapper,
|
||||
AttachmentService $attachmentService,
|
||||
$userId
|
||||
) {
|
||||
$this->cardMapper = $cardMapper;
|
||||
$this->stackMapper = $stackMapper;
|
||||
$this->boardMapper = $boardMapper;
|
||||
$this->labelMapper = $labelMapper;
|
||||
$this->permissionService = $permissionService;
|
||||
$this->boardService = $boardService;
|
||||
$this->notificationHelper = $notificationHelper;
|
||||
@@ -56,6 +73,22 @@ class CardService {
|
||||
$this->currentUser = $userId;
|
||||
}
|
||||
|
||||
public function enrich($card) {
|
||||
$cardId = $card->getId();
|
||||
$card->setAssignedUsers($this->assignedUsersMapper->find($cardId));
|
||||
$card->setLabels($this->labelMapper->findAssignedLabelsForCard($cardId));
|
||||
$card->setAttachmentCount($this->attachmentService->count($cardId));
|
||||
}
|
||||
|
||||
public function fetchDeleted($boardId) {
|
||||
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
|
||||
$cards = $this->cardMapper->findDeleted($boardId);
|
||||
foreach ($cards as $card) {
|
||||
$this->enrich($card);
|
||||
}
|
||||
return $cards;
|
||||
}
|
||||
|
||||
public function find($cardId) {
|
||||
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
|
||||
$card = $this->cardMapper->find($cardId);
|
||||
@@ -89,10 +122,13 @@ class CardService {
|
||||
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));
|
||||
$card = $this->cardMapper->find($id);
|
||||
$card->setDeletedAt(time());
|
||||
$this->cardMapper->update($card);
|
||||
return $card;
|
||||
}
|
||||
|
||||
public function update($id, $title, $stackId, $type, $order, $description, $owner, $duedate) {
|
||||
public function update($id, $title, $stackId, $type, $order, $description, $owner, $duedate, $deletedAt) {
|
||||
$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.');
|
||||
@@ -108,6 +144,7 @@ class CardService {
|
||||
$card->setOwner($owner);
|
||||
$card->setDescription($description);
|
||||
$card->setDuedate($duedate);
|
||||
$card->setDeletedAt($deletedAt);
|
||||
return $this->cardMapper->update($card);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,26 +5,27 @@
|
||||
* @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\Service;
|
||||
|
||||
use OCA\Deck\Db\Acl;
|
||||
use OCA\Deck\Db\CardMapper;
|
||||
use OCA\Deck\Db\BoardMapper;
|
||||
use OCA\Deck\Db\LabelMapper;
|
||||
use OCA\Deck\Db\AssignedUsersMapper;
|
||||
use OCA\Deck\Db\Stack;
|
||||
@@ -38,46 +39,67 @@ class StackService {
|
||||
|
||||
private $stackMapper;
|
||||
private $cardMapper;
|
||||
private $boardMapper;
|
||||
private $labelMapper;
|
||||
private $permissionService;
|
||||
private $boardService;
|
||||
private $cardService;
|
||||
private $assignedUsersMapper;
|
||||
private $attachmentService;
|
||||
|
||||
public function __construct(
|
||||
StackMapper $stackMapper,
|
||||
BoardMapper $boardMapper,
|
||||
CardMapper $cardMapper,
|
||||
LabelMapper $labelMapper,
|
||||
PermissionService $permissionService,
|
||||
BoardService $boardService,
|
||||
CardService $cardService,
|
||||
AssignedUsersMapper $assignedUsersMapper,
|
||||
AttachmentService $attachmentService
|
||||
) {
|
||||
$this->stackMapper = $stackMapper;
|
||||
$this->boardMapper = $boardMapper;
|
||||
$this->cardMapper = $cardMapper;
|
||||
$this->labelMapper = $labelMapper;
|
||||
$this->permissionService = $permissionService;
|
||||
$this->boardService = $boardService;
|
||||
$this->cardService = $cardService;
|
||||
$this->assignedUsersMapper = $assignedUsersMapper;
|
||||
$this->attachmentService = $attachmentService;
|
||||
}
|
||||
|
||||
private function enrichStackWithCards($stack) {
|
||||
$cards = $this->cardMapper->findAll($stack->id);
|
||||
|
||||
if(is_null($cards)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($cards as $card) {
|
||||
$this->cardService->enrich($card);
|
||||
}
|
||||
|
||||
$stack->setCards($cards);
|
||||
}
|
||||
|
||||
private function enrichStacksWithCards($stacks) {
|
||||
foreach ($stacks as $stack) {
|
||||
$this->enrichStackWithCards($stack);
|
||||
}
|
||||
}
|
||||
|
||||
public function findAll($boardId) {
|
||||
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_READ);
|
||||
$stacks = $this->stackMapper->findAll($boardId);
|
||||
$labels = $this->labelMapper->getAssignedLabelsForBoard($boardId);
|
||||
foreach ($stacks as $stackIndex => $stack) {
|
||||
$cards = $this->cardMapper->findAll($stack->id);
|
||||
foreach ($cards as $cardIndex => $card) {
|
||||
$assignedUsers = $this->assignedUsersMapper->find($card->getId());
|
||||
$card->setAssignedUsers($assignedUsers);
|
||||
if (array_key_exists($card->id, $labels)) {
|
||||
$cards[$cardIndex]->setLabels($labels[$card->id]);
|
||||
}
|
||||
$card->setAttachmentCount($this->attachmentService->count($card->getId()));
|
||||
}
|
||||
$stacks[$stackIndex]->setCards($cards);
|
||||
}
|
||||
$this->enrichStacksWithCards($stacks);
|
||||
return $stacks;
|
||||
}
|
||||
|
||||
public function fetchDeleted($boardId) {
|
||||
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
|
||||
$stacks = $this->stackMapper->findDeleted($boardId);
|
||||
$this->enrichStacksWithCards($stacks);
|
||||
return $stacks;
|
||||
}
|
||||
|
||||
@@ -115,10 +137,18 @@ class StackService {
|
||||
|
||||
public function delete($id) {
|
||||
$this->permissionService->checkPermission($this->stackMapper, $id, Acl::PERMISSION_MANAGE);
|
||||
return $this->stackMapper->delete($this->stackMapper->find($id));
|
||||
|
||||
$stack = $this->stackMapper->find($id);
|
||||
$stack->setDeletedAt(time());
|
||||
$this->stackMapper->update($stack);
|
||||
|
||||
$this->enrichStackWithCards($stack);
|
||||
|
||||
return $stack;
|
||||
}
|
||||
|
||||
public function update($id, $title, $boardId, $order) {
|
||||
|
||||
public function update($id, $title, $boardId, $order, $deletedAt) {
|
||||
$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.');
|
||||
@@ -127,6 +157,7 @@ class StackService {
|
||||
$stack->setTitle($title);
|
||||
$stack->setBoardId($boardId);
|
||||
$stack->setOrder($order);
|
||||
$stack->setDeletedAt($deletedAt);
|
||||
return $this->stackMapper->update($stack);
|
||||
}
|
||||
|
||||
@@ -154,4 +185,4 @@ class StackService {
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
</form>
|
||||
<button class="icon-delete button-inline stack-actions"
|
||||
ng-if="!s.status.editStack"
|
||||
ng-click="stackservice.delete(s.id)"></button>
|
||||
ng-click="stackDelete(s)"></button>
|
||||
</h3>
|
||||
<ul data-as-sortable="sortOptions" is-disabled="!boardservice.canEdit() || filter==='archive'" data-ng-model="s.cards" class="card-list" ng-class="{emptyStack: !s.cards.length}">
|
||||
<li class="card as-sortable-item"
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<p>{{ statusservice.text }}</p></div>
|
||||
</div>
|
||||
<div id="sidebar-header">
|
||||
<a class="icon-close" ui-sref="board" ng-click="sidebar.show=!sidebar.show" title="<?php p($l->t('Close')); ?>"> <?php
|
||||
?><span class="hidden-visually"><?php p($l->t('Close')); ?></span><?php
|
||||
<a class="icon-close" ui-sref="board" ng-click="sidebar.show=!sidebar.show" title="<?php p($l->t('Close')); ?>"> <?php
|
||||
?><span class="hidden-visually"><?php p($l->t('Close')); ?></span><?php
|
||||
?></a>
|
||||
<h3>{{ boardservice.getCurrent().title }}</h3>
|
||||
</div>
|
||||
@@ -14,6 +14,8 @@
|
||||
<ul class="tabHeaders">
|
||||
<li class="tabHeader" ng-class="{'selected': (params.tab==0 || !params.tab)}" ui-sref="{tab: 0}"><a><?php p($l->t('Sharing')); ?></a></li>
|
||||
<li class="tabHeader" ng-class="{'selected': (params.tab==1)}" ui-sref="{tab: 1}"><a><?php p($l->t('Tags')); ?></a></li>
|
||||
<li class="tabHeader" ng-class="{'selected': (params.tab==2)}" ui-sref="{tab: 2}"><a><?php p($l->t('Deleted Stacks')); ?></a></li>
|
||||
<li class="tabHeader" ng-class="{'selected': (params.tab==3)}" ui-sref="{tab: 3}"><a><?php p($l->t('Deleted Cards')); ?></a></li>
|
||||
</ul>
|
||||
<div class="tabsContainer">
|
||||
<div id="tabBoardShare" class="tab" ng-if="params.tab==0 || !params.tab">
|
||||
@@ -118,4 +120,31 @@
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="board-detail-deleted-stacks" class="tab deletedStacksTabView" ng-if="params.tab==2">
|
||||
<ul class='board-detail__deleted-list'>
|
||||
<li class='board-detail__deleted-list__item' ng-repeat="deletedStack in stackservice.deleted">
|
||||
<span class="icon icon-deck"></span>
|
||||
<span>{{deletedStack.title}}</span>
|
||||
<span>{{deletedStack.deletedAt | relativeDateFilter }}</span>
|
||||
<a ng-click="stackUndoDelete(deletedStack)">
|
||||
<span class="icon icon-history"></span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="board-detail-deleted-cards" class="tab deletedCardsTabView" ng-if="params.tab==3">
|
||||
<ul class='board-detail__deleted-list'>
|
||||
<li class='board-detail__deleted-list__item' ng-repeat="deletedCard in cardservice.deleted">
|
||||
<span class="icon icon-deck"></span>
|
||||
<span>{{deletedCard.title}}</span>
|
||||
<span>{{stackservice.tryAllThenDeleted(deletedCard.stackId).title}}</span>
|
||||
<span>{{deletedCard.deletedAt | relativeDateFilter }}</span>
|
||||
<a ng-click="cardOrCardAndStackUndoDelete(deletedCard)">
|
||||
<span class="icon icon-history"></span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
* @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;
|
||||
@@ -175,4 +175,4 @@ class BoardMapperTest extends MapperTestUtility {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
* @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;
|
||||
@@ -81,6 +81,7 @@ class CardTest extends TestCase {
|
||||
'attachments' => null,
|
||||
'attachmentCount' => null,
|
||||
'assignedUsers' => null,
|
||||
'deletedAt' => 0
|
||||
], $card->jsonSerialize());
|
||||
}
|
||||
public function testJsonSerializeLabels() {
|
||||
@@ -103,6 +104,7 @@ class CardTest extends TestCase {
|
||||
'attachments' => null,
|
||||
'attachmentCount' => null,
|
||||
'assignedUsers' => null,
|
||||
'deletedAt' => 0
|
||||
], $card->jsonSerialize());
|
||||
}
|
||||
|
||||
@@ -135,7 +137,8 @@ class CardTest extends TestCase {
|
||||
'attachments' => null,
|
||||
'attachmentCount' => null,
|
||||
'assignedUsers' => ['user1'],
|
||||
'deletedAt' => 0
|
||||
], $card->jsonSerialize());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
* @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;
|
||||
@@ -39,6 +39,7 @@ class StackTest extends \Test\TestCase {
|
||||
'title' => "My Stack",
|
||||
'order' => 1,
|
||||
'boardId' => 1,
|
||||
'deletedAt' => 0
|
||||
], $board->jsonSerialize());
|
||||
}
|
||||
public function testJsonSerializeWithCards() {
|
||||
@@ -51,6 +52,7 @@ class StackTest extends \Test\TestCase {
|
||||
'order' => 1,
|
||||
'boardId' => 1,
|
||||
'cards' => array("foo", "bar"),
|
||||
'deletedAt' => 0
|
||||
], $board->jsonSerialize());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ use OCA\Deck\Db\AssignedUsersMapper;
|
||||
use OCA\Deck\Db\Card;
|
||||
use OCA\Deck\Db\CardMapper;
|
||||
use OCA\Deck\Db\StackMapper;
|
||||
use OCA\Deck\Db\BoardMapper;
|
||||
use OCA\Deck\Db\LabelMapper;
|
||||
use OCA\Deck\NotFoundException;
|
||||
use OCA\Deck\Notification\NotificationHelper;
|
||||
use OCA\Deck\StatusException;
|
||||
@@ -48,19 +50,36 @@ class CardServiceTest extends TestCase {
|
||||
private $notificationHelper;
|
||||
/** @var AssignedUsersMapper|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $assignedUsersMapper;
|
||||
/** @var BoardService|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $boardService;
|
||||
/** @var BoardService|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $boardService;
|
||||
/** @var LabelMapper|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $labelMapper;
|
||||
private $boardMapper;
|
||||
private $attachmentService;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->cardMapper = $this->createMock(CardMapper::class);
|
||||
$this->stackMapper = $this->createMock(StackMapper::class);
|
||||
$this->boardMapper = $this->createMock(BoardMapper::class);
|
||||
$this->labelMapper = $this->createMock(LabelMapper::class);
|
||||
$this->permissionService = $this->createMock(PermissionService::class);
|
||||
$this->boardService = $this->createMock(BoardService::class);
|
||||
$this->notificationHelper = $this->createMock(NotificationHelper::class);
|
||||
$this->assignedUsersMapper = $this->createMock(AssignedUsersMapper::class);
|
||||
$this->attachmentService = $this->createMock(AttachmentService::class);
|
||||
$this->cardService = new CardService($this->cardMapper, $this->stackMapper, $this->permissionService, $this->boardService, $this->notificationHelper, $this->assignedUsersMapper, $this->attachmentService, 'userXY');
|
||||
$this->cardService = new CardService(
|
||||
$this->cardMapper,
|
||||
$this->stackMapper,
|
||||
$this->boardMapper,
|
||||
$this->labelMapper,
|
||||
$this->permissionService,
|
||||
$this->boardService,
|
||||
$this->notificationHelper,
|
||||
$this->assignedUsersMapper,
|
||||
$this->attachmentService,
|
||||
'user1'
|
||||
);
|
||||
}
|
||||
|
||||
public function testFind() {
|
||||
@@ -100,13 +119,15 @@ class CardServiceTest extends TestCase {
|
||||
}
|
||||
|
||||
public function testDelete() {
|
||||
$cardToBeDeleted = new Card();
|
||||
$this->cardMapper->expects($this->once())
|
||||
->method('find')
|
||||
->willReturn(new Card());
|
||||
->willReturn($cardToBeDeleted);
|
||||
$this->cardMapper->expects($this->once())
|
||||
->method('delete')
|
||||
->willReturn(1);
|
||||
$this->assertEquals(1, $this->cardService->delete(123));
|
||||
->method('update')
|
||||
->willReturn($cardToBeDeleted);
|
||||
$this->cardService->delete(123);
|
||||
$this->assertTrue($cardToBeDeleted->getDeletedAt() <= time(), 'deletedAt is in the past');
|
||||
}
|
||||
|
||||
public function testUpdate() {
|
||||
@@ -115,7 +136,7 @@ class CardServiceTest extends TestCase {
|
||||
$card->setArchived(false);
|
||||
$this->cardMapper->expects($this->once())->method('find')->willReturn($card);
|
||||
$this->cardMapper->expects($this->once())->method('update')->willReturnCallback(function($c) { return $c; });
|
||||
$actual = $this->cardService->update(123, 'newtitle', 234, 'text', 999, 'foo', 'admin', '2017-01-01 00:00:00');
|
||||
$actual = $this->cardService->update(123, 'newtitle', 234, 'text', 999, 'foo', 'admin', '2017-01-01 00:00:00', null);
|
||||
$this->assertEquals('newtitle', $actual->getTitle());
|
||||
$this->assertEquals(234, $actual->getStackId());
|
||||
$this->assertEquals('text', $actual->getType());
|
||||
@@ -131,7 +152,7 @@ class CardServiceTest extends TestCase {
|
||||
$this->cardMapper->expects($this->once())->method('find')->willReturn($card);
|
||||
$this->cardMapper->expects($this->never())->method('update');
|
||||
$this->setExpectedException(StatusException::class);
|
||||
$this->cardService->update(123, 'newtitle', 234, 'text', 999, 'foo', 'admin', '2017-01-01 00:00:00');
|
||||
$this->cardService->update(123, 'newtitle', 234, 'text', 999, 'foo', 'admin', '2017-01-01 00:00:00', null);
|
||||
}
|
||||
|
||||
public function testRename() {
|
||||
@@ -317,4 +338,4 @@ class CardServiceTest extends TestCase {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace OCA\Deck\Service;
|
||||
use OCA\Deck\Db\AssignedUsersMapper;
|
||||
use OCA\Deck\Db\Card;
|
||||
use OCA\Deck\Db\CardMapper;
|
||||
use OCA\Deck\Db\BoardMapper;
|
||||
use OCA\Deck\Db\Label;
|
||||
use OCA\Deck\Db\LabelMapper;
|
||||
use OCA\Deck\Db\Stack;
|
||||
@@ -48,6 +49,8 @@ class StackServiceTest extends TestCase {
|
||||
private $stackMapper;
|
||||
/** @var \PHPUnit\Framework\MockObject\MockObject|CardMapper */
|
||||
private $cardMapper;
|
||||
/** @var \PHPUnit\Framework\MockObject\MockObject|BoardMapper */
|
||||
private $boardMapper;
|
||||
/** @var \PHPUnit\Framework\MockObject\MockObject|LabelMapper */
|
||||
private $labelMapper;
|
||||
/** @var \PHPUnit\Framework\MockObject\MockObject|PermissionService */
|
||||
@@ -58,23 +61,30 @@ class StackServiceTest extends TestCase {
|
||||
private $attachmentService;
|
||||
/** @var BoardService|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $boardService;
|
||||
/** @var CardService|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $cardService;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->stackMapper = $this->createMock(StackMapper::class);
|
||||
$this->cardMapper = $this->createMock(CardMapper::class);
|
||||
$this->labelMapper = $this->createMock(LabelMapper::class);
|
||||
$this->boardMapper = $this->createMock(BoardMapper::class);
|
||||
$this->permissionService = $this->createMock(PermissionService::class);
|
||||
$this->boardService = $this->createMock(BoardService::class);
|
||||
$this->cardService = $this->createMock(CardService::class);
|
||||
$this->assignedUsersMapper = $this->createMock(AssignedUsersMapper::class);
|
||||
$this->attachmentService = $this->createMock(AttachmentService::class);
|
||||
$this->labelMapper = $this->createMock(LabelMapper::class);
|
||||
|
||||
$this->stackService = new StackService(
|
||||
$this->stackMapper,
|
||||
$this->cardMapper,
|
||||
$this->labelMapper,
|
||||
$this->boardMapper,
|
||||
$this->cardMapper,
|
||||
$this->labelMapper,
|
||||
|
||||
$this->permissionService,
|
||||
$this->boardService,
|
||||
$this->cardService,
|
||||
$this->assignedUsersMapper,
|
||||
$this->attachmentService
|
||||
);
|
||||
@@ -83,9 +93,16 @@ class StackServiceTest extends TestCase {
|
||||
public function testFindAll() {
|
||||
$this->permissionService->expects($this->once())->method('checkPermission');
|
||||
$this->stackMapper->expects($this->once())->method('findAll')->willReturn($this->getStacks());
|
||||
$this->labelMapper->expects($this->once())->method('getAssignedLabelsForBoard')->willReturn($this->getLabels());
|
||||
$this->cardService->expects($this->atLeastOnce())->method('enrich')->will(
|
||||
$this->returnCallback(
|
||||
function($card) {
|
||||
$card->setLabels($this->getLabels()[$card->getId()]);
|
||||
}
|
||||
)
|
||||
);
|
||||
$this->cardMapper->expects($this->any())->method('findAll')->willReturn($this->getCards(222));
|
||||
|
||||
|
||||
$actual = $this->stackService->findAll(123);
|
||||
for($stackId=0; $stackId<3; $stackId++) {
|
||||
for ($cardId=0;$cardId<10;$cardId++) {
|
||||
@@ -130,8 +147,10 @@ class StackServiceTest extends TestCase {
|
||||
private function getStacks() {
|
||||
$s1 = new Stack();
|
||||
$s1->setId(222);
|
||||
$s1->setBoardId(1);
|
||||
$s2 = new Stack();
|
||||
$s2->setId(223);
|
||||
$s1->setBoardId(1);
|
||||
return [$s1, $s2];
|
||||
}
|
||||
private function getCards($stackId=0) {
|
||||
@@ -158,9 +177,12 @@ class StackServiceTest extends TestCase {
|
||||
|
||||
public function testDelete() {
|
||||
$this->permissionService->expects($this->once())->method('checkPermission');
|
||||
$this->stackMapper->expects($this->once())->method('find')->willReturn(new Stack());
|
||||
$this->stackMapper->expects($this->once())->method('delete');
|
||||
$stackToBeDeleted = new Stack();
|
||||
$stackToBeDeleted->setId(1);
|
||||
$this->stackMapper->expects($this->once())->method('find')->willReturn($stackToBeDeleted);
|
||||
$this->stackMapper->expects($this->once())->method('update');
|
||||
$this->stackService->delete(123);
|
||||
$this->assertTrue($stackToBeDeleted->getDeletedAt() <= time(), "deletedAt is in the past");
|
||||
}
|
||||
|
||||
public function testUpdate() {
|
||||
@@ -172,7 +194,7 @@ class StackServiceTest extends TestCase {
|
||||
$stack->setTitle('Foo');
|
||||
$stack->setBoardId(2);
|
||||
$stack->setOrder(1);
|
||||
$result = $this->stackService->update(123, 'Foo', 2, 1);
|
||||
$result = $this->stackService->update(123, 'Foo', 2, 1, null);
|
||||
$this->assertEquals($stack, $result);
|
||||
}
|
||||
|
||||
@@ -207,4 +229,4 @@ class StackServiceTest extends TestCase {
|
||||
return $stack;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ class CardControllerTest extends \Test\TestCase {
|
||||
->method('update')
|
||||
->with(1, 'title', 3, 'text', 5, 'foo', $this->userId, '2017-01-01 00:00:00')
|
||||
->willReturn(1);
|
||||
$this->assertEquals(1, $this->controller->update(1, 'title', 3, 'text', 5, 'foo', '2017-01-01 00:00:00'));
|
||||
$this->assertEquals(1, $this->controller->update(1, 'title', 3, 'text', 5, 'foo', '2017-01-01 00:00:00', null));
|
||||
}
|
||||
|
||||
public function testDelete() {
|
||||
|
||||
@@ -81,7 +81,7 @@ class StackControllerTest extends \Test\TestCase {
|
||||
->method('update')
|
||||
->with(1, 2, 3, 4)
|
||||
->willReturn(1);
|
||||
$this->assertEquals(1, $this->controller->update(1, 2, 3, 4));
|
||||
$this->assertEquals(1, $this->controller->update(1, 2, 3, 4, null));
|
||||
}
|
||||
|
||||
public function testReorder() {
|
||||
|
||||
Reference in New Issue
Block a user