refactoring and stack undo delete early wip
Signed-off-by: Manuel Arno Korfmann <manu@korfmann.info> stack soft delete done Signed-off-by: Manuel Arno Korfmann <manu@korfmann.info> stack undo delete done Signed-off-by: Manuel Arno Korfmann <manu@korfmann.info> stack undo: code review remarks and fixes Signed-off-by: Manuel Arno Korfmann <manu@korfmann.info>
This commit is contained in:
committed by
Julius Härtl
parent
f2795f120b
commit
ef4ce31c47
@@ -77,6 +77,14 @@
|
|||||||
<length>8</length>
|
<length>8</length>
|
||||||
<notnull>false</notnull>
|
<notnull>false</notnull>
|
||||||
</field>
|
</field>
|
||||||
|
<field>
|
||||||
|
<name>deleted_at</name>
|
||||||
|
<type>integer</type>
|
||||||
|
<default>0</default>
|
||||||
|
<length>8</length>
|
||||||
|
<notnull>false</notnull>
|
||||||
|
<unsigned>true</unsigned>
|
||||||
|
</field>
|
||||||
<index>
|
<index>
|
||||||
<name>deck_stacks_board_id_index</name>
|
<name>deck_stacks_board_id_index</name>
|
||||||
<field>
|
<field>
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ return [
|
|||||||
['name' => 'stack#update', 'url' => '/stacks/{stackId}', 'verb' => 'PUT'],
|
['name' => 'stack#update', 'url' => '/stacks/{stackId}', 'verb' => 'PUT'],
|
||||||
['name' => 'stack#reorder', 'url' => '/stacks/{stackId}/reorder', 'verb' => 'PUT'],
|
['name' => 'stack#reorder', 'url' => '/stacks/{stackId}/reorder', 'verb' => 'PUT'],
|
||||||
['name' => 'stack#delete', 'url' => '/stacks/{stackId}', 'verb' => 'DELETE'],
|
['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'],
|
['name' => 'stack#archived', 'url' => '/stacks/{boardId}/archived', 'verb' => 'GET'],
|
||||||
|
|
||||||
// cards
|
// cards
|
||||||
|
|||||||
@@ -1215,12 +1215,18 @@ input.input-inline {
|
|||||||
|
|
||||||
.tabHeaders {
|
.tabHeaders {
|
||||||
clear: both;
|
clear: both;
|
||||||
overflow: hidden;
|
overflow: initial;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabsContainer {
|
.tabsContainer {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
height: 100%;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-select-offscreen {
|
.ui-select-offscreen {
|
||||||
|
|||||||
@@ -4,20 +4,20 @@
|
|||||||
* @author Julius Härtl <jus@bitgrid.net>
|
* @author Julius Härtl <jus@bitgrid.net>
|
||||||
*
|
*
|
||||||
* @license GNU AGPL version 3 or any later version
|
* @license GNU AGPL version 3 or any later version
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Affero General Public License as
|
* it under the terms of the GNU Affero General Public License as
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
* License, or (at your option) any later version.
|
* License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU Affero General Public License for more details.
|
* GNU Affero General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* 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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import app from '../app/App.js';
|
import app from '../app/App.js';
|
||||||
@@ -42,7 +42,17 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
|
|||||||
$scope.board = BoardService.getCurrent();
|
$scope.board = BoardService.getCurrent();
|
||||||
$scope.uploader = FileService.uploader;
|
$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
|
// workaround for $stateParams changes not being propagated
|
||||||
$scope.$watch(function() {
|
$scope.$watch(function() {
|
||||||
@@ -138,9 +148,11 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.loadDeletedCards = function() {
|
$scope.loadDeletedEntity = function(service, scopeKey) {
|
||||||
CardService.fetchDeleted($scope.id).then(function (data) {
|
service.fetchDeleted($scope.id).then(function (data) {
|
||||||
$scope.deletedCards = data;
|
for(i=0;i<data.length;i++) {
|
||||||
|
$scope[scopeKey][data[i].id] = data[i];
|
||||||
|
}
|
||||||
}, function (error) {
|
}, function (error) {
|
||||||
$scope.statusservice.setError('Error occured', error);
|
$scope.statusservice.setError('Error occured', error);
|
||||||
});
|
});
|
||||||
@@ -196,30 +208,30 @@ 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.stackUndoDelete = function (deletedStack) {
|
||||||
|
StackService.undoDelete(deletedStack).then(function() {
|
||||||
|
delete $scope.deletedStacks[deletedStack.id];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$scope.cardDelete = function (card) {
|
$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) {
|
CardService.delete(card.id).then(function () {
|
||||||
if (!state) {
|
StackService.removeCard(card);
|
||||||
return;
|
$scope.deletedCards[card.id] = card;
|
||||||
}
|
|
||||||
CardService.delete(card.id).then(function () {
|
|
||||||
StackService.removeCard(card);
|
|
||||||
$scope.deletedCards.push(card);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.cardUndoDelete = function (deletedCard) {
|
$scope.cardUndoDelete = function (deletedCard) {
|
||||||
CardService.undoDelete(deletedCard);
|
CardService.undoDelete(deletedCard).then(function() {
|
||||||
StackService.addCard(deletedCard);
|
StackService.addCard(deletedCard);
|
||||||
$scope.removeFromDeletedCards(deletedCard);
|
delete $scope.deletedCards[deletedCard.id];
|
||||||
};
|
});
|
||||||
|
|
||||||
$scope.removeFromDeletedCards = function(deletedCard) {
|
|
||||||
for(var i=0;i<$scope.deletedCards.length;i++) {
|
|
||||||
if($scope.deletedCards[i].id === deletedCard.id) {
|
|
||||||
$scope.deletedCards.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.cardArchive = function (card) {
|
$scope.cardArchive = function (card) {
|
||||||
@@ -262,7 +274,6 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
|
|||||||
// TODO: remove from cards
|
// TODO: remove from cards
|
||||||
};
|
};
|
||||||
$scope.labelCreate = function (label) {
|
$scope.labelCreate = function (label) {
|
||||||
alert(label);
|
|
||||||
label.boardId = $scope.id;
|
label.boardId = $scope.id;
|
||||||
LabelService.create(label).then(function (data) {
|
LabelService.create(label).then(function (data) {
|
||||||
$scope.newStack.title = '';
|
$scope.newStack.title = '';
|
||||||
@@ -410,6 +421,4 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
|
|||||||
}
|
}
|
||||||
return card.attachmentCount;
|
return card.attachmentCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.loadDeletedCards();
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,20 +4,20 @@
|
|||||||
* @author Julius Härtl <jus@bitgrid.net>
|
* @author Julius Härtl <jus@bitgrid.net>
|
||||||
*
|
*
|
||||||
* @license GNU AGPL version 3 or any later version
|
* @license GNU AGPL version 3 or any later version
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Affero General Public License as
|
* it under the terms of the GNU Affero General Public License as
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
* License, or (at your option) any later version.
|
* License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU Affero General Public License for more details.
|
* GNU Affero General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* 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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from '../app/App.js';
|
import app from '../app/App.js';
|
||||||
|
|
||||||
@@ -48,6 +48,19 @@ app.factory('ApiService', function ($http, $q) {
|
|||||||
return deferred.promise;
|
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) {
|
ApiService.prototype.fetchOne = function (id) {
|
||||||
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@@ -111,20 +124,19 @@ app.factory('ApiService', function ($http, $q) {
|
|||||||
deferred.reject('Deleting ' + self.endpoint + ' failed');
|
deferred.reject('Deleting ' + self.endpoint + ' failed');
|
||||||
});
|
});
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ApiService.prototype.softDelete = function (id) {
|
ApiService.prototype.undoDelete = function(entity) {
|
||||||
var deferred = $q.defer();
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
entity.deletedAt = 0;
|
||||||
$http.delete(this.baseUrl + '/' + id).then(function (response) {
|
|
||||||
self.data[id].deletedAt = response.data.deletedAt;
|
var promise = this.update(entity);
|
||||||
deferred.resolve(response.data);
|
|
||||||
}, function (error) {
|
promise.then(function() {
|
||||||
deferred.reject('Deleting ' + self.endpoint + ' failed');
|
self.data[entity.id] = entity;
|
||||||
});
|
});
|
||||||
return deferred.promise;
|
|
||||||
|
return promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
// methods for managing data
|
// methods for managing data
|
||||||
|
|||||||
@@ -4,20 +4,20 @@
|
|||||||
* @author Julius Härtl <jus@bitgrid.net>
|
* @author Julius Härtl <jus@bitgrid.net>
|
||||||
*
|
*
|
||||||
* @license GNU AGPL version 3 or any later version
|
* @license GNU AGPL version 3 or any later version
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Affero General Public License as
|
* it under the terms of the GNU Affero General Public License as
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
* License, or (at your option) any later version.
|
* License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU Affero General Public License for more details.
|
* GNU Affero General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* 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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from '../app/App.js';
|
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 = 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) {
|
CardService.prototype.reorder = function (card, order) {
|
||||||
var deferred = $q.defer();
|
var deferred = $q.defer();
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -179,19 +172,6 @@ app.factory('CardService', function (ApiService, $http, $q) {
|
|||||||
return deferred.promise;
|
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);
|
var service = new CardService($http, 'cards', $q);
|
||||||
return service;
|
return service;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,20 +4,20 @@
|
|||||||
* @author Julius Härtl <jus@bitgrid.net>
|
* @author Julius Härtl <jus@bitgrid.net>
|
||||||
*
|
*
|
||||||
* @license GNU AGPL version 3 or any later version
|
* @license GNU AGPL version 3 or any later version
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Affero General Public License as
|
* it under the terms of the GNU Affero General Public License as
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
* License, or (at your option) any later version.
|
* License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU Affero General Public License for more details.
|
* GNU Affero General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* 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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import app from '../app/App.js';
|
import app from '../app/App.js';
|
||||||
|
|
||||||
@@ -27,6 +27,7 @@ app.factory('StackService', function (ApiService, CardService, $http, $q) {
|
|||||||
ApiService.call(this, $http, ep, $q);
|
ApiService.call(this, $http, ep, $q);
|
||||||
};
|
};
|
||||||
StackService.prototype = angular.copy(ApiService.prototype);
|
StackService.prototype = angular.copy(ApiService.prototype);
|
||||||
|
|
||||||
StackService.prototype.fetchAll = function (boardId) {
|
StackService.prototype.fetchAll = function (boardId) {
|
||||||
var deferred = $q.defer();
|
var deferred = $q.defer();
|
||||||
var self = this;
|
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);
|
var service = new StackService($http, 'stacks', $q);
|
||||||
return service;
|
return service;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -74,10 +74,11 @@ class StackController extends Controller {
|
|||||||
* @param $title
|
* @param $title
|
||||||
* @param $boardId
|
* @param $boardId
|
||||||
* @param $order
|
* @param $order
|
||||||
|
* @param $deletedAt
|
||||||
* @return \OCP\AppFramework\Db\Entity
|
* @return \OCP\AppFramework\Db\Entity
|
||||||
*/
|
*/
|
||||||
public function update($id, $title, $boardId, $order) {
|
public function update($id, $title, $boardId, $order, $deletedAt) {
|
||||||
return $this->stackService->update($id, $title, $boardId, $order);
|
return $this->stackService->update($id, $title, $boardId, $order, $deletedAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,4 +99,14 @@ class StackController extends Controller {
|
|||||||
public function delete($stackId) {
|
public function delete($stackId) {
|
||||||
return $this->stackService->delete($stackId);
|
return $this->stackService->delete($stackId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
* @param $boardId
|
||||||
|
* @return \OCP\AppFramework\Db\Entity
|
||||||
|
*/
|
||||||
|
public function deleted($boardId) {
|
||||||
|
return $this->stackService->fetchDeleted($boardId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,12 +27,14 @@ class Stack extends RelationalEntity {
|
|||||||
|
|
||||||
protected $title;
|
protected $title;
|
||||||
protected $boardId;
|
protected $boardId;
|
||||||
|
protected $deletedAt;
|
||||||
protected $cards = array();
|
protected $cards = array();
|
||||||
protected $order;
|
protected $order;
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->addType('id', 'integer');
|
$this->addType('id', 'integer');
|
||||||
$this->addType('boardId', 'integer');
|
$this->addType('boardId', 'integer');
|
||||||
|
$this->addType('deletedAt', 'integer');
|
||||||
$this->addType('order', 'integer');
|
$this->addType('order', 'integer');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,4 +49,4 @@ class Stack extends RelationalEntity {
|
|||||||
}
|
}
|
||||||
return $json;
|
return $json;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,10 +51,17 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
|
|||||||
|
|
||||||
|
|
||||||
public function findAll($boardId, $limit = null, $offset = null) {
|
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);
|
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) {
|
public function delete(Entity $entity) {
|
||||||
// delete cards on stack
|
// delete cards on stack
|
||||||
@@ -73,4 +80,4 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
|
|||||||
$entity = $this->find($stackId);
|
$entity = $this->find($stackId);
|
||||||
return $entity->getBoardId();
|
return $entity->getBoardId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ namespace OCA\Deck\Service;
|
|||||||
|
|
||||||
use OCA\Deck\Db\Acl;
|
use OCA\Deck\Db\Acl;
|
||||||
use OCA\Deck\Db\CardMapper;
|
use OCA\Deck\Db\CardMapper;
|
||||||
|
use OCA\Deck\Db\BoardMapper;
|
||||||
use OCA\Deck\Db\LabelMapper;
|
use OCA\Deck\Db\LabelMapper;
|
||||||
use OCA\Deck\Db\AssignedUsersMapper;
|
use OCA\Deck\Db\AssignedUsersMapper;
|
||||||
use OCA\Deck\Db\Stack;
|
use OCA\Deck\Db\Stack;
|
||||||
@@ -38,6 +39,7 @@ class StackService {
|
|||||||
|
|
||||||
private $stackMapper;
|
private $stackMapper;
|
||||||
private $cardMapper;
|
private $cardMapper;
|
||||||
|
private $boardMapper;
|
||||||
private $labelMapper;
|
private $labelMapper;
|
||||||
private $permissionService;
|
private $permissionService;
|
||||||
private $boardService;
|
private $boardService;
|
||||||
@@ -46,6 +48,7 @@ class StackService {
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
StackMapper $stackMapper,
|
StackMapper $stackMapper,
|
||||||
|
BoardMapper $boardMapper,
|
||||||
CardMapper $cardMapper,
|
CardMapper $cardMapper,
|
||||||
LabelMapper $labelMapper,
|
LabelMapper $labelMapper,
|
||||||
PermissionService $permissionService,
|
PermissionService $permissionService,
|
||||||
@@ -54,6 +57,7 @@ class StackService {
|
|||||||
AttachmentService $attachmentService
|
AttachmentService $attachmentService
|
||||||
) {
|
) {
|
||||||
$this->stackMapper = $stackMapper;
|
$this->stackMapper = $stackMapper;
|
||||||
|
$this->boardMapper = $boardMapper;
|
||||||
$this->cardMapper = $cardMapper;
|
$this->cardMapper = $cardMapper;
|
||||||
$this->labelMapper = $labelMapper;
|
$this->labelMapper = $labelMapper;
|
||||||
$this->permissionService = $permissionService;
|
$this->permissionService = $permissionService;
|
||||||
@@ -81,6 +85,11 @@ class StackService {
|
|||||||
return $stacks;
|
return $stacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function fetchDeleted($boardId) {
|
||||||
|
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
|
||||||
|
return $this->stackMapper->findDeleted($boardId);
|
||||||
|
}
|
||||||
|
|
||||||
public function findAllArchived($boardId) {
|
public function findAllArchived($boardId) {
|
||||||
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_READ);
|
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_READ);
|
||||||
$stacks = $this->stackMapper->findAll($boardId);
|
$stacks = $this->stackMapper->findAll($boardId);
|
||||||
@@ -115,10 +124,16 @@ class StackService {
|
|||||||
|
|
||||||
public function delete($id) {
|
public function delete($id) {
|
||||||
$this->permissionService->checkPermission($this->stackMapper, $id, Acl::PERMISSION_MANAGE);
|
$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);
|
$this->permissionService->checkPermission($this->stackMapper, $id, Acl::PERMISSION_MANAGE);
|
||||||
if ($this->boardService->isArchived($this->stackMapper, $id)) {
|
if ($this->boardService->isArchived($this->stackMapper, $id)) {
|
||||||
throw new StatusException('Operation not allowed. This board is archived.');
|
throw new StatusException('Operation not allowed. This board is archived.');
|
||||||
@@ -127,6 +142,7 @@ class StackService {
|
|||||||
$stack->setTitle($title);
|
$stack->setTitle($title);
|
||||||
$stack->setBoardId($boardId);
|
$stack->setBoardId($boardId);
|
||||||
$stack->setOrder($order);
|
$stack->setOrder($order);
|
||||||
|
$stack->setDeletedAt($deletedAt);
|
||||||
return $this->stackMapper->update($stack);
|
return $this->stackMapper->update($stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
</form>
|
</form>
|
||||||
<button class="icon-delete button-inline stack-actions"
|
<button class="icon-delete button-inline stack-actions"
|
||||||
ng-if="!s.status.editStack"
|
ng-if="!s.status.editStack"
|
||||||
ng-click="stackservice.delete(s.id)"></button>
|
ng-click="stackDelete(s)"></button>
|
||||||
</h3>
|
</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}">
|
<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"
|
<li class="card as-sortable-item"
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
<p>{{ statusservice.text }}</p></div>
|
<p>{{ statusservice.text }}</p></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="sidebar-header">
|
<div id="sidebar-header">
|
||||||
<a class="icon-close" ui-sref="board" ng-click="sidebar.show=!sidebar.show" title="<?php p($l->t('Close')); ?>"> <?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
|
?><span class="hidden-visually"><?php p($l->t('Close')); ?></span><?php
|
||||||
?></a>
|
?></a>
|
||||||
<h3>{{ boardservice.getCurrent().title }}</h3>
|
<h3>{{ boardservice.getCurrent().title }}</h3>
|
||||||
</div>
|
</div>
|
||||||
@@ -14,7 +14,8 @@
|
|||||||
<ul class="tabHeaders">
|
<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==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==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 Cards')); ?></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>
|
</ul>
|
||||||
<div class="tabsContainer">
|
<div class="tabsContainer">
|
||||||
<div id="tabBoardShare" class="tab" ng-if="params.tab==0 || !params.tab">
|
<div id="tabBoardShare" class="tab" ng-if="params.tab==0 || !params.tab">
|
||||||
@@ -120,17 +121,30 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="board-detail-deleted-cards" class="tab deletedCardsTabView" ng-if="params.tab==2">
|
<div id="board-detail-deleted-stacks" class="tab deletedStacksTabView" ng-if="params.tab==2">
|
||||||
<ul>
|
<ul>
|
||||||
<li ng-repeat="deletedCard in deletedCards">
|
<li ng-repeat="deletedStack in deletedStacks">
|
||||||
<dl>
|
<dl>
|
||||||
<dt>Title</dt>
|
<dt>Title</dt>
|
||||||
<dd>{{deletedCard.title}}<dd>
|
<dd>{{deletedStack.title}}<dd>
|
||||||
<dt>Stack</dt>
|
</dl>
|
||||||
<dd>{{stackservice.data[deletedCard.stackId].title}}</dd>
|
<a ng-click="stackUndoDelete(deletedStack)"><span class="icon icon-undo"></span><br /><span><?php p($l->t('Undo delete')); ?></span></a>
|
||||||
</dl>
|
</li>
|
||||||
<a ng-click="cardUndoDelete(deletedCard)"><span class="icon icon-undo"></span><br /><span><?php p($l->t('Undo delete')); ?></span></a>
|
</ul>
|
||||||
</li>
|
</div>
|
||||||
</ul>
|
|
||||||
|
<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>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user