diff --git a/appinfo/routes.php b/appinfo/routes.php
index 4ab64f84a..96774f444 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -65,7 +65,12 @@ return [
['name' => 'card#removeLabel', 'url' => '/cards/{cardId}/label/{labelId}', 'verb' => 'DELETE'],
['name' => 'card#assignUser', 'url' => '/cards/{cardId}/assign', 'verb' => 'POST'],
['name' => 'card#unassignUser', 'url' => '/cards/{cardId}/unassign', 'verb' => 'PUT'],
+
+ // dashboard
+ ['name' => 'card#findAllWithDue', 'url' => '/dashboard/due', 'verb' => 'GET'],
+ ['name' => 'card#findMyAssignedCards', 'url' => '/dashboard/assigned', 'verb' => 'GET'],
+ // attachments
['name' => 'attachment#getAll', 'url' => '/cards/{cardId}/attachments', 'verb' => 'GET'],
['name' => 'attachment#create', 'url' => '/cards/{cardId}/attachment', 'verb' => 'POST'],
['name' => 'attachment#display', 'url' => '/cards/{cardId}/attachment/{attachmentId}', 'verb' => 'GET'],
@@ -110,6 +115,8 @@ return [
['name' => 'card_api#reorder', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/reorder', 'verb' => 'PUT'],
['name' => 'card_api#delete', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}', 'verb' => 'DELETE'],
+ ['name' => 'card_api#findAllWithDue', 'url' => '/api/v1.0/dashboard/due', 'verb' => 'GET'],
+
['name' => 'label_api#get', 'url' => '/api/v1.0/boards/{boardId}/labels/{labelId}', 'verb' => 'GET'],
['name' => 'label_api#create', 'url' => '/api/v1.0/boards/{boardId}/labels', 'verb' => 'POST'],
['name' => 'label_api#update', 'url' => '/api/v1.0/boards/{boardId}/labels/{labelId}', 'verb' => 'PUT'],
diff --git a/lib/Controller/CardController.php b/lib/Controller/CardController.php
index dcb3e6046..db6bc08bd 100644
--- a/lib/Controller/CardController.php
+++ b/lib/Controller/CardController.php
@@ -25,6 +25,7 @@ namespace OCA\Deck\Controller;
use OCA\Deck\Service\AssignmentService;
use OCA\Deck\Service\CardService;
+use OCA\Deck\Service\DashboardService;
use OCP\IRequest;
use OCP\AppFramework\Controller;
@@ -33,10 +34,11 @@ class CardController extends Controller {
private $cardService;
private $assignmentService;
- public function __construct($appName, IRequest $request, CardService $cardService, AssignmentService $assignmentService, $userId) {
+ public function __construct($appName, IRequest $request, CardService $cardService, DashboardService $dashboardService, AssignmentService $assignmentService, $userId) {
parent::__construct($appName, $request);
$this->userId = $userId;
$this->cardService = $cardService;
+ $this->dashboardService = $dashboardService;
$this->assignmentService = $assignmentService;
}
@@ -165,4 +167,20 @@ class CardController extends Controller {
public function unassignUser($cardId, $userId, $type = 0) {
return $this->assignmentService->unassignUser($cardId, $userId, $type);
}
+
+ /**
+ * @NoAdminRequired
+ * @return array
+ */
+ public function findAllWithDue($userId) {
+ return $this->dashboardService->findAllWithDue($userId);
+ }
+
+ /**
+ * @NoAdminRequired
+ * @return array
+ */
+ public function findMyAssignedCards($userId) {
+ return $this->dashboardService->findMyAssignedCards($userId);
+ }
}
diff --git a/lib/Db/CardMapper.php b/lib/Db/CardMapper.php
index ad2c97724..1a77dac0d 100644
--- a/lib/Db/CardMapper.php
+++ b/lib/Db/CardMapper.php
@@ -141,6 +141,23 @@ class CardMapper extends DeckMapper implements IPermissionMapper {
return $this->findEntities($sql, [$stackId], $limit, $offset);
}
+ public function findAllWithDue($boardId) {
+ $sql = 'SELECT c.* FROM `*PREFIX*deck_cards` c
+ INNER JOIN `*PREFIX*deck_stacks` s ON s.id = c.stack_id
+ INNER JOIN `*PREFIX*deck_boards` b ON b.id = s.board_id
+ WHERE `s`.`board_id` = ? AND duedate IS NOT NULL AND NOT c.archived AND c.deleted_at = 0 AND s.deleted_at = 0 AND NOT b.archived AND b.deleted_at = 0';
+ return $this->findEntities($sql, [$boardId]);
+ }
+
+ public function findMyAssignedCards($boardId, $username) {
+ $sql = 'SELECT c.* FROM `*PREFIX*deck_cards` c
+ INNER JOIN `*PREFIX*deck_stacks` s ON s.id = c.stack_id
+ INNER JOIN `*PREFIX*deck_boards` b ON b.id = s.board_id
+ INNER JOIN `*PREFIX*deck_assigned_users` u ON c.id = card_id
+ WHERE `s`.`board_id` = ? AND participant = ? AND NOT c.archived AND c.deleted_at = 0 AND s.deleted_at = 0 AND NOT b.archived AND b.deleted_at = 0';
+ return $this->findEntities($sql, [$boardId, $username]);
+ }
+
public function findOverdue() {
$sql = 'SELECT id,title,duedate,notified from `*PREFIX*deck_cards` WHERE duedate < NOW() AND NOT archived AND deleted_at = 0';
return $this->findEntities($sql);
diff --git a/lib/Service/CardService.php b/lib/Service/CardService.php
index 50ea6fa49..4b969709a 100644
--- a/lib/Service/CardService.php
+++ b/lib/Service/CardService.php
@@ -564,4 +564,28 @@ class CardService {
'\OCA\Deck\Card::onUpdate', new FTSEvent(null, ['id' => $cardId, 'card' => $card])
);
}
+
+ /**
+ *
+ * @return array
+ * @throws \OCA\Deck\NoPermissionException
+ * @throws BadRequestException
+ */
+ public function findAllWithDue($userId) {
+ $cards = $this->cardMapper->findAllWithDue($userId);
+
+ return $cards;
+ }
+
+ /**
+ *
+ * @return array
+ * @throws \OCA\Deck\NoPermissionException
+ * @throws BadRequestException
+ */
+ public function findMyAssignedCards($userId) {
+ $cards = $this->cardMapper->findMyAssignedCards($userId);
+
+ return $cards;
+ }
}
diff --git a/lib/Service/DashboardService.php b/lib/Service/DashboardService.php
new file mode 100644
index 000000000..41519dbed
--- /dev/null
+++ b/lib/Service/DashboardService.php
@@ -0,0 +1,163 @@
+
+ *
+ * @author Julius Härtl
+ * @author Maxence Lange
+ *
+ * @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 .
+ *
+ */
+
+namespace OCA\Deck\Service;
+
+use OCA\Deck\Activity\ActivityManager;
+use OCA\Deck\Activity\ChangeSet;
+use OCA\Deck\Db\Acl;
+use OCA\Deck\Db\AclMapper;
+use OCA\Deck\Db\AssignedUsersMapper;
+use OCA\Deck\Db\ChangeHelper;
+use OCA\Deck\Db\IPermissionMapper;
+use OCA\Deck\Db\CardMapper;
+use OCA\Deck\Db\Label;
+use OCA\Deck\Db\Stack;
+use OCA\Deck\Db\StackMapper;
+use OCA\Deck\NoPermissionException;
+use OCA\Deck\Notification\NotificationHelper;
+use OCP\AppFramework\Db\DoesNotExistException;
+use OCP\IGroupManager;
+use OCP\IL10N;
+use OCA\Deck\Db\Board;
+use OCA\Deck\Db\BoardMapper;
+use OCA\Deck\Db\LabelMapper;
+use OCP\IUserManager;
+use OCA\Deck\BadRequestException;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\EventDispatcher\GenericEvent;
+
+class DashboardService {
+ private $boardMapper;
+ private $stackMapper;
+ private $labelMapper;
+ private $aclMapper;
+ private $cardMapper;
+ private $l10n;
+ private $permissionService;
+ private $notificationHelper;
+ private $assignedUsersMapper;
+ private $userManager;
+ private $groupManager;
+ private $userId;
+ private $activityManager;
+ /** @var EventDispatcherInterface */
+ private $eventDispatcher;
+ private $changeHelper;
+
+ public function __construct(
+ BoardMapper $boardMapper,
+ StackMapper $stackMapper,
+ IL10N $l10n,
+ LabelMapper $labelMapper,
+ AclMapper $aclMapper,
+ CardMapper $cardMapper,
+ PermissionService $permissionService,
+ NotificationHelper $notificationHelper,
+ AssignedUsersMapper $assignedUsersMapper,
+ IUserManager $userManager,
+ IGroupManager $groupManager,
+ ActivityManager $activityManager,
+ EventDispatcherInterface $eventDispatcher,
+ ChangeHelper $changeHelper,
+ $userId
+ ) {
+ $this->boardMapper = $boardMapper;
+ $this->stackMapper = $stackMapper;
+ $this->labelMapper = $labelMapper;
+ $this->aclMapper = $aclMapper;
+ $this->cardMapper = $cardMapper;
+ $this->l10n = $l10n;
+ $this->permissionService = $permissionService;
+ $this->notificationHelper = $notificationHelper;
+ $this->assignedUsersMapper = $assignedUsersMapper;
+ $this->userManager = $userManager;
+ $this->groupManager = $groupManager;
+ $this->activityManager = $activityManager;
+ $this->eventDispatcher = $eventDispatcher;
+ $this->changeHelper = $changeHelper;
+ $this->userId = $userId;
+ }
+
+ /**
+ * Set a different user than the current one, e.g. when no user is available in occ
+ *
+ * @param string $userId
+ */
+ public function setUserId(string $userId): void {
+ $this->userId = $userId;
+ }
+
+
+ /**
+ * @return array
+ */
+ public function findAllWithDue($userId) {
+ $userInfo = $this->getBoardPrerequisites();
+ $userBoards = $this->findAllBoardsFromUser($userInfo);
+ $allDueCards = [];
+ foreach ($userBoards as $userBoard) {
+ $allDueCards[] = $this->cardMapper->findAllWithDue($userBoard->getId());
+ }
+ return $allDueCards;
+ }
+
+ /**
+ * @return array
+ */
+ public function findMyAssignedCards($userId) {
+ $userInfo = $this->getBoardPrerequisites();
+ $userBoards = $this->findAllBoardsFromUser($userInfo);
+ $allAssignedCards = [];
+ foreach ($userBoards as $userBoard) {
+ $allAssignedCards[] = $this->cardMapper->findMyAssignedCards($userBoard->getId(), $this->userId);
+ }
+ return $allAssignedCards;
+ }
+
+ /**
+ * @return array
+ */
+ private function findAllBoardsFromUser($userInfo, $since = -1) {
+ $userBoards = $this->boardMapper->findAllByUser($userInfo['user'], null, null, $since);
+ $groupBoards = $this->boardMapper->findAllByGroups($userInfo['user'], $userInfo['groups'],null, null, $since);
+ $circleBoards = $this->boardMapper->findAllByCircles($userInfo['user'], null, null, $since);
+ return array_merge($userBoards, $groupBoards, $circleBoards);
+ }
+
+ /**
+ * @return array
+ */
+ private function getBoardPrerequisites() {
+ $groups = $this->groupManager->getUserGroupIds(
+ $this->userManager->get($this->userId)
+ );
+ return [
+ 'user' => $this->userId,
+ 'groups' => $groups
+ ];
+ }
+
+
+}
diff --git a/src/App.vue b/src/App.vue
index c5237165f..ee190cd62 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -85,6 +85,7 @@ export default {
created() {
this.$store.dispatch('loadBoards')
this.$store.dispatch('loadSharees')
+ this.$store.dispatch('loadDashboards')
},
}
diff --git a/src/components/Controls.vue b/src/components/Controls.vue
index 6a4fbb309..258bae031 100644
--- a/src/components/Controls.vue
+++ b/src/components/Controls.vue
@@ -30,6 +30,10 @@
({{ t('deck', 'Archived cards') }})
+
+ -
+ - @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 .
+ -
+ -->
+
+
+
+
+
+
+
+
overdue
+
+
+ {{ card.title }}
+
+
+
+
+
today
+ {{ withDueDashboardGroup.today }}
+
+
+
+
tomorrow
+ {{ withDueDashboardGroup.tomorrow }}
+
+
+
+
this week
+ {{ withDueDashboardGroup.thisWeek }}
+
+
+
+
+
+
overdue
+
+ {{ card.title }}
+
+
+
+
+
today
+ {{ assignedCardsDashboardGroup.today }}
+
+
+
+
tomorrow
+ {{ assignedCardsDashboardGroup.tomorrow }}
+
+
+
+
this week
+ {{ assignedCardsDashboardGroup.thisWeek }}
+
+
+
+
+
+
+
+
+
diff --git a/src/components/navigation/AppNavigation.vue b/src/components/navigation/AppNavigation.vue
index ffd16a029..6430bfd28 100644
--- a/src/components/navigation/AppNavigation.vue
+++ b/src/components/navigation/AppNavigation.vue
@@ -23,6 +23,14 @@
+
+
{
+ return {
+ filter: route.params.filter,
+ }
+ },
+ },
+ },
{
path: '/board',
name: 'boards',
diff --git a/src/services/CardApi.js b/src/services/CardApi.js
index 114556d05..d52623dc2 100644
--- a/src/services/CardApi.js
+++ b/src/services/CardApi.js
@@ -195,4 +195,24 @@ export class CardApi {
})
}
+ findAllWithDue(data) {
+ return axios.get(this.url(`/dashboard/due`))
+ .then(
+ (response) => Promise.resolve(response.data),
+ (err) => Promise.reject(err)
+ )
+ .catch((err) => Promise.reject(err)
+ )
+ }
+
+ findMyAssignedCards(data) {
+ return axios.get(this.url(`/dashboard/assigned`))
+ .then(
+ (response) => Promise.resolve(response.data),
+ (err) => Promise.reject(err)
+ )
+ .catch((err) => Promise.reject(err)
+ )
+ }
+
}
diff --git a/src/store/dashboard.js b/src/store/dashboard.js
new file mode 100644
index 000000000..d5d2d030c
--- /dev/null
+++ b/src/store/dashboard.js
@@ -0,0 +1,63 @@
+/*
+ * @copyright Copyright (c) 2020 Jakob Röhrl
+ *
+ * @author Jakob Röhrl
+ *
+ * @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 Vue from 'vue'
+import Vuex from 'vuex'
+import { CardApi } from '../services/CardApi'
+Vue.use(Vuex)
+
+const apiClient = new CardApi()
+export default {
+ state: {
+ withDue: [],
+ assignedCards: [],
+ },
+ getters: {
+ withDueDashboard: state => {
+ return state.withDue
+ },
+ assignedCardsDashboard: state => {
+ return state.assignedCards
+ },
+ },
+ mutations: {
+ setWithDueDashboard(state, withDue) {
+ state.withDue = withDue
+ },
+ setAssignedCards(state, assignedCards) {
+ state.assignedCards = assignedCards
+ },
+
+ },
+ actions: {
+ async loadDashboards({ commit }) {
+ const withDue = await apiClient.findAllWithDue()
+ const withDueFlat = withDue.flat()
+ commit('setWithDueDashboard', withDueFlat)
+
+ const assignedCards = await apiClient.findMyAssignedCards()
+ const assignedCardsFlat = assignedCards.flat()
+ commit('setAssignedCards', assignedCardsFlat)
+
+ },
+ },
+}
diff --git a/src/store/main.js b/src/store/main.js
index 6189fd934..024051b2f 100644
--- a/src/store/main.js
+++ b/src/store/main.js
@@ -32,6 +32,7 @@ import card from './card'
import comment from './comment'
import trashbin from './trashbin'
import attachment from './attachment'
+import dashboard from './dashboard'
import debounce from 'lodash/debounce'
Vue.use(Vuex)
@@ -51,6 +52,7 @@ export default new Vuex.Store({
comment,
trashbin,
attachment,
+ dashboard,
},
strict: debug,
state: {