Compare commits

..

7 Commits

Author SHA1 Message Date
Jakob
998e5281d2 Merge branch 'master' into removeCommentCheck 2020-09-28 14:10:56 +02:00
Jakob Röhrl
7135f8162d remove comment check
Signed-off-by: Jakob Röhrl <jakob.roehrl@web.de>
2020-09-23 14:57:32 +02:00
Jakob Röhrl
ff14afbe11 Merge branch 'master' of https://github.com/nextcloud/deck 2020-09-23 14:09:33 +02:00
Jakob Röhrl
b9d2e9c791 Revert "consistent naming"
This reverts commit a1bd13d960.
2020-09-23 13:21:23 +02:00
Jakob Röhrl
a1bd13d960 consistent naming
Signed-off-by: Jakob Röhrl <jakob.roehrl@web.de>
2020-09-23 13:20:20 +02:00
Jakob Röhrl
6d5ddd4e2c Revert "consistent naming"
This reverts commit a34e8feb33.
2020-09-23 12:49:52 +02:00
Jakob Röhrl
a34e8feb33 consistent naming
Signed-off-by: Jakob Röhrl <jakob.roehrl@web.de>
2020-09-23 12:44:30 +02:00
33 changed files with 868 additions and 1133 deletions

View File

@@ -5,7 +5,7 @@ steps:
image: nextcloudci/php7.3:latest
environment:
APP_NAME: deck
CORE_BRANCH: stable20
CORE_BRANCH: master
DB: sqlite
commands:
# Pre-setup steps
@@ -20,7 +20,7 @@ steps:
image: nextcloudci/php7.2:latest
environment:
APP_NAME: deck
CORE_BRANCH: stable20
CORE_BRANCH: master
DB: sqlite
commands:
- composer install
@@ -29,7 +29,7 @@ steps:
image: nextcloudci/php7.3:php7.3-2
environment:
APP_NAME: deck
CORE_BRANCH: stable20
CORE_BRANCH: master
DB: sqlite
commands:
- composer install
@@ -38,7 +38,7 @@ steps:
image: nextcloudci/php7.4:latest
environment:
APP_NAME: deck
CORE_BRANCH: stable20
CORE_BRANCH: master
DB: sqlite
commands:
- composer install
@@ -58,7 +58,7 @@ steps:
image: nextcloudci/php7.3:latest
environment:
APP_NAME: deck
CORE_BRANCH: stable20
CORE_BRANCH: master
DB: sqlite
commands:
# Pre-setup steps
@@ -84,7 +84,7 @@ steps:
image: nextcloudci/php7.4:latest
environment:
APP_NAME: deck
CORE_BRANCH: stable20
CORE_BRANCH: master
DB: sqlite
commands:
# Pre-setup steps
@@ -111,7 +111,7 @@ steps:
image: nextcloudci/php7.3:latest
environment:
APP_NAME: deck
CORE_BRANCH: stable20
CORE_BRANCH: master
DB: sqlite
commands:
# Pre-setup steps

View File

@@ -2,13 +2,9 @@ language: php
services:
- mysql
php:
- 7.2
- 7.3
- 7.4
env:
- CORE_BRANCH=stable20 DB=mysql
- CORE_BRANCH=stable19 DB=mysql
- CORE_BRANCH=stable18 DB=mysql
- CORE_BRANCH=master DB=mysql
matrix:
include:
@@ -21,13 +17,10 @@ before_install:
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
- bash ./before_install.sh deck $CORE_BRANCH $DB
- cd ../server
- ./occ app:enable deck
before_script:
- cd apps/deck
- composer install --no-dev
- cd ../../
- ./occ app:enable deck
- cd apps/deck
script:
- composer install

View File

