Compare commits

...

11 Commits

Author SHA1 Message Date
Julius Härtl
452f9a3312 Update CHANGELOG.md for 0.1.4
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2017-05-04 22:02:56 +02:00
Julius Härtl
95b0f49739 Bump version to 0.1.4
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2017-05-04 14:15:43 +02:00
Julius Härtl
b330f03a32 Merge pull request #118 from nextcloud/fix-acl-nonexisting
Do not fail on nonexisting acl users/groups
2017-05-04 14:12:28 +02:00
Julius Härtl
12ebffb885 Do not use getDisplayName for groups
it is not supported in Nextcloud 11

https://github.com/nextcloud/server/pull/4690
https://github.com/nextcloud/server/pull/2833
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2017-05-04 12:10:17 +02:00
Julius Härtl
860cbab1d3 Add proper error handling when user/group cannot be found
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2017-05-04 11:26:45 +02:00
Julius Härtl
c08455e11b Merge pull request #113 from nextcloud/delete-fix-and-undo
Fix deleting boards and add a simple undo function
2017-05-04 08:37:05 +02:00
Julius Härtl
a6b6842e2b No not fail on nonexisting acl users/groups
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2017-05-03 13:25:58 +02:00
Julius Härtl
588252d949 Fix deleting boards and add a simple undo function
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2017-05-03 13:22:30 +02:00
Julius Härtl
3b0390f50f Merge pull request #114 from nextcloud/firefox-red-input
Remove firefox invalid box shadow
2017-05-03 09:35:44 +02:00
Julius Härtl
c8389fbcb0 Remove firefox invalid box shadow
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2017-05-02 09:05:11 +02:00
Julius Härtl
94788bed39 Add IRC channel to README 2017-05-01 14:16:10 +02:00
14 changed files with 218 additions and 19 deletions

View File

@@ -1,3 +1,15 @@
# Changelog
All notable changes to this project will be documented in this file.
## 0.1.4
### Fixed
- Avoid red shadow on input in firefox
- Fix broken delete function for boards
- Fix broken board loading when groups were used for sharing
- Fix bug when users/groups got deleted
## 0.1.3 - 2017-05-01
### Added

View File

@@ -1,6 +1,7 @@
# Deck
[![Build Status](https://travis-ci.org/nextcloud/deck.svg?branch=master)](https://travis-ci.org/nextcloud/deck) [![CodeCov](https://codecov.io/github/nextcloud/deck/coverage.svg?branch=master)](https://codecov.io/github/nextcloud/deck) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/nextcloud/deck/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/nextcloud/deck/?branch=master) [![Dependency Status](https://www.versioneye.com/user/projects/58ad558f4ca76f004ed475b3/badge.svg?style=flat)](https://www.versioneye.com/user/projects/58ad558f4ca76f004ed475b3)
[![Build Status](https://travis-ci.org/nextcloud/deck.svg?branch=master)](https://travis-ci.org/nextcloud/deck) [![CodeCov](https://codecov.io/github/nextcloud/deck/coverage.svg?branch=master)](https://codecov.io/github/nextcloud/deck) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/nextcloud/deck/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/nextcloud/deck/?branch=master) [![Dependency Status](https://www.versioneye.com/user/projects/58ad558f4ca76f004ed475b3/badge.svg?style=flat)](https://www.versioneye.com/user/projects/58ad558f4ca76f004ed475b3) [![#nextcloud-deck](https://img.shields.io/badge/IRC-%23nextcloud--deck%20on%20freenode-blue.svg)](https://webchat.freenode.net/?channels=nextcloud-deck)
Deck is a kanban style organization tool aimed at personal planning and project organization for teams integrated with Nextcloud.

View File

@@ -16,7 +16,7 @@
💥 This is still alpha software: it may not be stable enough for production!
</description>
<version>0.1.3</version>
<version>0.1.4</version>
<licence>agpl</licence>
<author>Julius Härtl</author>
<namespace>Deck</namespace>
@@ -30,4 +30,9 @@
<dependencies>
<nextcloud min-version="11" max-version="12" />
</dependencies>
<repair-steps>
<post-migration>
<step>OCA\Deck\Migration\UnknownUsers</step>
</post-migration>
</repair-steps>
</info>

View File

@@ -86,6 +86,13 @@ button:hover {
cursor: pointer;
}
#app-navigation .app-navigation-entry-utils li.board-delete-undo {
background-color: #aa0000;
color: #fff;
cursor: pointer;
width: auto !important;
}
/**
* Board view
*/
@@ -202,6 +209,18 @@ button:hover {
min-height: initial;
}
#stack-add input:invalid {
box-shadow: none;
}
#stack-add input:-moz-submit-invalid {
box-shadow: none;
}
#stack-add input:-moz-ui-invalid {
box-shadow:none;
}
#stack-add button {
display: table-cell;
vertical-align: middle;

View File

