Compare commits

..

20 Commits

Author SHA1 Message Date
Julius Härtl
38921cade8 Merge pull request #3461 from nextcloud/backport/3459/stable1.4 2021-11-30 16:32:02 +01:00
Julius Härtl
8a3e679c33 Fix cursor generation if no results are found
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-11-30 12:22:12 +00:00
Jonas
d1b81e697f Merge pull request #3442 from nextcloud/backport/3428/stable1.4
[stable1.4] Allow to download an attachment without navigating to the files app
2021-11-22 19:11:06 +01:00
Julius Härtl
215fcf61bc Allow to download an attachment without navigating to the files app
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-11-22 17:40:47 +00:00
Julius Härtl
e418373503 Bump version to 1.4.7
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-11-10 20:42:09 +01:00
Julius Härtl
ca22b0ad2c Bump version to 1.4.6
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-11-10 16:12:11 +01:00
Julius Härtl
e4cbc694d4 Merge pull request #3408 from nextcloud/backport/3384/stable1.4
[stable1.4] Keep exceptions http response generic
2021-11-05 19:53:35 +01:00
Julius Härtl
f53e51fc4e Keep exceptions http response generic and return the request ID for further tracing
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-11-05 17:02:30 +00:00
Julius Härtl
dcbbb22dda Merge pull request #3393 from nextcloud/backport/3391/stable1.4 2021-10-27 14:16:23 +02:00
Paweł Kuffel
e85042e1b4 use displayname instead of uid for mentions
Signed-off-by: Paweł Kuffel <pawel@kuffel.io>
2021-10-27 11:59:25 +00:00
Julius Härtl
a720669354 Merge pull request #3379 from nextcloud/backport/3324/stable1.4 2021-10-22 20:24:34 +02:00
Julius Härtl
216b9445d3 Merge pull request #3385 from Artem4590/backport/3323/stable1.4 2021-10-22 20:24:17 +02:00
Artem Lavrukhin
b21faa8501 [stable1.4] Extend drag-and-drop zone in card sidebar
Signed-off-by: Artem Lavrukhin <lavryha4590@gmail.com>
2021-10-21 14:48:19 +03:00
Lera Dmitrieva
1bc28c68a5 Fix menu button position in card modal
Signed-off-by: Lera Dmitrieva <dmit.valerya@yandex.ru>
2021-10-12 10:25:31 +00:00
Julius Härtl
f78f8bfd7f Merge pull request #3367 from nextcloud/backport/3364/stable1.4 2021-10-06 11:40:26 +02:00
Julius Härtl
01bddf029e Fix optional parameter order
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-10-06 09:24:23 +00:00
Julius Härtl
bdead3cdd5 Merge pull request #3360 from nextcloud/enh/stable1.4-paginated-search-for-boards-and-cards 2021-10-05 08:24:10 +02:00
Julien Veyssier
88d164b411 use distinct pagination cursor for cards and boards, use cursor and limit in SearchService::searchBoards()
Signed-off-by: Julien Veyssier <eneiluj@posteo.net>
2021-10-04 17:30:45 +02:00
Julius Härtl
1638c3d350 Merge pull request #3359 from nextcloud/backport/stable1.4/2935 2021-10-04 16:25:10 +02:00
Joas Schilling
454d515192 Rich object string parameters for notifications
Signed-off-by: Joas Schilling <coding@schilljs.com>
2021-10-04 14:21:23 +02:00
15 changed files with 294 additions and 86 deletions

View File

@@ -1,6 +1,25 @@
# Changelog
All notable changes to this project will be documented in this file.
## 1.4.7
### Fixed
- Fix release asset build
## 1.4.6
### Fixed
- #3379 Fix menu button position in card modal
- #3360 Improve combined search @eneiluj
- #3367 Fix optional parameter order
- #3393 Use displayname instead of uid for mentions
- #3359 Rich object string parameters for notifications @juliushaertl
- #3385 Extend drag-and-drop zone in card sidebar @Artem4590
- #3408 Keep exceptions http response generic
## 1.4.5
### Fixed