@@ -1,67 +1,31 @@
# Changelog
All notable changes to this project will be documented in this file.
## 1.1.2 - 2020-10-14
## 1.1.0 - unreleased
### Bugfixes
### Merged PRs
* [#2436](https://github.com/nextcloud/deck/pull/2436) Move most destructive actions in drop down menus to the bottom
* [#2438](https://github.com/nextcloud/deck/pull/2438) Fix scrollable titles with Dyslexia font
* [#2439](https://github.com/nextcloud/deck/pull/2439) Only remove card padding for editable cards
* [#2442](https://github.com/nextcloud/deck/pull/2442) Move navigation toggle handling to @nextcloud/vue native one
* [#2443](https://github.com/nextcloud/deck/pull/2443) Do not open the dialog automatically upon card creation, only upon click
## 1.1.1 - 2020-10-13
### Bugfixes
* [#2364](https://github.com/nextcloud/deck/pull/2364) Use uid instead of displayname for sharee results
* [#2365](https://github.com/nextcloud/deck/pull/2365) Comments do not depend on the comments app (@jakobroehrl)
* [#2395](https://github.com/nextcloud/deck/pull/2395) Fix failure if full text search app was enabled
* [#2396](https://github.com/nextcloud/deck/pull/2396) Also exclude deleted items from calendar boards
* [#2425](https://github.com/nextcloud/deck/pull/2425) Fix filter popover styling (@Flamenco)
* [#2432](https://github.com/nextcloud/deck/pull/2432) Properly handle multiple shares in a row and refactor sharee loading
## 1.1.0 - 2020-10-03
### Features
* [#2115](https://github.com/nextcloud/deck/pull/2115) Dashboard widget for Nextcloud 20
* [#1545](https://github.com/nextcloud/deck/pull/1545) Show cards in calendar/tasks app and make them available though CalDAV
* [#2200](https://github.com/nextcloud/deck/pull/2200) Unified search implementation for Nextcloud 20
* [#1934](https://github.com/nextcloud/deck/pull/1934) Upcoming cards overview @jakobroehrl
* [#2047](https://github.com/nextcloud/deck/pull/2047) Show card details in modal @jakobroehrl
* [#1853](https://github.com/nextcloud/deck/pull/1853) Archive all cards from stack @jakobroehrl
* [#1865](https://github.com/nextcloud/deck/pull/1865) Add stack button on empty board @jakobroehrl
* [#1926](https://github.com/nextcloud/deck/pull/1926) New filter: unassigned cards @jakobroehrl
### Bugfixes
* [#1934](https://github.com/nextcloud/deck/pull/1934) Card dashboard @jakobroehrl
* [#2035](https://github.com/nextcloud/deck/pull/2035) Attach files in description @jakobroehrl
* [#2047](https://github.com/nextcloud/deck/pull/2047) Show card details in modal @jakobroehrl
* [#2115](https://github.com/nextcloud/deck/pull/2115) Dashboard panel @juliushaertl
* [#2123](https://github.com/nextcloud/deck/pull/2123) Fix control tooltip @jakobroehrl
* [#2144](https://github.com/nextcloud/deck/pull/2144) Fix nextcloud if install with dev dependencies @matchish
* [#2158](https://github.com/nextcloud/deck/pull/2158) Fix description in dark mode
* [#2157](https://github.com/nextcloud/deck/pull/2157) Build/webpack shared config @juliushaertl
* [#2158](https://github.com/nextcloud/deck/pull/2158) Fix description in dark mode @juliushaertl
* [#2169](https://github.com/nextcloud/deck/pull/2169) Bump webpack-merge from 5.0.9 to 5.1.0 @dependabot
* [#2170](https://github.com/nextcloud/deck/pull/2170) Add lastModified date to boards API documentation @stefan-niedermann
* [#2188](https://github.com/nextcloud/deck/pull/2188) CardBadges: Count checkboxes started with "+ [ ]" @joreiff
* [#2206](https://github.com/nextcloud/deck/pull/2206) Fix read-only sidebar (fixes #2033)
* [#2208](https://github.com/nextcloud/deck/pull/2208) Fix design, dark mode and keyboard navigation of the board list
* [#2200](https://github.com/nextcloud/deck/pull/2200) Unified search implementation @juliushaertl
* [#2206](https://github.com/nextcloud/deck/pull/2206) Fix read-only sidebar (fixes #2033) @juliushaertl
* [#2208](https://github.com/nextcloud/deck/pull/2208) Fix design, dark mode and keyboard navigation of the board list @juliushaertl
* [#2210](https://github.com/nextcloud/deck/pull/2210) Fix an incorrect/misleading message in lib/Service/BoardService.php @jordanbancino
* [#2243](https://github.com/nextcloud/deck/pull/2243) Various smaller styling fixes
* [#2244](https://github.com/nextcloud/deck/pull/2244) Toggle filter on clicking card labels
* [#2117](https://github.com/nextcloud/deck/pull/2117) Activity fixes
* [#2255](https://github.com/nextcloud/deck/pull/2255) Use unified search events to apply on board filtering
* [#2271](https://github.com/nextcloud/deck/pull/2271) Sort tags in filter @jakobroehrl
* [#2318](https://github.com/nextcloud/deck/pull/2318) Card title: prevent space and no text @jakobroehrl
* [#2319](https://github.com/nextcloud/deck/pull/2319) Move style loading to BeforeTemplateRenderedEvent
* [#2320](https://github.com/nextcloud/deck/pull/2320) Consistent naming @jakobroehrl
* [#2252](https://github.com/nextcloud/deck/pull/2252) Fix double slash in the deck activity links @baraksoa
* [#2270](https://github.com/nextcloud/deck/pull/2270) Fix empty content view to align with other widgets
* [#2275](https://github.com/nextcloud/deck/pull/2275) Wait for services to be registered before performing further setup that requires services
* [#2278](https://github.com/nextcloud/deck/pull/2278) Fix wrong SQL queries @Chartman123
* [#2279](https://github.com/nextcloud/deck/pull/2279) L10n:add translation to card placeholder @mjanssens
* [#2282](https://github.com/nextcloud/deck/pull/2282) Duedate picker localization
* [#2283](https://github.com/nextcloud/deck/pull/2283) Do not handle exceptions from page controller in the ExceptionMiddleware
* [#2298](https://github.com/nextcloud/deck/pull/2298) Use absolute URLs for the search @nickvergessen
* [#2211](https://github.com/nextcloud/deck/pull/2211) Update incorrect field in API documentation (docs/API.md) @jordanbancino
* [#2243](https://github.com/nextcloud/deck/pull/2243) Various smaller styling fixes @juliushaertl
* [#2244](https://github.com/nextcloud/deck/pull/2244) Toggle filter on clicking card labels @juliushaertl
## 1.0.5 - 2020-07-15

View File

@@ -17,7 +17,7 @@
- 🚀 Get your project organized
</description>
<version>1.1.2</version>
<version>1.1.0-beta2</version>
<licence>agpl</licence>
<author>Julius Härtl</author>
<namespace>Deck</namespace>
@@ -36,7 +36,7 @@
<database min-version="9.4">pgsql</database>
<database>sqlite</database>
<database min-version="5.5">mysql</database>
<nextcloud min-version="18" max-version="20" />
<nextcloud min-version="18" max-version="21" />
</dependencies>
<background-jobs>
<job>OCA\Deck\Cron\DeleteCron</job>

View File

@@ -191,7 +191,6 @@ OC.L10N.register(
"Select Date" : "Seleccione la fecha",
"Modified" : "Modificado",
"Created" : "Creado",
"The title cannot be empty." : "El título no puede estar vacío.",
"No comments yet. Begin the discussion!" : "Todavía no hay comentarios. ¡Comienza la discusión!",
"Save" : "Guardar",
"The comment cannot be empty." : "El comentario no puede estar vacío.",
@@ -217,7 +216,6 @@ OC.L10N.register(
"Show boards in calendar/tasks" : "Mostrar tableros en calendario/tareas",
"Limit deck usage of groups" : "Limitar el uso de Deck a grupos",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Limitar Deck impedirá que usuarios que no formen parte de esos grupos creen sus propios tableros. Los usuarios todavía podrán trabajar en tableros que hayan sido compartidos con ellos.",
"Board name" : "Nombre del tablero",
"Edit board" : "Editar tablero",
"Clone board " : "Clonar tablero",
"Unarchive board " : "Desarchivar tablero",

View File

@@ -189,7 +189,6 @@
"Select Date" : "Seleccione la fecha",
"Modified" : "Modificado",
"Created" : "Creado",
"The title cannot be empty." : "El título no puede estar vacío.",
"No comments yet. Begin the discussion!" : "Todavía no hay comentarios. ¡Comienza la discusión!",
"Save" : "Guardar",
"The comment cannot be empty." : "El comentario no puede estar vacío.",
@@ -215,7 +214,6 @@
"Show boards in calendar/tasks" : "Mostrar tableros en calendario/tareas",
"Limit deck usage of groups" : "Limitar el uso de Deck a grupos",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Limitar Deck impedirá que usuarios que no formen parte de esos grupos creen sus propios tableros. Los usuarios todavía podrán trabajar en tableros que hayan sido compartidos con ellos.",
"Board name" : "Nombre del tablero",
"Edit board" : "Editar tablero",
"Clone board " : "Clonar tablero",
"Unarchive board " : "Desarchivar tablero",

View File

@@ -121,7 +121,6 @@ OC.L10N.register(
"Apply filter" : "Primijeni filtar",
"Filter by tag" : "Filtriraj prema oznaci",
"Filter by assigned user" : "Filtriraj prema dodijeljenom korisniku",
"Unassigned" : "Nedodijeljeno",
"Filter by due date" : "Filtriraj prema datumu dospijeća",
"Overdue" : "Kasni",
"Next 24 hours" : "Sljedeća 24 sata",
@@ -134,8 +133,6 @@ OC.L10N.register(
"Toggle compact mode" : "Prebaci u kompaktni način rada",
"Details" : "Pojedinosti",
"Loading board" : "Učitavanje ploče",
"No lists available" : "Nema dostupnih popisa",
"Create a new list to add cards to this board" : "Stvorite novi popis kako biste dodali kartice na ovu ploču",
"Board not found" : "Ploča nije pronađena",
"Sharing" : "Dijeljenje",
"Tags" : "Oznake",

View File

@@ -119,7 +119,6 @@
"Apply filter" : "Primijeni filtar",
"Filter by tag" : "Filtriraj prema oznaci",
"Filter by assigned user" : "Filtriraj prema dodijeljenom korisniku",
"Unassigned" : "Nedodijeljeno",
"Filter by due date" : "Filtriraj prema datumu dospijeća",
"Overdue" : "Kasni",
"Next 24 hours" : "Sljedeća 24 sata",
@@ -132,8 +131,6 @@
"Toggle compact mode" : "Prebaci u kompaktni način rada",
"Details" : "Pojedinosti",
"Loading board" : "Učitavanje ploče",
"No lists available" : "Nema dostupnih popisa",
"Create a new list to add cards to this board" : "Stvorite novi popis kako biste dodali kartice na ovu ploču",
"Board not found" : "Ploča nije pronađena",
"Sharing" : "Dijeljenje",
"Tags" : "Oznake",

View File

@@ -191,7 +191,6 @@ OC.L10N.register(
"Select Date" : "Tarih Seçin",
"Modified" : "Değiştirilme",
"Created" : "Oluşturulma",
"The title cannot be empty." : "Başlık boş olamaz.",
"No comments yet. Begin the discussion!" : "Henüz bir yorum yapılmamış. Tartışmayı başlatın!",
"Save" : "Kaydet",
"The comment cannot be empty." : "Yorum boş olamaz.",
@@ -217,7 +216,6 @@ OC.L10N.register(
"Show boards in calendar/tasks" : "Takvimler ve görevlerde panolar görüntülensin",
"Limit deck usage of groups" : "Tahtayı şu gruplar kullanabilsin",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Tahta kullanımı gruplar ile sınırlandığında belirtilen grupların üyesi olmayan kişiler kendi tahtalarını oluşturamaz. Bu kullanıcılar ancak kendileri ile paylaşılan tahtalar üzerinde çalışabilir.",
"Board name" : "Pano adı",
"Edit board" : "Panoyu sil",
"Clone board " : "Panoyu kopyala",
"Unarchive board " : "Panoyu arşivden çıkar",

View File

@@ -189,7 +189,6 @@
"Select Date" : "Tarih Seçin",
"Modified" : "Değiştirilme",
"Created" : "Oluşturulma",
"The title cannot be empty." : "Başlık boş olamaz.",
"No comments yet. Begin the discussion!" : "Henüz bir yorum yapılmamış. Tartışmayı başlatın!",
"Save" : "Kaydet",
"The comment cannot be empty." : "Yorum boş olamaz.",
@@ -215,7 +214,6 @@
"Show boards in calendar/tasks" : "Takvimler ve görevlerde panolar görüntülensin",
"Limit deck usage of groups" : "Tahtayı şu gruplar kullanabilsin",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Tahta kullanımı gruplar ile sınırlandığında belirtilen grupların üyesi olmayan kişiler kendi tahtalarını oluşturamaz. Bu kullanıcılar ancak kendileri ile paylaşılan tahtalar üzerinde çalışabilir.",
"Board name" : "Pano adı",
"Edit board" : "Panoyu sil",
"Clone board " : "Panoyu kopyala",
"Unarchive board " : "Panoyu arşivden çıkar",

View File

@@ -240,25 +240,9 @@ class ActivityManager {
}
public function triggerEvent($objectType, $entity, $subject, $additionalParams = [], $author = null) {
if ($author === null) {
$author = $this->userId;
}
try {
$event = $this->createEvent($objectType, $entity, $subject, $additionalParams, $author);
if ($event !== null) {
$json = json_encode($event->getSubjectParameters());
if (mb_strlen($json) > 4000) {
$params = json_decode(json_encode($event->getSubjectParameters()), true);
$newContent = $params['after'];
unset($params['before'], $params['after'], $params['card']['description']);
$params['after'] = mb_substr($newContent, 0, 2000);
if (mb_strlen($newContent) > 2000) {
$params['after'] .= '...';
}
$event->setSubject($event->getSubject(), $params);
}
$this->sendToUsers($event);
}
} catch (\Exception $e) {

View File

@@ -96,7 +96,6 @@ class DeckProvider implements IProvider {
unset($subjectParams['author']);
}
$user = $this->userManager->get($author);
$params = [];
if ($user !== null) {
$params = [
'user' => [
@@ -329,8 +328,8 @@ class DeckProvider implements IProvider {
if (array_key_exists('diff', $subjectParams) && $subjectParams['diff']) {
$diff = new Diff();
// Don't add diff as message since we are limited to 255 chars here
$event->setParsedMessage($subjectParams['after']);
//$event->setParsedMessage('<pre class="visualdiff">' . $diff->render($subjectParams['before'], $subjectParams['after']) . '</pre>');
//$event->setMessage($subjectParams['after']);
$event->setParsedMessage('<pre class="visualdiff">' . $diff->render($subjectParams['before'], $subjectParams['after']) . '</pre>');
return $params;
}
if (array_key_exists('before', $subjectParams)) {

View File

@@ -59,7 +59,7 @@ class DeckCalendarBackend {
}
public function getBoards(): array {
return $this->boardService->findAll(-1, null, false);
return $this->boardService->findAll();
}
public function getBoard(int $id): Board {

View File

@@ -93,18 +93,10 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
* @param null $offset
* @return array
*/
public function findAllByUser($userId, $limit = null, $offset = null, $since = -1, $includeArchived = true) {
// FIXME: One moving to QBMapper we should allow filtering the boards probably by method chaining for additional where clauses
$sql = 'SELECT id, title, owner, color, archived, deleted_at, 0 as shared, last_modified FROM `*PREFIX*deck_boards` WHERE owner = ? AND last_modified > ?';
if (!$includeArchived) {
$sql .= ' AND NOT archived AND deleted_at = 0';
}
$sql .= ' UNION ' .
public function findAllByUser($userId, $limit = null, $offset = null, $since = -1) {
$sql = 'SELECT id, title, owner, color, archived, deleted_at, 0 as shared, last_modified FROM `*PREFIX*deck_boards` WHERE owner = ? AND last_modified > ? UNION ' .
'SELECT boards.id, title, owner, color, archived, deleted_at, 1 as shared, last_modified FROM `*PREFIX*deck_boards` as boards ' .
'JOIN `*PREFIX*deck_board_acl` as acl ON boards.id=acl.board_id WHERE acl.participant=? AND acl.type=? AND boards.owner != ? AND last_modified > ?';
if (!$includeArchived) {
$sql .= ' AND NOT archived AND deleted_at = 0';
}
$entries = $this->findEntities($sql, [$userId, $since, $userId, Acl::PERMISSION_TYPE_USER, $userId, $since], $limit, $offset);
/* @var Board $entry */
foreach ($entries as $entry) {
@@ -128,7 +120,7 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
* @param null $offset
* @return array
*/
public function findAllByGroups($userId, $groups, $limit = null, $offset = null, $since = -1,$includeArchived = true) {
public function findAllByGroups($userId, $groups, $limit = null, $offset = null) {
if (count($groups) <= 0) {
return [];
}
@@ -140,10 +132,7 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
$sql .= ' OR ';
}
}
$sql .= ')';
if (!$includeArchived) {
$sql .= ' AND NOT archived AND deleted_at = 0';
}
$sql .= ');';
$entries = $this->findEntities($sql, array_merge([$userId, Acl::PERMISSION_TYPE_GROUP], $groups), $limit, $offset);
/* @var Board $entry */
foreach ($entries as $entry) {
@@ -153,7 +142,7 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
return $entries;
}
public function findAllByCircles($userId, $limit = null, $offset = null, $since = -1,$includeArchived = true) {
public function findAllByCircles($userId, $limit = null, $offset = null) {
if (!$this->circlesEnabled) {
return [];
}
@@ -172,10 +161,7 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
$sql .= ' OR ';
}
}
$sql .= ')';
if (!$includeArchived) {
$sql .= ' AND NOT archived AND deleted_at = 0';
}
$sql .= ');';
$entries = $this->findEntities($sql, array_merge([$userId, Acl::PERMISSION_TYPE_CIRCLE], $circles), $limit, $offset);
/* @var Board $entry */
foreach ($entries as $entry) {

View File

@@ -42,7 +42,7 @@ class FTSEvent extends Event {
}
public function getArgument($key) {
if (isset($this->arguments[$key])) {
if ($this->hasArgument($key)) {
return $this->arguments[$key];
}

View File

@@ -107,11 +107,11 @@ class BoardService {
$this->userId = $userId;
}
public function getUserBoards(int $since = -1, $includeArchived = true): array {
public function getUserBoards(int $since = -1): array {
$userInfo = $this->getBoardPrerequisites();
$userBoards = $this->boardMapper->findAllByUser($userInfo['user'], null, null, $since, $includeArchived);
$groupBoards = $this->boardMapper->findAllByGroups($userInfo['user'], $userInfo['groups'],null, null, $since, $includeArchived);
$circleBoards = $this->boardMapper->findAllByCircles($userInfo['user'], null, null, $since, $includeArchived);
$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);
$mergedBoards = array_merge($userBoards, $groupBoards, $circleBoards);
$result = [];
/** @var Board $item */
@@ -125,11 +125,11 @@ class BoardService {
/**
* @return array
*/
public function findAll($since = -1, $details = null, $includeArchived = true) {
public function findAll($since = -1, $details = null) {
if ($this->boardsCache) {
return $this->boardsCache;
}
$complete = $this->getUserBoards($since, $includeArchived);
$complete = $this->getUserBoards($since);
$result = [];
/** @var Board $item */
foreach ($complete as &$item) {

View File

@@ -415,11 +415,8 @@ class CardService {
if ($card->getArchived()) {
throw new StatusException('Operation not allowed. This card is archived.');
}
$changes = new ChangeSet($card);
$card->setStackId($stackId);
$this->cardMapper->update($card);
$changes->setAfter($card);
$this->activityManager->triggerUpdateEvents(ActivityManager::DECK_OBJECT_CARD, $changes, ActivityManager::SUBJECT_CARD_UPDATE);
$cards = $this->cardMapper->findAll($stackId);
$result = [];

1579
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -33,30 +33,29 @@
"@juliushaertl/vue-richtext": "^0.3.3",
"@nextcloud/auth": "^1.3.0",
"@nextcloud/axios": "^1.4.0",
"@nextcloud/dialogs": "^3.0.0",
"@nextcloud/event-bus": "^1.2.0",
"@nextcloud/dialogs": "^2.0.1",
"@nextcloud/files": "^1.1.0",
"@nextcloud/initial-state": "^1.2.0",
"@nextcloud/initial-state": "^1.1.2",
"@nextcloud/l10n": "^1.4.1",
"@nextcloud/moment": "^1.1.1",
"@nextcloud/router": "^1.2.0",
"@nextcloud/vue": "^2.7.0",
"@nextcloud/vue": "^2.6.5",
"@nextcloud/vue-dashboard": "^1.0.1",
"blueimp-md5": "^2.18.0",
"dompurify": "^2.1.1",
"lodash": "^4.17.20",
"markdown-it": "^11.0.1",
"markdown-it-task-lists": "^2.1.1",
"moment": "^2.29.1",
"moment": "^2.29.0",
"nextcloud-vue-collections": "^0.8.1",
"p-queue": "^6.6.1",
"url-search-params-polyfill": "^8.1.0",
"vue": "^2.6.12",
"vue-at": "^2.5.0-beta.2",
"vue-click-outside": "^1.1.0",
"vue-easymde": "^1.3.0",
"vue-easymde": "^1.2.2",
"vue-infinite-loading": "^2.4.5",
"vue-router": "^3.4.6",
"vue-router": "^3.4.4",
"vue-smooth-dnd": "^0.8.1",
"vuex": "^3.5.1",
"vuex-router-sync": "^5.0.0"
@@ -73,44 +72,44 @@
"@babel/preset-env": "^7.11.5",
"@nextcloud/browserslist-config": "^1.0.0",
"@nextcloud/eslint-config": "^2.1.0",
"@nextcloud/eslint-plugin": "^1.5.0",
"@nextcloud/eslint-plugin": "^1.4.0",
"@nextcloud/webpack-vue-config": "^1.4.1",
"@vue/test-utils": "^1.1.0",
"acorn": "^8.0.4",
"acorn": "^8.0.1",
"babel-eslint": "^10.1.0",
"babel-jest": "^26.5.2",
"babel-jest": "^26.3.0",
"babel-loader": "^8.1.0",
"css-loader": "^4.3.0",
"eslint": "^6.8.0",
"eslint-config-standard": "^12.0.0",
"eslint-friendly-formatter": "^4.0.1",
"eslint-loader": "^3.0.4",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"eslint-plugin-vue": "^6.2.2",
"file-loader": "^6.1.1",
"jest": "^26.5.2",
"file-loader": "^6.1.0",
"jest": "^26.4.2",
"jest-serializer-vue": "^2.0.2",
"minimist": "^1.2.5",
"node-sass": "^4.14.1",
"raw-loader": "^4.0.2",
"sass-loader": "^10.0.3",
"style-loader": "^1.3.0",
"raw-loader": "^4.0.1",
"sass-loader": "^10.0.2",
"style-loader": "^1.2.1",
"stylelint": "^13.7.2",
"stylelint-config-recommended": "^3.0.0",
"stylelint-config-recommended-scss": "^4.2.0",
"stylelint-scss": "^3.18.0",
"stylelint-webpack-plugin": "^2.1.0",
"url-loader": "^4.1.1",
"url-loader": "^4.1.0",
"vue-jest": "^3.0.7",
"vue-loader": "^15.9.3",
"vue-template-compiler": "^2.6.12",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0",
"webpack-merge": "^5.2.0"
"webpack-merge": "^5.1.4"
},
"jest": {
"moduleFileExtensions": [

View File

@@ -21,11 +21,11 @@
-->
<template>
<Content id="content" app-name="deck" :class="{ 'nav-hidden': !navShown, 'sidebar-hidden': !sidebarRouterView }">
<AppNavigation />
<AppContent>
<div id="content" :class="{ 'nav-hidden': !navShown, 'sidebar-hidden': !sidebarRouterView }">
<AppNavigation v-show="navShown" />
<div id="app-content">
<router-view />
</AppContent>
</div>
<Modal v-if="cardDetailsInModal && $route.params.cardId" :title="t('deck', 'Card details')" @close="hideModal()">
<div class="modal__content modal__card">
@@ -34,16 +34,15 @@
</Modal>
<router-view v-show="!cardDetailsInModal || !$route.params.cardId" name="sidebar" />
</Content>
</div>
</template>
<script>
import { mapState } from 'vuex'
import AppNavigation from './components/navigation/AppNavigation'
import { Modal, Content, AppContent } from '@nextcloud/vue'
import { Modal } from '@nextcloud/vue'
import { BoardApi } from './services/BoardApi'
import { emit, subscribe } from '@nextcloud/event-bus'
const boardApi = new BoardApi()
@@ -52,8 +51,6 @@ export default {
components: {
AppNavigation,
Modal,
Content,
AppContent,
},
data() {
return {
@@ -94,15 +91,6 @@ export default {
this.$store.dispatch('loadBoards')
this.$store.dispatch('loadSharees')
},
mounted() {
// Set navigation to initial state and update in case it gets toggled
emit('toggle-navigation', { open: this.navShown, _initial: true })
this.$nextTick(() => {
subscribe('navigation-toggled', (navState) => {
this.$store.dispatch('toggleNav', navState.open)
})
})
},
methods: {
hideModal() {
this.$router.push({ name: 'board' })
@@ -143,6 +131,13 @@ export default {
}
}
}
</style>
<style lang="scss">
.multiselect {
width: 100%;
}
.modal__card {
min-width: 320px;
@@ -152,11 +147,3 @@ export default {
height: 80vh;
}
</style>
<style lang="scss">
.multiselect {
width: 100%;
}
</style>

View File

@@ -22,17 +22,17 @@
<template>
<div class="controls">
<div v-if="overviewName" class="board-title">
<div class="board-bullet icon-calendar-dark" />
<h2>{{ overviewName }}</h2>
</div>
<div v-else-if="board" class="board-title">
<div id="app-navigation-toggle-custom" class="icon-menu" @click="toggleNav" />
<div v-if="board" class="board-title">
<div :style="{backgroundColor: '#' + board.color}" class="board-bullet" />
<h2>{{ board.title }}</h2>
<h2><a href="#">{{ board.title }}</a></h2>
<p v-if="showArchived">
({{ t('deck', 'Archived cards') }})
</p>
</div>
<div v-if="overviewName" class="board-title">
<h2><a href="#">{{ overviewName }}</a></h2>
</div>
<div v-if="board" class="board-actions">
<div v-if="canManage && !showArchived && !board.archived"
id="stack-add"
@@ -321,9 +321,6 @@ export default {
<style lang="scss" scoped>
.controls {
display: flex;
padding: 3px;
height: 44px;
padding-left: 44px;
.board-title {
display: flex;
@@ -340,7 +337,7 @@ export default {
height: 20px;
border: none;
border-radius: 50%;
background-color: transparent;
background-color: #aaa;
margin: 12px;
margin-left: -4px;
}
@@ -398,7 +395,6 @@ export default {
width: 250px;
max-height: 80vh;
overflow: auto;
padding: 8px;
}
.filter h3 {

View File

@@ -181,7 +181,7 @@ export default {
}
.board {
padding-left: $board-spacing;
margin-left: $board-spacing;
position: relative;
height: calc(100% - 44px);
overflow-x: scroll;

View File

@@ -7,19 +7,10 @@
:options="formatedSharees"
:user-select="true"
label="displayName"
:loading="isLoading || !!isSearching"
:disabled="isLoading"
track-by="multiselectKey"
:internal-search="true"
@input="clickAddAcl"
@search-change="asyncFind">
<template #noOptions>
{{ isSearching ? t('deck', 'Searching for users, groups and circles ...') : t('deck', 'No participants found') }}
</template>
<template #noResult>
{{ isSearching ? t('deck', 'Searching for users, groups and circles ...') : t('deck', 'No participants found') }}
</template>
</Multiselect>
@search-change="asyncFind" />
<ul
id="shareWithList"
@@ -72,7 +63,6 @@ import { Avatar, Multiselect, Actions, ActionButton, ActionCheckbox } from '@nex
import { CollectionList } from 'nextcloud-vue-collections'
import { mapGetters, mapState } from 'vuex'
import { getCurrentUser } from '@nextcloud/auth'
import { showError } from '@nextcloud/dialogs'
export default {
name: 'SharingTabSidebar',
@@ -93,7 +83,6 @@ export default {
data() {
return {
isLoading: false,
isSearching: false,
addAcl: null,
addAclForAPI: null,
}
@@ -113,7 +102,7 @@ export default {
formatedSharees() {
return this.unallocatedSharees.map(item => {
const sharee = {
user: item.value.shareWith,
user: item.label,
displayName: item.label,
icon: 'icon-user',
multiselectKey: item.shareType + ':' + item.primaryKey,
@@ -130,7 +119,7 @@ export default {
sharee.value = item.value
return sharee
}).slice(0, 10)
})
},
unallocatedSharees() {
return this.sharees.filter((sharee) => {
@@ -148,20 +137,13 @@ export default {
this.asyncFind('')
},
methods: {
async asyncFind(query) {
// manual debounce to handle async searching more easily and have more control over the loading state
const timestamp = (new Date()).getTime()
if (!this.isSearching || timestamp > this.isSearching + 300) {
this.isSearching = timestamp
await this.$store.dispatch('loadSharees', query)
// only reset searching flag if the most recent search finished
if (this.isSearching === timestamp) {
this.isSearching = false
}
}
asyncFind(query) {
this.isLoading = true
this.$store.dispatch('loadSharees', query).then(response => {
this.isLoading = false
})
},
async clickAddAcl() {
clickAddAcl() {
this.addAclForAPI = {
type: this.addAcl.value.shareType,
participant: this.addAcl.value.shareWith,
@@ -169,16 +151,7 @@ export default {
permissionShare: false,
permissionManage: false,
}
this.isLoading = true
try {
await this.$store.dispatch('addAclToCurrentBoard', this.addAclForAPI)
} catch (e) {
const errorMessage = t('deck', 'Failed to create share with {displayName}', { displayName: this.addAcl.displayName })
console.error(errorMessage, e)
showError(errorMessage)
}
this.addAcl = null
this.isLoading = false
this.$store.dispatch('addAclToCurrentBoard', this.addAclForAPI)
},
clickEditAcl(acl) {
this.addAclForAPI = Object.assign({}, acl)

View File

@@ -160,7 +160,6 @@ export default {
]),
...mapState({
showArchived: state => state.showArchived,
cardDetailsInModal: state => state.cardDetailsInModal,
}),
cardsByStack() {
return this.$store.getters.cardsByStack(this.stack.id).filter((card) => {
@@ -246,9 +245,7 @@ export default {
this.$refs.newCardInput.focus()
this.animate = false
})
if (!this.cardDetailsInModal) {
this.$router.push({ name: 'card', params: { cardId: newCard.id } })
}
this.$router.push({ name: 'card', params: { cardId: newCard.id } })
} catch (e) {
showError('Could not create card: ' + e.response.data.message)
} finally {

View File

@@ -571,16 +571,14 @@ export default {
// FIXME: Obivously we should at some point not randomly reuse the sidebar component
// since this is not oficially supported
.modal__card .app-sidebar {
$modal-padding: 14px;
border: 0;
min-width: calc(100% - #{$modal-padding*2});
min-width: 100%;
position: relative;
top: 0;
left: 0;
right: 0;
max-width: calc(100% - #{$modal-padding*2});
padding: 14px;
max-height: calc(100% - #{$modal-padding*2});
max-width: 100%;
max-height: 100%;
&::v-deep {
.app-sidebar-header {
position: sticky;

View File

@@ -144,19 +144,17 @@ export default {
}
}
$avatar-offset: 12px;
.avatar-list {
float: right;
display: inline-flex;
padding-right: $avatar-offset;
padding-right: 8px;
flex-direction: row-reverse;
.avatardiv,
/deep/ .avatardiv {
width: 36px;
height: 36px;
box-sizing: content-box !important;
margin-right: -$avatar-offset;
margin-right: -12px;
transition: margin-right 0.2s ease-in-out;
&.icon-more {

View File

@@ -121,6 +121,10 @@ export default {
}
}
.card:not(.card__editable) .avatars {
margin-right: 10px;
}
.fade-enter-active, .fade-leave-active {
transition: opacity .125s;
}

View File

@@ -31,7 +31,7 @@
class="card"
@click="openCard">
<div class="card-upper">
<h3 v-if="compactMode || isArchived || showArchived || !canEdit">
<h3 v-if="isArchived || showArchived || !canEdit">
{{ card.title }}
</h3>
<h3 v-else-if="!editing">
@@ -41,7 +41,6 @@
<form v-if="editing"
v-click-outside="cancelEdit"
class="dragDisabled"
@click.stop
@keyup.esc="cancelEdit"
@submit.prevent="finishedEdit(card)">
<input v-model="copiedCard.title"
@@ -60,9 +59,9 @@
name="zoom"
tag="ul"
class="labels"
@click.stop="openCard">
@click="openCard">
<li v-for="label in labelsSorted" :key="label.id" :style="labelStyle(label)">
<span @click.stop="applyLabelFilter(label)">{{ label.title }}</span>
<span @click="applyLabelFilter(label)">{{ label.title }}</span>
</li>
</transition-group>
<div v-show="!compactMode" class="card-controls compact-item" @click="openCard">
@@ -116,10 +115,10 @@ export default {
]),
canEdit() {
if (this.currentBoard) {
return !this.currentBoard.archived && this.$store.getters.canEdit
return this.$store.getters.canEdit
}
const board = this.$store.getters.boards.find((item) => item.id === this.card.boardId)
return board ? !board.archived && board.permissions.PERMISSION_EDIT : false
return board.permissions.PERMISSION_EDIT
},
card() {
return this.item ? this.item : this.$store.getters.cardById(this.id)
@@ -172,10 +171,6 @@ export default {
background-color: var(--color-main-background);
margin-bottom: $card-spacing;
&::v-deep * {
cursor: pointer;
}
body.dark &, body.theme--dark & {
border: 2px solid var(--color-border);
}
@@ -200,7 +195,7 @@ export default {
margin: 12px $card-padding;
flex-grow: 1;
font-size: 100%;
overflow: hidden;
overflow-x: hidden;
word-wrap: break-word;
padding-left: 4px;
span {
@@ -217,16 +212,11 @@ export default {
.card-controls {
display: flex;
margin-left: $card-padding;
margin-right: $card-padding;
& > div {
display: flex;
max-height: 44px;
}
}
&.card__editable .card-controls {
margin-right: 0;
}
}
.duedate {

View File

@@ -30,18 +30,18 @@
<ActionButton v-if="showArchived === false && isCurrentUserAssigned" icon="icon-user" @click="unassignCardFromMe()">
{{ t('deck', 'Unassign myself') }}
</ActionButton>
<ActionButton icon="icon-external" @click.stop="modalShow=true">
{{ t('deck', 'Move card') }}
</ActionButton>
<ActionButton icon="icon-settings-dark" @click="openCard">
{{ t('deck', 'Card details') }}
</ActionButton>
<ActionButton icon="icon-archive" @click="archiveUnarchiveCard()">
{{ showArchived ? t('deck', 'Unarchive card') : t('deck', 'Archive card') }}
</ActionButton>
<ActionButton v-if="showArchived === false" icon="icon-delete" @click="deleteCard()">
{{ t('deck', 'Delete card') }}
</ActionButton>
<ActionButton icon="icon-external" @click.stop="modalShow=true">
{{ t('deck', 'Move card') }}
</ActionButton>
<ActionButton icon="icon-settings-dark" @click="openCard">
{{ t('deck', 'Card details') }}
</ActionButton>
</Actions>
</div>
<Modal v-if="modalShow" :title="t('deck', 'Move card to another board')" @close="modalShow=false">
@@ -106,7 +106,7 @@ export default {
return this.$store.getters.canEdit
}
const board = this.$store.getters.boards.find((item) => item.id === this.card.boardId)
return !!board?.permissions?.PERMISSION_EDIT
return board.permissions.PERMISSION_EDIT
},
isBoardAndStackChoosen() {
if (this.selectedBoard === '' || this.selectedStack === '') {

View File

@@ -192,4 +192,8 @@ export default {
color: var(--color-text-light);
}
}
::v-deep .app-navigation-toggle {
display: none;
}
</style>

View File

@@ -27,7 +27,6 @@ import { sync } from 'vuex-router-sync'
import { translate, translatePlural } from '@nextcloud/l10n'
import { generateFilePath } from '@nextcloud/router'
import { showError } from '@nextcloud/dialogs'
import { subscribe } from '@nextcloud/event-bus'
import { Tooltip } from '@nextcloud/vue'
import ClickOutside from 'vue-click-outside'
import './models'
@@ -75,17 +74,8 @@ new Vue({
}
},
created() {
subscribe('nextcloud:unified-search.search', ({ query }) => {
this.$store.commit('setSearchQuery', query)
})
subscribe('nextcloud:unified-search.reset', () => {
this.$store.commit('setSearchQuery', '')
})
// FIXME remove this once Nextcloud 20 is minimum required version
// eslint-disable-next-line
new OCA.Search(this.filter, this.cleanSearch)
this.interval = setInterval(() => {
this.time = Date.now()
}, 1000)

View File

@@ -34,6 +34,7 @@ import comment from './comment'
import trashbin from './trashbin'
import attachment from './attachment'
import overview from './overview'
import debounce from 'lodash/debounce'
Vue.use(Vuex)
const apiClient = new BoardApi()
@@ -58,7 +59,7 @@ export default new Vuex.Store({
state: {
config: loadState('deck', 'config', {}),
showArchived: false,
navShown: localStorage.getItem('deck.navShown') === 'true',
navShown: true,
compactMode: localStorage.getItem('deck.compactMode') === 'true',
cardDetailsInModal: localStorage.getItem('deck.cardDetailsInModal') === 'true',
sidebarShown: false,
@@ -203,9 +204,8 @@ export default new Vuex.Store({
return board.id !== b.id
})
},
toggleNav(state, navState) {
state.navShown = navState
localStorage.setItem('deck.navShown', navState)
toggleNav(state) {
state.navShown = !state.navShown
},
toggleSidebar(state) {
state.sidebarShown = !state.sidebarShown
@@ -392,7 +392,7 @@ export default new Vuex.Store({
const boards = await apiClient.loadBoards()
commit('setBoards', boards)
},
async loadSharees({ commit }, query) {
loadSharees: debounce(function({ commit }, query) {
const params = new URLSearchParams()
if (typeof query === 'undefined') {
return
@@ -402,15 +402,16 @@ export default new Vuex.Store({
params.append('perPage', 20)
params.append('itemType', [0, 1, 7])
const response = await axios.get(generateOcsUrl('apps/files_sharing/api/v1') + 'sharees', { params })
commit('setSharees', response.data.ocs.data)
},
axios.get(generateOcsUrl('apps/files_sharing/api/v1') + 'sharees', { params }).then((response) => {
commit('setSharees', response.data.ocs.data)
})
}, 250),
setBoardFilter({ commmit }, filter) {
commmit('setBoardFilter', filter)
},
toggleNav({ commit }, navState) {
commit('toggleNav', navState)
toggleNav({ commit }) {
commit('toggleNav')
},
toggleSidebar({ commit }) {
commit('toggleSidebar')
@@ -453,11 +454,13 @@ export default new Vuex.Store({
},
// acl actions
async addAclToCurrentBoard({ dispatch, commit }, newAcl) {
addAclToCurrentBoard({ dispatch, commit }, newAcl) {
newAcl.boardId = this.state.currentBoard.id
const result = await apiClient.addAcl(newAcl)
commit('addAclToCurrentBoard', result)
dispatch('refreshBoard', newAcl.boardId)
apiClient.addAcl(newAcl)
.then((returnAcl) => {
commit('addAclToCurrentBoard', returnAcl)
dispatch('refreshBoard', newAcl.boardId)
})
},
updateAclFromCurrentBoard({ commit }, acl) {
acl.boardId = this.state.currentBoard.id

View File

@@ -287,7 +287,7 @@ class DeckProviderTest extends TestCase {
$this->assertEquals('test string Card', $event->getParsedSubject());
$this->assertEquals('test string {card}', $event->getRichSubject());
$this->assertEquals('BCD', $event->getMessage());
$this->assertEquals('BCD', $event->getParsedMessage());
$this->assertEquals('<pre class="visualdiff"><del>A</del>BC<ins>D</ins></pre>', $event->getParsedMessage());
}
public function testParseParamForBoard() {