From ef4ce31c47a5ef70d1a4d00f2d4cd182ac067f2c Mon Sep 17 00:00:00 2001
From: Manuel Arno Korfmann
Date: Mon, 16 Jul 2018 00:09:43 +0200
Subject: [PATCH] refactoring and stack undo delete early wip
Signed-off-by: Manuel Arno Korfmann
stack soft delete done
Signed-off-by: Manuel Arno Korfmann
stack undo delete done
Signed-off-by: Manuel Arno Korfmann
stack undo: code review remarks and fixes
Signed-off-by: Manuel Arno Korfmann
---
appinfo/database.xml | 8 ++++
appinfo/routes.php | 1 +
css/style.scss | 8 +++-
js/controller/BoardController.js | 69 ++++++++++++++++------------
js/service/ApiService.js | 40 ++++++++++------
js/service/CardService.js | 28 ++---------
js/service/StackService.js | 30 ++----------
lib/Controller/StackController.php | 15 +++++-
lib/Db/Stack.php | 4 +-
lib/Db/StackMapper.php | 13 ++++--
lib/Service/StackService.php | 20 +++++++-
templates/part.board.mainView.php | 2 +-
templates/part.board.sidebarView.php | 44 ++++++++++++------
13 files changed, 164 insertions(+), 118 deletions(-)
diff --git a/appinfo/database.xml b/appinfo/database.xml
index e9b7586ab..91c9076ba 100644
--- a/appinfo/database.xml
+++ b/appinfo/database.xml
@@ -77,6 +77,14 @@
8false
+
+ deleted_at
+ integer
+ 0
+ 8
+ false
+ true
+ deck_stacks_board_id_index
diff --git a/appinfo/routes.php b/appinfo/routes.php
index 47a550f5b..de62cb0cc 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -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' => '/stacks/deleted/{boardId}', 'verb' => 'GET'],
['name' => 'stack#archived', 'url' => '/stacks/{boardId}/archived', 'verb' => 'GET'],
// cards
diff --git a/css/style.scss b/css/style.scss
index a94b60938..efd70ef3e 100644
--- a/css/style.scss
+++ b/css/style.scss
@@ -1215,12 +1215,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 {
diff --git a/js/controller/BoardController.js b/js/controller/BoardController.js
index 9b5e2bc59..048190d47 100644
--- a/js/controller/BoardController.js
+++ b/js/controller/BoardController.js
@@ -4,20 +4,20 @@
* @author Julius Härtl
*
* @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 .
- *
+ *
*/
import app from '../app/App.js';
@@ -42,7 +42,17 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
$scope.board = BoardService.getCurrent();
$scope.uploader = FileService.uploader;
- $scope.deletedCards = [];
+ $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');
+ }
+ });
// workaround for $stateParams changes not being propagated
$scope.$watch(function() {
@@ -138,9 +148,11 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
});
};
- $scope.loadDeletedCards = function() {
- CardService.fetchDeleted($scope.id).then(function (data) {
- $scope.deletedCards = data;
+ $scope.loadDeletedEntity = function(service, scopeKey) {
+ service.fetchDeleted($scope.id).then(function (data) {
+ for(i=0;i
*
* @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 .
- *
+ *
*/
import app from '../app/App.js';
@@ -48,6 +48,19 @@ app.factory('ApiService', function ($http, $q) {
return deferred.promise;
};
+ 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);
+ }, function (error) {
+ deferred.reject('Fetching ' + self.endpoint + ' failed');
+ });
+ return deferred.promise;
+ };
+
+
ApiService.prototype.fetchOne = function (id) {
this.id = id;
@@ -111,20 +124,19 @@ app.factory('ApiService', function ($http, $q) {
deferred.reject('Deleting ' + self.endpoint + ' failed');
});
return deferred.promise;
-
};
- ApiService.prototype.softDelete = function (id) {
- var deferred = $q.defer();
+ ApiService.prototype.undoDelete = function(entity) {
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');
+ entity.deletedAt = 0;
+
+ var promise = this.update(entity);
+
+ promise.then(function() {
+ self.data[entity.id] = entity;
});
- return deferred.promise;
+
+ return promise;
};
// methods for managing data
diff --git a/js/service/CardService.js b/js/service/CardService.js
index 840d21156..eb62785f0 100644
--- a/js/service/CardService.js
+++ b/js/service/CardService.js
@@ -4,20 +4,20 @@
* @author Julius Härtl
*
* @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 .
- *
+ *
*/
import app from '../app/App.js';
@@ -27,13 +27,6 @@ app.factory('CardService', function (ApiService, $http, $q) {
};
CardService.prototype = angular.copy(ApiService.prototype);
- CardService.prototype.delete = CardService.prototype.softDelete;
-
- CardService.prototype.undoDelete = function(card) {
- card.deletedAt = 0;
- this.update(card);
- };
-
CardService.prototype.reorder = function (card, order) {
var deferred = $q.defer();
var self = this;
@@ -179,19 +172,6 @@ app.factory('CardService', function (ApiService, $http, $q) {
return deferred.promise;
};
- CardService.prototype.fetchDeleted = function (boardId) {
- var deferred = $q.defer();
- var self = this;
- $http.get(this.baseUrl + '/deleted/' + boardId).then(function (response) {
- var objects = response.data;
- deferred.resolve(objects);
- }, function (error) {
- deferred.reject('Fetching ' + self.endpoint + ' failed');
- });
- return deferred.promise;
- };
-
-
var service = new CardService($http, 'cards', $q);
return service;
});
diff --git a/js/service/StackService.js b/js/service/StackService.js
index a4671a331..a5ee1d0a9 100644
--- a/js/service/StackService.js
+++ b/js/service/StackService.js
@@ -4,20 +4,20 @@
* @author Julius Härtl
*
* @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 .
- *
+ *
*/
import app from '../app/App.js';
@@ -27,6 +27,7 @@ app.factory('StackService', function (ApiService, CardService, $http, $q) {
ApiService.call(this, $http, ep, $q);
};
StackService.prototype = angular.copy(ApiService.prototype);
+
StackService.prototype.fetchAll = function (boardId) {
var deferred = $q.defer();
var self = this;
@@ -129,27 +130,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;
});
-
diff --git a/lib/Controller/StackController.php b/lib/Controller/StackController.php
index 3e2e358ed..3063d858f 100644
--- a/lib/Controller/StackController.php
+++ b/lib/Controller/StackController.php
@@ -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);
+ }
+
}
diff --git a/lib/Db/Stack.php b/lib/Db/Stack.php
index 8a3d00ffb..b90604126 100644
--- a/lib/Db/Stack.php
+++ b/lib/Db/Stack.php
@@ -27,12 +27,14 @@ class Stack extends RelationalEntity {
protected $title;
protected $boardId;
+ protected $deletedAt;
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;
}
-}
\ No newline at end of file
+}
diff --git a/lib/Db/StackMapper.php b/lib/Db/StackMapper.php
index 51d8bf29c..c98d87cc0 100644
--- a/lib/Db/StackMapper.php
+++ b/lib/Db/StackMapper.php
@@ -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();
}
-}
\ No newline at end of file
+}
diff --git a/lib/Service/StackService.php b/lib/Service/StackService.php
index 581543623..a4ca46eec 100644
--- a/lib/Service/StackService.php
+++ b/lib/Service/StackService.php
@@ -25,6 +25,7 @@ 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,6 +39,7 @@ class StackService {
private $stackMapper;
private $cardMapper;
+ private $boardMapper;
private $labelMapper;
private $permissionService;
private $boardService;
@@ -46,6 +48,7 @@ class StackService {
public function __construct(
StackMapper $stackMapper,
+ BoardMapper $boardMapper,
CardMapper $cardMapper,
LabelMapper $labelMapper,
PermissionService $permissionService,
@@ -54,6 +57,7 @@ class StackService {
AttachmentService $attachmentService
) {
$this->stackMapper = $stackMapper;
+ $this->boardMapper = $boardMapper;
$this->cardMapper = $cardMapper;
$this->labelMapper = $labelMapper;
$this->permissionService = $permissionService;
@@ -81,6 +85,11 @@ class StackService {
return $stacks;
}
+ public function fetchDeleted($boardId) {
+ $this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
+ return $this->stackMapper->findDeleted($boardId);
+ }
+
public function findAllArchived($boardId) {
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_READ);
$stacks = $this->stackMapper->findAll($boardId);
@@ -115,10 +124,16 @@ 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);
+
+ 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 +142,7 @@ class StackService {
$stack->setTitle($title);
$stack->setBoardId($boardId);
$stack->setOrder($order);
+ $stack->setDeletedAt($deletedAt);
return $this->stackMapper->update($stack);
}
diff --git a/templates/part.board.mainView.php b/templates/part.board.mainView.php
index bc5f483c2..26447c9c2 100644
--- a/templates/part.board.mainView.php
+++ b/templates/part.board.mainView.php
@@ -52,7 +52,7 @@
+ ng-click="stackDelete(s)">