View File

@@ -16,7 +16,7 @@
- 🚀 Get your project organized
</description>
<version>1.4.5</version>
<version>1.4.7</version>
<licence>agpl</licence>
<author>Julius Härtl</author>
<namespace>Deck</namespace>

View File

@@ -36,8 +36,10 @@ class CommentsApiController extends OCSController {
private $commentService;
public function __construct(
$appName, IRequest $request, $corsMethods = 'PUT, POST, GET, DELETE, PATCH', $corsAllowedHeaders = 'Authorization, Content-Type, Accept', $corsMaxAge = 1728000,
CommentService $commentService
string $appName,
IRequest $request,
CommentService $commentService,
string $corsMethods = 'PUT, POST, GET, DELETE, PATCH', string $corsAllowedHeaders = 'Authorization, Content-Type, Accept', int $corsMaxAge = 1728000
) {
parent::__construct($appName, $request, $corsMethods, $corsAllowedHeaders, $corsMaxAge);
$this->commentService = $commentService;

View File

@@ -32,6 +32,7 @@ use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\OCS\OCSException;
use OCP\AppFramework\OCSController;
use OCP\ILogger;
use OCP\IRequest;
use OCP\Util;
use OCP\IConfig;
@@ -41,6 +42,8 @@ class ExceptionMiddleware extends Middleware {
private $logger;
/** @var IConfig */
private $config;
/** @var IRequest */
private $request;
/**
* SharingMiddleware constructor.
@@ -48,9 +51,10 @@ class ExceptionMiddleware extends Middleware {
* @param ILogger $logger
* @param IConfig $config
*/
public function __construct(ILogger $logger, IConfig $config) {
public function __construct(ILogger $logger, IConfig $config, IRequest $request) {
$this->logger = $logger;
$this->config = $config;
$this->request = $request;
}
/**
@@ -67,43 +71,10 @@ class ExceptionMiddleware extends Middleware {
throw $exception;
}
if ($exception instanceof ConflictException) {
if ($this->config->getSystemValue('loglevel', Util::WARN) === Util::DEBUG) {
$this->logger->logException($exception);
}
return new JSONResponse([
'status' => $exception->getStatus(),
'message' => $exception->getMessage(),
'data' => $exception->getData(),
], $exception->getStatus());
}
if ($exception instanceof StatusException) {
if ($this->config->getSystemValue('loglevel', Util::WARN) === Util::DEBUG) {
$this->logger->logException($exception);
}
if ($controller instanceof OCSController) {
$exception = new OCSException($exception->getMessage(), $exception->getStatus(), $exception);
throw $exception;
}
return new JSONResponse([
'status' => $exception->getStatus(),
'message' => $exception->getMessage()
], $exception->getStatus());
}
if (strpos(get_class($controller), 'OCA\\Deck\\Controller\\') === 0) {
$response = [
'status' => 500,
'message' => $exception->getMessage()
];
$this->logger->logException($exception);
if ($this->config->getSystemValue('debug', true) === true) {
$response['exception'] = (array) $exception;
}
return new JSONResponse($response, 500);
}
$debugMode = $this->config->getSystemValue('debug', false);
$exceptionMessage = $debugMode !== true
? 'Internal server error: Please contact the server administrator if this error reappears multiple times, please include the request ID "' . $this->request->getId() . '" below in your report.'
: $exception->getMessage();
// uncatched DoesNotExistExceptions will be thrown when the main entity is not found
// we return a 403 so we don't leak information over existing entries
@@ -114,6 +85,43 @@ class ExceptionMiddleware extends Middleware {
'message' => 'Permission denied'
], 403);
}
if ($exception instanceof StatusException) {
if ($this->config->getSystemValue('loglevel', Util::WARN) === Util::DEBUG) {
$this->logger->logException($exception);
}
if ($exception instanceof ConflictException) {
return new JSONResponse([
'status' => $exception->getStatus(),
'message' => $exception->getMessage(),
'data' => $exception->getData(),
], $exception->getStatus());
}
if ($controller instanceof OCSController) {
$exception = new OCSException($exception->getMessage(), $exception->getStatus(), $exception);
throw $exception;
}
return new JSONResponse([
'status' => $exception->getStatus(),
'message' => $exception->getMessage(),
], $exception->getStatus());
}
if (strpos(get_class($controller), 'OCA\\Deck\\Controller\\') === 0) {
$response = [
'status' => 500,
'message' => $exceptionMessage,
'requestId' => $this->request->getId(),
];
$this->logger->logException($exception);
if ($debugMode === true) {
$response['exception'] = (array) $exception;
}
return new JSONResponse($response, 500);
}
throw $exception;
}

View File

@@ -25,6 +25,7 @@ namespace OCA\Deck\Notification;
use OCA\Deck\Db\BoardMapper;
use OCA\Deck\Db\CardMapper;
use OCA\Deck\Db\StackMapper;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\L10N\IFactory;
@@ -41,6 +42,8 @@ class Notifier implements INotifier {
protected $userManager;
/** @var CardMapper */
protected $cardMapper;
/** @var StackMapper */
protected $stackMapper;
/** @var BoardMapper */
protected $boardMapper;
@@ -49,12 +52,14 @@ class Notifier implements INotifier {
IURLGenerator $url,
IUserManager $userManager,
CardMapper $cardMapper,
StackMapper $stackMapper,
BoardMapper $boardMapper
) {
$this->l10nFactory = $l10nFactory;
$this->url = $url;
$this->userManager = $userManager;
$this->cardMapper = $cardMapper;
$this->stackMapper = $stackMapper;
$this->boardMapper = $boardMapper;
}
@@ -100,6 +105,11 @@ class Notifier implements INotifier {
if (!$boardId) {
throw new AlreadyProcessedException();
}
$card = $this->cardMapper->find($cardId);
$stackId = $card->getStackId();
$stack = $this->stackMapper->find($stackId);
$initiator = $this->userManager->get($params[2]);
if ($initiator !== null) {
$dn = $initiator->getDisplayName();
@@ -110,8 +120,22 @@ class Notifier implements INotifier {
(string) $l->t('The card "%s" on "%s" has been assigned to you by %s.', [$params[0], $params[1], $dn])
);
$notification->setRichSubject(
(string) $l->t('{user} has assigned the card "%s" on "%s" to you.', [$params[0], $params[1]]),
$l->t('{user} has assigned the card {deck-card} on {deck-board} to you.'),
[
'deck-card' => [
'type' => 'deck-card',
'id' => $cardId,
'name' => $params[0],
'boardname' => $params[1],
'stackname' => $stack->getTitle(),
'link' => $this->url->linkToRouteAbsolute('deck.page.index') . '#/board/' . $boardId . '/card/' . $cardId . '',
],
'deck-board' => [
'type' => 'deck-board',
'id' => $boardId,
'name' => $params[1],
'link' => $this->url->linkToRouteAbsolute('deck.page.index') . '#/board/' . $boardId,
],
'user' => [
'type' => 'user',
'id' => $params[2],
@@ -127,9 +151,33 @@ class Notifier implements INotifier {
if (!$boardId) {
throw new AlreadyProcessedException();
}
$card = $this->cardMapper->find($cardId);
$stackId = $card->getStackId();
$stack = $this->stackMapper->find($stackId);
$notification->setParsedSubject(
(string) $l->t('The card "%s" on "%s" has reached its due date.', $params)
);
$notification->setRichSubject(
$l->t('The card {deck-card} on {deck-board} has reached its due date.'),
[
'deck-card' => [
'type' => 'deck-card',
'id' => $cardId,
'name' => $params[0],
'boardname' => $params[1],
'stackname' => $stack->getTitle(),
'link' => $this->url->linkToRouteAbsolute('deck.page.index') . '#/board/' . $boardId . '/card/' . $cardId . '',
],
'deck-board' => [
'type' => 'deck-board',
'id' => $boardId,
'name' => $params[1],
'link' => $this->url->linkToRouteAbsolute('deck.page.index') . '#/board/' . $boardId,
],
]
);
$notification->setLink($this->url->linkToRouteAbsolute('deck.page.index') . '#/board/' . $boardId . '/card/' . $cardId . '');
break;
case 'card-comment-mentioned':
@@ -138,6 +186,11 @@ class Notifier implements INotifier {
if (!$boardId) {
throw new AlreadyProcessedException();
}
$card = $this->cardMapper->find($cardId);
$stackId = $card->getStackId();
$stack = $this->stackMapper->find($stackId);
$initiator = $this->userManager->get($params[2]);
if ($initiator !== null) {
$dn = $initiator->getDisplayName();
@@ -148,8 +201,16 @@ class Notifier implements INotifier {
(string) $l->t('%s has mentioned you in a comment on "%s".', [$dn, $params[0]])
);
$notification->setRichSubject(
(string) $l->t('{user} has mentioned you in a comment on "%s".', [$params[0]]),
$l->t('{user} has mentioned you in a comment on {deck-card}.'),
[
'deck-card' => [
'type' => 'deck-card',
'id' => $cardId,
'name' => $params[0],
'boardname' => $params[1],
'stackname' => $stack->getTitle(),
'link' => $this->url->linkToRouteAbsolute('deck.page.index') . '#/board/' . $boardId . '/card/' . $cardId . '',
],
'user' => [
'type' => 'user',
'id' => $params[2],
@@ -177,8 +238,14 @@ class Notifier implements INotifier {
(string) $l->t('The board "%s" has been shared with you by %s.', [$params[0], $dn])
);
$notification->setRichSubject(
(string) $l->t('{user} has shared the board %s with you.', [$params[0]]),
$l->t('{user} has shared {deck-board} with you.'),
[
'deck-board' => [
'type' => 'deck-board',
'id' => $boardId,
'name' => $params[0],
'link' => $this->url->linkToRouteAbsolute('deck.page.index') . '#/board/' . $boardId,
],
'user' => [
'type' => 'user',
'id' => $params[1],

View File

@@ -63,29 +63,52 @@ class DeckProvider implements IProvider {
}
public function search(IUser $user, ISearchQuery $query): SearchResult {
$cursor = $query->getCursor() !== null ? (int)$query->getCursor() : null;
$boardResults = $this->searchService->searchBoards($query->getTerm(), $query->getLimit(), $cursor);
$cardResults = $this->searchService->searchCards($query->getTerm(), $query->getLimit(), $cursor);
$results = array_merge(
array_map(function (Board $board) {
return new BoardSearchResultEntry($board, $this->urlGenerator);
}, $boardResults),
array_map(function (Card $card) {
return new CardSearchResultEntry($card->getRelatedBoard(), $card->getRelatedStack(), $card, $this->urlGenerator);
}, $cardResults)
);
$cursor = $query->getCursor();
[$boardCursor, $cardCursor] = $this->parseCursor($cursor);
if (count($cardResults) < $query->getLimit()) {
$boardObjects = $this->searchService->searchBoards($query->getTerm(), $query->getLimit(), $boardCursor);
$boardResults = array_map(function (Board $board) {
return [
'object' => $board,
'entry' => new BoardSearchResultEntry($board, $this->urlGenerator)
];
}, $boardObjects);
$cardObjects = $this->searchService->searchCards($query->getTerm(), $query->getLimit(), $cardCursor);
$cardResults = array_map(function (Card $card) {
return [
'object' => $card,
'entry' => new CardSearchResultEntry($card->getRelatedBoard(), $card->getRelatedStack(), $card, $this->urlGenerator)
];
}, $cardObjects);
$results = array_merge($boardResults, $cardResults);
usort($results, function ($a, $b) {
$ta = $a['object']->getLastModified();
$tb = $b['object']->getLastModified();
return $ta === $tb
? 0
: ($ta > $tb ? -1 : 1);
});
$resultEntries = array_map(function (array $result) {
return $result['entry'];
}, $results);
// if both cards and boards results are less then the limit, we know we won't get more
if (count($resultEntries) < $query->getLimit()) {
return SearchResult::complete(
'Deck',
$results
$resultEntries
);
}
$newCursor = $this->getNewCursor($boardObjects, $cardObjects);
return SearchResult::paginated(
'Deck',
$results,
$cardResults[count($results) - 1]->getLastModified()
$resultEntries,
$newCursor
);
}
@@ -95,4 +118,27 @@ class DeckProvider implements IProvider {
}
return 10;
}
private function parseCursor(?string $cursor): array {
$boardCursor = null;
$cardCursor = null;
if ($cursor !== null) {
$splitCursor = explode('|', $cursor);
if (count($splitCursor) >= 2) {
$boardCursor = (int)$splitCursor[0] ?: null;
$cardCursor = (int)$splitCursor[1] ?: null;
}
}
return [$boardCursor, $cardCursor];
}
private function getNewCursor(array $boards, array $cards): string {
$boardTimestamps = array_map(function (Board $board) {
return $board->getLastModified();
}, $boards);
$cardTimestamps = array_map(function (Card $card) {
return $card->getLastModified();
}, $cards);
return (count($boardTimestamps) > 0 ? min($boardTimestamps) : '') . '|' . (count($cardTimestamps) > 0 ? min($cardTimestamps) : '');
}
}

View File

@@ -81,7 +81,7 @@ class SearchService {
return $board->getId();
}, $boards);
$matchedCards = $this->cardMapper->search($boardIds, $this->filterStringParser->parse($term), $limit, $cursor);
$self = $this;
return array_map(function (Card $card) use ($self) {
$self->cardService->enrich($card);
@@ -91,9 +91,24 @@ class SearchService {
public function searchBoards(string $term, ?int $limit, ?int $cursor): array {
$boards = $this->boardService->getUserBoards();
return array_filter($boards, static function (Board $board) use ($term) {
return mb_stripos(mb_strtolower($board->getTitle()), mb_strtolower($term)) > -1;
// get boards that have a lastmodified date which is lower than the cursor
// and which match the search term
$filteredBoards = array_filter($boards, static function (Board $board) use ($term, $cursor) {
return (
($cursor === null || $board->getLastModified() < $cursor)
&& mb_stripos(mb_strtolower($board->getTitle()), mb_strtolower($term)) > -1
);
});
// sort the boards, recently modified first
usort($filteredBoards, function ($boardA, $boardB) {
$ta = $boardA->getLastModified();
$tb = $boardB->getLastModified();
return $ta === $tb
? 0
: ($ta > $tb ? -1 : 1);
});
// limit the number of results
return array_slice($filteredBoards, 0, $limit);
}
public function searchComments(string $term, ?int $limit = null, ?int $cursor = null): array {

View File

@@ -23,6 +23,12 @@
namespace OCA\Deck;
/**
* User facing exception that can be thrown with an error being reported to the frontend
* or consumers of the API
*
* This exception is catched in the ExceptionMiddleware
*/
class StatusException extends \Exception {
public function __construct($message) {
parent::__construct($message);

View File

@@ -1,7 +1,7 @@
{
"name": "deck",
"description": "",
"version": "1.4.5",
"version": "1.4.7",
"authors": [
{
"name": "Julius Härtl",

View File

@@ -131,6 +131,12 @@ export default {
position: relative;
}
.attachments-drag-zone.drop-upload--sidebar {
display: flex;
flex-direction: column;
flex-basis: 100%;
}
.dragover {
position: absolute;
background: var(--color-primary-light);

View File

@@ -22,7 +22,7 @@
<template>
<AttachmentDragAndDrop :card-id="cardId" class="drop-upload--sidebar">
<div class="button-group" v-if="!isReadOnly">
<div v-if="!isReadOnly" class="button-group">
<button class="icon-upload" @click="uploadNewFile()">
{{ t('deck', 'Upload new files') }}
</button>
@@ -79,6 +79,12 @@
<ActionLink v-if="attachment.extendedData.fileid" icon="icon-folder" :href="internalLink(attachment)">
{{ t('deck', 'Show in Files') }}
</ActionLink>
<ActionLink v-if="attachment.extendedData.fileid"
icon="icon-download"
:href="downloadLink(attachment)"
download>
{{ t('deck', 'Download') }}
</ActionLink>
<ActionButton v-if="attachment.extendedData.fileid && !isReadOnly" icon="icon-delete" @click="unshareAttachment(attachment)">
{{ t('deck', 'Remove attachment') }}
</ActionButton>
@@ -101,7 +107,8 @@ import { Actions, ActionButton, ActionLink } from '@nextcloud/vue'
import AttachmentDragAndDrop from '../AttachmentDragAndDrop'
import relativeDate from '../../mixins/relativeDate'
import { formatFileSize } from '@nextcloud/files'
import { generateUrl, generateOcsUrl } from '@nextcloud/router'
import { getCurrentUser } from '@nextcloud/auth'
import { generateUrl, generateOcsUrl, generateRemoteUrl } from '@nextcloud/router'
import { mapState } from 'vuex'
import { loadState } from '@nextcloud/initial-state'
import attachmentUpload from '../../mixins/attachmentUpload'
@@ -174,6 +181,9 @@ export default {
internalLink() {
return (attachment) => generateUrl('/f/' + attachment.extendedData.fileid)
},
downloadLink() {
return (attachment) => generateRemoteUrl(`dav/files/${getCurrentUser().uid}/${attachment.extendedData.path}`)
},
formattedFileSize() {
return (filesize) => formatFileSize(filesize)
},

View File

@@ -185,6 +185,13 @@ export default {
<style lang="scss" scoped>
section.app-sidebar__tab--active {
min-height: auto;
display: flex;
flex-direction: column;
height: 100%;
}
// FIXME: Obivously we should at some point not randomly reuse the sidebar component
// since this is not oficially supported
.modal__card .app-sidebar {
@@ -202,7 +209,6 @@ export default {
.app-sidebar-header {
position: sticky;
top: 0;
padding-top: $modal-padding;
z-index: 100;
background-color: var(--color-main-background);
}
@@ -214,12 +220,6 @@ export default {
background-color: var(--color-main-background);
}
section.app-sidebar__tab--active {
min-height: auto;
display: flex;
flex-direction: column;
}
#emptycontent, .emptycontent {
margin-top: 88px;
}

View File

@@ -26,7 +26,7 @@
<At ref="at"
v-model="commentText"
:members="members"
name-key="uid"
name-key="displayname"
:tab-select="true">
<template v-slot:item="s">
<Avatar class="atwho-li--avatar" :user="s.item.uid" :size="24" />

View File

@@ -47,10 +47,12 @@ class ExceptionMiddlewareTest extends \Test\TestCase {
public function setUp(): void {
$this->logger = $this->createMock(ILogger::class);
$this->config = $this->createMock(IConfig::class);
$this->request = $this->createMock(IRequest::class);
$this->controller = $this->createMock(Controller::class);
$this->exceptionMiddleware = new ExceptionMiddleware(
$this->logger,
$this->config
$this->config,
$this->request
);
}
@@ -81,10 +83,11 @@ class ExceptionMiddlewareTest extends \Test\TestCase {
}
public function testAfterExceptionFail() {
$this->request->expects($this->any())->method('getId')->willReturn('abc123');
// BoardService $boardService, PermissionService $permissionService, $userId
$boardController = new BoardController('deck', $this->createMock(IRequest::class), $this->createMock(BoardService::class), $this->createMock(PermissionService::class), 'admin');
$result = $this->exceptionMiddleware->afterException($boardController, 'bar', new \Exception('failed hard'));
$this->assertEquals('failed hard', $result->getData()['message']);
$result = $this->exceptionMiddleware->afterException($boardController, 'bar', new \Exception('other exception message'));
$this->assertEquals('Internal server error: Please contact the server administrator if this error reappears multiple times, please include the request ID "abc123" below in your report.', $result->getData()['message']);
$this->assertEquals(500, $result->getData()['status']);
}
}

View File

@@ -25,29 +25,33 @@ namespace OCA\Deck\Notification;
use OCA\Deck\Db\BoardMapper;
use OCA\Deck\Db\CardMapper;
use OCA\Deck\Db\StackMapper;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\IUserManager;
use OCP\L10N\IFactory;
use OCP\Notification\INotification;
use PHPUnit\Framework\MockObject\MockObject;
class NotifierTest extends \Test\TestCase {
/** @var IFactory */
/** @var IFactory|MockObject */
protected $l10nFactory;
/** @var IURLGenerator */
/** @var IURLGenerator|MockObject */
protected $url;
/** @var IUserManager */
/** @var IUserManager|MockObject */
protected $userManager;
/** @var CardMapper */
/** @var CardMapper|MockObject */
protected $cardMapper;
/** @var StackMapper|MockObject */
protected $stackMapper;
/** @var BoardMapper */
protected $boardMapper;
/** @var IL10N|MockObject */
protected $l10n;
/** @var Notifier */
protected $notifier;
/** @var IL10N */
protected $l10n;
public function setUp(): void {
parent::setUp();
@@ -55,12 +59,14 @@ class NotifierTest extends \Test\TestCase {
$this->url = $this->createMock(IURLGenerator::class);
$this->userManager = $this->createMock(IUserManager::class);
$this->cardMapper = $this->createMock(CardMapper::class);
$this->stackMapper = $this->createMock(StackMapper::class);
$this->boardMapper = $this->createMock(BoardMapper::class);
$this->notifier = new Notifier(
$this->l10nFactory,
$this->url,
$this->userManager,
$this->cardMapper,
$this->stackMapper,
$this->boardMapper
);
$this->l10n = \OC::$server->getL10N('deck');
@@ -149,7 +155,7 @@ class NotifierTest extends \Test\TestCase {
->with($expectedMessage);
$notification->expects($this->once())
->method('setRichSubject')
->with('{user} has mentioned you in a comment on "Card title".');
->with('{user} has mentioned you in a comment on {deck-card}.');
$this->url->expects($this->once())
@@ -218,11 +224,25 @@ class NotifierTest extends \Test\TestCase {
->with($expectedMessage);
$notification->expects($this->once())
->method('setRichSubject')
->with('{user} has assigned the card "Card title" on "Board title" to you.', [
->with('{user} has assigned the card {deck-card} on {deck-board} to you.', [
'user' => [
'type' => 'user',
'id' => 'otheruser',
'name' => $dn,
],
'deck-card' => [
'type' => 'deck-card',
'id' => '123',
'name' => 'Card title',
'boardname' => 'Board title',
'stackname' => null,
'link' => '#/board/123/card/123',
],
'deck-board' => [
'type' => 'deck-board',
'id' => 123,
'name' => 'Board title',
'link' => '#/board/123',
]
]);
@@ -288,11 +308,17 @@ class NotifierTest extends \Test\TestCase {
->with($expectedMessage);
$notification->expects($this->once())
->method('setRichSubject')
->with('{user} has shared the board Board title with you.', [
->with('{user} has shared {deck-board} with you.', [
'user' => [
'type' => 'user',
'id' => 'otheruser',
'name' => $dn,
],
'deck-board' => [
'type' => 'deck-board',
'id' => 123,
'name' => 'Board title',
'link' => '#/board/123',
]
]);