diff --git a/appinfo/database.xml b/appinfo/database.xml
index 3eb4495f9..e9b7586ab 100644
--- a/appinfo/database.xml
+++ b/appinfo/database.xml
@@ -167,6 +167,14 @@
boolean
false
+
+ deleted_at
+ integer
+ 0
+ 8
+ false
+ true
+
deck_cards_stack_id_index
diff --git a/appinfo/routes.php b/appinfo/routes.php
index cdd2b9d79..47a550f5b 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -50,6 +50,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#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'],
diff --git a/js/controller/BoardController.js b/js/controller/BoardController.js
index d8b4c16b7..bce35642f 100644
--- a/js/controller/BoardController.js
+++ b/js/controller/BoardController.js
@@ -42,6 +42,8 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
$scope.board = BoardService.getCurrent();
$scope.uploader = FileService.uploader;
+ $scope.deletedCards = [];
+
// workaround for $stateParams changes not being propagated
$scope.$watch(function() {
return $state.params;
@@ -136,6 +138,14 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
});
};
+ $scope.loadDeletedCards = function() {
+ CardService.fetchDeleted($scope.id).then(function (data) {
+ $scope.deletedCards = data;
+ }, function (error) {
+ $scope.statusservice.setError('Error occured', error);
+ });
+ }
+
$scope.loadDefault = function () {
StackService.fetchAll($scope.id).then(function (data) {
$scope.statusservice.releaseWaiting();
@@ -193,6 +203,7 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
}
CardService.delete(card.id).then(function () {
StackService.removeCard(card);
+ $scope.loadDeletedCards();
});
});
};
@@ -384,4 +395,5 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
return card.attachmentCount;
};
+ $scope.loadDeletedCards();
});
diff --git a/js/service/ApiService.js b/js/service/ApiService.js
index 051479417..2705739a7 100644
--- a/js/service/ApiService.js
+++ b/js/service/ApiService.js
@@ -114,6 +114,18 @@ app.factory('ApiService', function ($http, $q) {
};
+ ApiService.prototype.softDelete = 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;
+ };
// methods for managing data
ApiService.prototype.clear = function () {
diff --git a/js/service/CardService.js b/js/service/CardService.js
index 51edc4e1d..614ef1e8e 100644
--- a/js/service/CardService.js
+++ b/js/service/CardService.js
@@ -27,6 +27,8 @@ app.factory('CardService', function (ApiService, $http, $q) {
};
CardService.prototype = angular.copy(ApiService.prototype);
+ CardService.prototype.delete = CardService.prototype.softDelete;
+
CardService.prototype.reorder = function (card, order) {
var deferred = $q.defer();
var self = this;
@@ -172,6 +174,22 @@ 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;
+ return objects;
+ deferred.resolve(self.data);
+ }, function (error) {
+ deferred.reject('Fetching ' + self.endpoint + ' failed');
+ });
+ return deferred.promise;
+
+ };
+
+
var service = new CardService($http, 'cards', $q);
return service;
});
diff --git a/lib/Controller/CardController.php b/lib/Controller/CardController.php
index aeadcb6f9..1084ece45 100644
--- a/lib/Controller/CardController.php
+++ b/lib/Controller/CardController.php
@@ -104,6 +104,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
diff --git a/lib/Db/Card.php b/lib/Db/Card.php
index 21f65d79e..f065fc10a 100644
--- a/lib/Db/Card.php
+++ b/lib/Db/Card.php
@@ -42,6 +42,7 @@ class Card extends RelationalEntity {
protected $archived = false;
protected $duedate;
protected $notified = false;
+ protected $deletedAt;
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');
diff --git a/lib/Db/CardMapper.php b/lib/Db/CardMapper.php
index e0234fc43..7cb5d1f57 100644
--- a/lib/Db/CardMapper.php
+++ b/lib/Db/CardMapper.php
@@ -124,6 +124,13 @@ class CardMapper extends DeckMapper implements IPermissionMapper {
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 AND c.deleted_at <= ? ORDER BY `c`.`order`';
+ return $this->findEntities($sql, [$boardId, time()], $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 {
}
-}
\ No newline at end of file
+}
diff --git a/lib/Service/CardService.php b/lib/Service/CardService.php
index a9b7ae7a2..8b040f4e7 100644
--- a/lib/Service/CardService.php
+++ b/lib/Service/CardService.php
@@ -56,6 +56,10 @@ class CardService {
$this->currentUser = $userId;
}
+ public function fetchDeleted($boardId) {
+ return $this->cardMapper->findDeleted($boardId);
+ }
+
public function find($cardId) {
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
$card = $this->cardMapper->find($cardId);
@@ -89,7 +93,10 @@ 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) {
diff --git a/templates/part.board.sidebarView.php b/templates/part.board.sidebarView.php
index 7229af2dd..b168d0cfa 100644
--- a/templates/part.board.sidebarView.php
+++ b/templates/part.board.sidebarView.php
@@ -14,6 +14,7 @@
@@ -118,4 +119,13 @@
+
+
+
+ -
+{{123}}
+{{deletedCard}}
+
+
+