зеркало из https://github.com/nextcloud/deck.git
stack, card undo delete: refactoring
Signed-off-by: Manuel Arno Korfmann <manu@korfmann.info> stack undo delete: serve cards with deleted and delete actions Signed-off-by: Manuel Arno Korfmann <manu@korfmann.info> stack, cards undo delete: codacy Signed-off-by: Manuel Arno Korfmann <manu@korfmann.info> card undo delete: 526#discussion_r204501758, refactoring Signed-off-by: Manuel Arno Korfmann <manu@korfmann.info> card, stack undo delete: code review fixes #1 Signed-off-by: Manuel Arno Korfmann <manu@korfmann.info> undo card, stack delete: show deleted stacks name in deleted card listing Signed-off-by: Manuel Arno Korfmann <manu@korfmann.info>
This commit is contained in:
Родитель
95548fba54
Коммит
41d30d4fd4
|
@ -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,7 +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' => '/stacks/deleted/{boardId}', 'verb' => 'GET'],
|
||||
['name' => 'stack#deleted', 'url' => '/{boardId}/stacks/deleted', 'verb' => 'GET'],
|
||||
['name' => 'stack#archived', 'url' => '/stacks/{boardId}/archived', 'verb' => 'GET'],
|
||||
|
||||
// cards
|
||||
|
@ -51,7 +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' => '/cards/deleted/{boardId}', 'verb' => 'GET'],
|
||||
['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 {
|
||||
|
|
|
@ -42,15 +42,12 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
|
|||
$scope.board = BoardService.getCurrent();
|
||||
$scope.uploader = FileService.uploader;
|
||||
|
||||
$scope.deletedCards = {};
|
||||
$scope.deletedStacks = {};
|
||||
|
||||
$scope.$watch(function() {
|
||||
return $state.current;
|
||||
}, function(currentState) {
|
||||
if(currentState.name === 'board.detail') {
|
||||
$scope.loadDeletedEntity(CardService, 'deletedCards');
|
||||
$scope.loadDeletedEntity(StackService, 'deletedStacks');
|
||||
CardService.fetchDeleted($scope.id);
|
||||
StackService.fetchDeleted($scope.id);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -148,16 +145,6 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
|
|||
});
|
||||
};
|
||||
|
||||
$scope.loadDeletedEntity = function(service, scopeKey) {
|
||||
service.fetchDeleted($scope.id).then(function (data) {
|
||||
for(i=0;i<data.length;i++) {
|
||||
$scope[scopeKey][data[i].id] = data[i];
|
||||
}
|
||||
}, function (error) {
|
||||
$scope.statusservice.setError('Error occured', error);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.loadDefault = function () {
|
||||
StackService.fetchAll($scope.id).then(function (data) {
|
||||
$scope.statusservice.releaseWaiting();
|
||||
|
@ -209,47 +196,52 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
|
|||
};
|
||||
|
||||
$scope.stackDelete = function (stack) {
|
||||
$scope.stackservice.delete(stack.id).then(function() {
|
||||
$scope.deletedStacks[stack.id] = stack;
|
||||
});
|
||||
}
|
||||
$scope.stackservice.delete(stack.id);
|
||||
};
|
||||
|
||||
$scope.stackUndoDelete = function (deletedStack) {
|
||||
return StackService.undoDelete(deletedStack).then(function() {
|
||||
delete $scope.deletedStacks[deletedStack.id];
|
||||
});
|
||||
}
|
||||
return StackService.undoDelete(deletedStack);
|
||||
};
|
||||
|
||||
$scope.cardDelete = function (card) {
|
||||
CardService.delete(card.id).then(function () {
|
||||
StackService.removeCard(card);
|
||||
$scope.deletedCards[card.id] = card;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.cardUndoDelete = function (deletedCard) {
|
||||
CardService.undoDelete(deletedCard).then(function() {
|
||||
delete $scope.deletedCards[deletedCard.id];
|
||||
|
||||
var associatedDeletedStack = $scope.deletedStacks[deletedCard.stackId];
|
||||
if(associatedDeletedStack !== undefined) {
|
||||
OC.dialogs.confirm(
|
||||
t('deck', 'The associated stack is deleted as well, do you want to restore it as well?'),
|
||||
t('deck', 'Yes'),
|
||||
function(state) {
|
||||
|
||||
if (state) {
|
||||
$scope.stackUndoDelete(associatedDeletedStack).then(function() {
|
||||
StackService.addCard(deletedCard);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
StackService.addCard(deletedCard);
|
||||
}
|
||||
});
|
||||
var associatedDeletedStack = $scope.stackservice.deleted[deletedCard.stackId];
|
||||
if(associatedDeletedStack !== undefined) {
|
||||
$scope.cardAndStackUndoDelete(deletedCard, associatedDeletedStack);
|
||||
} else {
|
||||
$scope._cardUndoDelete(deletedCard);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.cardAndStackUndoDelete = 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);
|
||||
|
|
|
@ -24,15 +24,27 @@ 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;
|
||||
|
@ -51,11 +63,18 @@ app.factory('ApiService', function ($http, $q) {
|
|||
ApiService.prototype.fetchDeleted = function (scopeId) {
|
||||
var deferred = $q.defer();
|
||||
var self = this;
|
||||
$http.get(this.baseUrl + '/deleted/' + scopeId).then(function (response) {
|
||||
var objects = response.data;
|
||||
deferred.resolve(objects);
|
||||
$http.get(this.generateUrl(scopeId + '/' + this.endpoint + '/deleted')).then(function (response) {
|
||||
var objects = response.data;
|
||||
objects.forEach(function (obj) {
|
||||
self.deleted[obj.id] = obj;
|
||||
|
||||
if(self.afterFetch !== undefined) {
|
||||
self.afterFetch(obj);
|
||||
}
|
||||
});
|
||||
deferred.resolve(objects);
|
||||
}, function (error) {
|
||||
deferred.reject('Fetching ' + self.endpoint + ' failed');
|
||||
deferred.reject('Fetching ' + self.endpoint + ' failed');
|
||||
});
|
||||
return deferred.promise;
|
||||
};
|
||||
|
@ -117,6 +136,7 @@ app.factory('ApiService', function ($http, $q) {
|
|||
var self = this;
|
||||
|
||||
$http.delete(this.baseUrl + '/' + id).then(function (response) {
|
||||
self.deleted[id] = self.data[id];
|
||||
self.remove(id);
|
||||
deferred.resolve(response.data);
|
||||
|
||||
|
@ -134,6 +154,7 @@ app.factory('ApiService', function ($http, $q) {
|
|||
|
||||
promise.then(function() {
|
||||
self.data[entity.id] = entity;
|
||||
self.remove(entity.id, 'deleted');
|
||||
});
|
||||
|
||||
return promise;
|
||||
|
@ -156,9 +177,9 @@ app.factory('ApiService', function ($http, $q) {
|
|||
element.status = {};
|
||||
}
|
||||
};
|
||||
ApiService.prototype.remove = function (id) {
|
||||
if (this.data[id] !== undefined) {
|
||||
delete this.data[id];
|
||||
ApiService.prototype.remove = function (id, collection = 'data') {
|
||||
if (this[collection][id] !== undefined) {
|
||||
delete this[collection][id];
|
||||
}
|
||||
};
|
||||
ApiService.prototype.addAll = function (entities) {
|
||||
|
|
|
@ -28,6 +28,10 @@ app.factory('StackService', function (ApiService, CardService, $http, $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;
|
||||
|
|
|
@ -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,7 +42,7 @@ class Card extends RelationalEntity {
|
|||
protected $archived = false;
|
||||
protected $duedate;
|
||||
protected $notified = false;
|
||||
protected $deletedAt;
|
||||
protected $deletedAt = 0;
|
||||
|
||||
private $databaseType = 'sqlite';
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ class Stack extends RelationalEntity {
|
|||
|
||||
protected $title;
|
||||
protected $boardId;
|
||||
protected $deletedAt;
|
||||
protected $deletedAt = 0;
|
||||
protected $cards = array();
|
||||
protected $order;
|
||||
|
||||
|
|
|
@ -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\Service;
|
||||
|
@ -66,28 +66,38 @@ class StackService {
|
|||
$this->attachmentService = $attachmentService;
|
||||
}
|
||||
|
||||
private function enrichStackWithCards($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()));
|
||||
}
|
||||
$stack->setCards($cards);
|
||||
}
|
||||
|
||||
private function enrichStacksWithCards($stacks) {
|
||||
foreach ($stacks as $stackIndex => $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);
|
||||
return $this->stackMapper->findDeleted($boardId);
|
||||
$stacks = $this->stackMapper->findDeleted($boardId);
|
||||
$this->enrichStacksWithCards($stacks);
|
||||
return $stacks;
|
||||
}
|
||||
|
||||
public function findAllArchived($boardId) {
|
||||
|
@ -129,6 +139,8 @@ class StackService {
|
|||
$stack->setDeletedAt(time());
|
||||
$this->stackMapper->update($stack);
|
||||
|
||||
$this->enrichStackWithCards($stack);
|
||||
|
||||
return $stack;
|
||||
}
|
||||
|
||||
|
|
|
@ -122,28 +122,28 @@
|
|||
</div>
|
||||
|
||||
<div id="board-detail-deleted-stacks" class="tab deletedStacksTabView" ng-if="params.tab==2">
|
||||
<ul>
|
||||
<li ng-repeat="deletedStack in deletedStacks">
|
||||
<dl>
|
||||
<dt>Title</dt>
|
||||
<dd>{{deletedStack.title}}<dd>
|
||||
</dl>
|
||||
<a ng-click="stackUndoDelete(deletedStack)"><span class="icon icon-undo"></span><br /><span><?php p($l->t('Undo delete')); ?></span></a>
|
||||
<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}}</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>
|
||||
<li ng-repeat="deletedCard in deletedCards">
|
||||
<dl>
|
||||
<dt>Title</dt>
|
||||
<dd>{{deletedCard.title}}<dd>
|
||||
<dt>Stack</dt>
|
||||
<dd>{{stackservice.data[deletedCard.stackId].title}}</dd>
|
||||
</dl>
|
||||
|
||||
<a ng-click="cardUndoDelete(deletedCard)"><span class="icon icon-undo"></span><br /><span><?php p($l->t('Undo delete')); ?></span></a>
|
||||
<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}}</span>
|
||||
<a ng-click="cardUndoDelete(deletedCard)">
|
||||
<span class="icon icon-history"></span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
Загрузка…
Ссылка в новой задаче