@@ -21,10 +21,12 @@
*
*/
app.controller('ListController', function ($scope, $location, $filter, BoardService, $element) {
app.controller('ListController', function ($scope, $location, $filter, BoardService, $element, $timeout) {
$scope.boards = [];
$scope.newBoard = {};
$scope.status = {};
$scope.status = {
deleteUndo: []
};
$scope.colors = ['0082c9', '00c9c6','00c906', 'c92b00', 'F1DB50', '7C31CC', '3A3B3D', 'CACBCD'];
$scope.boardservice = BoardService;
$scope.newBoard.color = $scope.colors[0];
@@ -71,9 +73,25 @@ app.controller('ListController', function ($scope, $location, $filter, BoardServ
};
$scope.boardDelete = function(board) {
BoardService.delete(board.id).then(function (data) {
$scope.filterData();
});
var boardId = board.id;
$scope.status.deleteUndo[boardId] = 10;
$scope.boardDeleteCountdown = function () {
console.log($scope.status);
if($scope.status.deleteUndo[boardId] > 0) {
$scope.status.deleteUndo[boardId]--;
$timeout($scope.boardDeleteCountdown, 1000);
}
if($scope.status.deleteUndo[boardId] === 0) {
BoardService.delete(board.id).then(function (data) {
$scope.filterData();
});
}
};
$timeout($scope.boardDeleteCountdown, 1000);
};
$scope.boardDeleteUndo = function (board) {
delete $scope.status.deleteUndo[board.id];
};
});

View File

@@ -23,9 +23,14 @@
namespace OCA\Deck\AppInfo;
use OCA\Deck\Db\Acl;
use OCA\Deck\Db\AclMapper;
use OCP\AppFramework\App;
use OCA\Deck\Middleware\SharingMiddleware;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IUser;
use OCP\IUserManager;
class Application extends App {
@@ -48,6 +53,30 @@ class Application extends App {
});
$container->registerMiddleware('SharingMiddleware');
// Delete user/group acl entries when they get deleted
/** @var IUserManager $userManager */
$userManager = $server->getUserManager();
$userManager->listen('\OC\User', 'postDelete', function(IUser $user) use ($container) {
/** @var AclMapper $aclMapper */
$aclMapper = $container->query(AclMapper::class);
$acls = $aclMapper->findByParticipant(Acl::PERMISSION_TYPE_USER, $user->getUID());
foreach ($acls as $acl) {
$aclMapper->delete($acl);
}
});
/** @var IUserManager $userManager */
$groupManager = $server->getGroupManager();
$groupManager->listen('\OC\Group', 'postDelete', function(IGroup $group) use ($container) {
/** @var AclMapper $aclMapper */
$aclMapper = $container->query(AclMapper::class);
$aclMapper->findByParticipant(Acl::PERMISSION_TYPE_GROUP, $group->getGID());
$acls = $aclMapper->findByParticipant(Acl::PERMISSION_TYPE_GROUP, $group->getGID());
foreach ($acls as $acl) {
$aclMapper->delete($acl);
}
});
}
public function registerNavigationEntry() {

View File

@@ -48,4 +48,9 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
return $entity->getBoardId();
}
public function findByParticipant($type, $participant) {
$sql = 'SELECT * from *PREFIX*deck_board_acl WHERE type = ? AND participant = ?';
return $this->findEntities($sql, [$type, $participant]);
}
}

View File

@@ -52,7 +52,6 @@ class Board extends RelationalEntity implements JsonSerializable {
if ($this->shared === -1) {
unset($json['shared']);
}
$json['owner'] = $this->resolveOwner();
return $json;
}

View File

@@ -130,6 +130,11 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
return $entries;
}
public function findAll() {
$sql = 'SELECT id from *PREFIX*deck_boards;';
return $this->findEntities($sql, []);
}
public function delete(/** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */
\OCP\AppFramework\Db\Entity $entity) {
// delete acl
@@ -166,10 +171,22 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
$groupManager = $this->groupManager;
$acl->resolveRelation('participant', function($participant) use (&$acl, &$userManager, &$groupManager) {
if($acl->getType() === Acl::PERMISSION_TYPE_USER) {
return new User($userManager->get($acl->getParticipant($participant)));
$user = $userManager->get($participant);
if($user !== null) {
return new User($user);
} else {
\OC::$server->getLogger()->debug('User ' . $acl->getId() . ' not found when mapping acl ' . $acl->getParticipant());
return null;
}
}
if($acl->getType() === Acl::PERMISSION_TYPE_GROUP) {
return new Group($groupManager->get($acl->getParticipant($participant)));
$group = $groupManager->get($participant);
if($group !== null) {
return new Group($group);
} else {
\OC::$server->getLogger()->debug('Group ' . $acl->getId() . ' not found when mapping acl ' . $acl->getParticipant());
return null;
}
}
throw new \Exception('Unknown permission type for mapping Acl');
});
@@ -181,7 +198,11 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
public function mapOwner(Board &$board) {
$userManager = $this->userManager;
$board->resolveRelation('owner', function($owner) use (&$userManager) {
return new User($userManager->get($owner));
$user = $userManager->get($owner);
if($user !== null) {
return new User($user);
}
return null;
});
}

View File

@@ -31,6 +31,7 @@ use OCP\IUserManager;
class CardMapper extends DeckMapper implements IPermissionMapper {
private $labelMapper;
private $userManager;
public function __construct(IDBConnection $db, LabelMapper $labelMapper, IUserManager $userManager) {
parent::__construct($db, 'deck_cards', '\OCA\Deck\Db\Card');
@@ -131,7 +132,11 @@ class CardMapper extends DeckMapper implements IPermissionMapper {
public function mapOwner(Card &$card) {
$userManager = $this->userManager;
$card->resolveRelation('owner', function($owner) use (&$userManager) {
return new User($userManager->get($owner));
$user = $userManager->get($owner);
if($user !== null) {
return new User($user);
}
return null;
});
}

View File

@@ -35,7 +35,7 @@ class Group extends RelationalObject {
public function getObjectSerialization() {
return [
'uid' => $this->object->getGID(),
'displayname' => $this->object->getDisplayName()
'displayname' => $this->object->getGID()
];
}
}

View File

@@ -25,7 +25,8 @@ namespace OCA\Deck\Db;
class RelationalObject implements \JsonSerializable {
private $primaryKey;
protected $primaryKey;
protected $object;
/**
* RelationalObject constructor.

View File

@@ -0,0 +1,83 @@
<?php
/**
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
*
* @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\Migration;
use OCA\Deck\Db\Acl;
use OCA\Deck\Db\AclMapper;
use OCA\Deck\Db\Board;
use OCA\Deck\Db\BoardMapper;
use OCP\IGroupManager;
use OCP\IUserManager;
use OCP\Migration\IRepairStep;
use OCP\Migration\IOutput;
class UnknownUsers implements IRepairStep {
private $userManager;
private $groupManager;
private $aclMapper;
private $boardMapper;
public function __construct(IUserManager $userManager, IGroupManager $groupManager, AclMapper $aclMapper, BoardMapper $boardMapper) {
$this->userManager = $userManager;
$this->groupManager = $groupManager;
$this->aclMapper = $aclMapper;
$this->boardMapper = $boardMapper;
}
/*
* @inheritdoc
*/
public function getName() {
return 'Delete orphaned ACL rules';
}
/**
* @inheritdoc
*/
public function run(IOutput $output) {
$boards = $this->boardMapper->findAll();
/** @var Board $board */
foreach ($boards as $board) {
$acls = $this->aclMapper->findAll($board->getId());
/** @var Acl $acl */
foreach ($acls as $acl) {
if($acl->getType() === Acl::PERMISSION_TYPE_USER) {
$user = $this->userManager->get($acl->getParticipant());
if($user === null) {
$this->aclMapper->delete($acl);
}
}
if($acl->getType() === Acl::PERMISSION_TYPE_GROUP) {
$group = $this->groupManager->get($acl->getParticipant());
if($group === null) {
$this->aclMapper->delete($acl);
}
}
}
}
}
}

View File

@@ -7,15 +7,16 @@
<a href="#!/board/{{b.id}}/" ng-if="!b.status.edit">{{ b.title }}</a>
<div class="app-navigation-entry-utils" ng-show="!b.status.edit" style="position:absolute;">
<ul>
<li class="app-navigation-entry-utils-counter board-delete-undo" ng-show="status.deleteUndo[b.id]" ng-click="boardDeleteUndo(b)" title="Click to undo">Deleting in {{ status.deleteUndo[b.id] }}s &nbsp; X</li>
<li class="app-navigation-entry-utils-menu-share svg" ng-show="b.shared>0"><i class="icon icon-share" title="<?php p($l->t('Shared with you')); ?>"> </i></li>
<li class="app-navigation-entry-utils-menu-button svg"><button class="icon-more"></button></li>
<li class="app-navigation-entry-utils-menu-button svg" ng-show="!status.deleteUndo[b.id]"><button class="icon-more"></button></li>
</ul>
</div>
<div class="app-navigation-entry-menu app-navigation-noclose" ng-show="!b.status.edit">
<ul>
<li ng-show="b.owner===user"><button class="icon-rename svg" title="<?php p($l->t('edit')); ?>" ng-click="b.status.edit=true"></button></li>
<li ng-show="b.owner===user"><button class="icon-delete svg" title="<?php p($l->t('delete')); ?>" ng-click="boardDelete(b)"></button></li>
<li ng-show="b.owner!==user"><button class="icon-delete svg" title="<?php p($l->t('remove share')); ?>" ng-click="boardRemoveShare(b)"></button></li>
<li ng-show="b.owner.uid===user"><button class="icon-rename svg" title="<?php p($l->t('edit')); ?>" ng-click="b.status.edit=true"></button></li>
<li ng-show="b.owner.uid===user"><button class="icon-delete svg" title="<?php p($l->t('delete')); ?>" ng-click="boardDelete(b)"></button></li>
<li ng-show="b.owner.uid!==user && false"><button class="icon-delete svg" title="<?php p($l->t('remove share')); ?>" ng-click="boardRemoveShare(b)"></button></li>
</ul>
</div>
<div class="app-navigation-entry-deleted" ng-show="false">