Add new command
Clean code Add new command Import last modified and deleted date Replace arrow functions by lambda functions Add properties to class Add dependency to composer.json Signed-off-by: Vitor Mattos <vitor@php.rio> Turn private methods Add output messages and associate users to cards Signed-off-by: Vitor Mattos <vitor@php.rio>
This commit is contained in:
committed by
Julius Härtl
parent
89028c74cb
commit
48df98ce67
@@ -44,6 +44,7 @@
|
||||
</background-jobs>
|
||||
<commands>
|
||||
<command>OCA\Deck\Command\UserExport</command>
|
||||
<command>OCA\Deck\Command\BoardImport</command>
|
||||
</commands>
|
||||
<activity>
|
||||
<settings>
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"cogpowered/finediff": "0.3.*"
|
||||
"cogpowered/finediff": "0.3.*",
|
||||
"justinrainbow/json-schema": "^5.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"roave/security-advisories": "dev-master",
|
||||
|
||||
120
composer.lock
generated
120
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "1f6d91406db4e7e16e31113951986e13",
|
||||
"content-hash": "cf4fb1b424f5f0c36ecc1391b10de59c",
|
||||
"packages": [
|
||||
{
|
||||
"name": "cogpowered/finediff",
|
||||
@@ -60,6 +60,76 @@
|
||||
"source": "https://github.com/cogpowered/FineDiff/tree/master"
|
||||
},
|
||||
"time": "2014-05-19T10:25:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "justinrainbow/json-schema",
|
||||
"version": "5.2.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/justinrainbow/json-schema.git",
|
||||
"reference": "2ab6744b7296ded80f8cc4f9509abbff393399aa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/2ab6744b7296ded80f8cc4f9509abbff393399aa",
|
||||
"reference": "2ab6744b7296ded80f8cc4f9509abbff393399aa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1",
|
||||
"json-schema/json-schema-test-suite": "1.2.0",
|
||||
"phpunit/phpunit": "^4.8.35"
|
||||
},
|
||||
"bin": [
|
||||
"bin/validate-json"
|
||||
],
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"JsonSchema\\": "src/JsonSchema/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Bruno Prieto Reis",
|
||||
"email": "bruno.p.reis@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Justin Rainbow",
|
||||
"email": "justin.rainbow@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Igor Wiedler",
|
||||
"email": "igor@wiedler.ch"
|
||||
},
|
||||
{
|
||||
"name": "Robert Schönthal",
|
||||
"email": "seroscho@googlemail.com"
|
||||
}
|
||||
],
|
||||
"description": "A library to validate a json schema.",
|
||||
"homepage": "https://github.com/justinrainbow/json-schema",
|
||||
"keywords": [
|
||||
"json",
|
||||
"schema"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/justinrainbow/json-schema/issues",
|
||||
"source": "https://github.com/justinrainbow/json-schema/tree/5.2.11"
|
||||
},
|
||||
"time": "2021-07-22T09:24:00+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
@@ -3677,16 +3747,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v5.4.1",
|
||||
"version": "v5.4.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "9130e1a0fc93cb0faadca4ee917171bd2ca9e5f4"
|
||||
"reference": "a2c6b7ced2eb7799a35375fb9022519282b5405e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/9130e1a0fc93cb0faadca4ee917171bd2ca9e5f4",
|
||||
"reference": "9130e1a0fc93cb0faadca4ee917171bd2ca9e5f4",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/a2c6b7ced2eb7799a35375fb9022519282b5405e",
|
||||
"reference": "a2c6b7ced2eb7799a35375fb9022519282b5405e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3756,7 +3826,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v5.4.1"
|
||||
"source": "https://github.com/symfony/console/tree/v5.4.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3772,7 +3842,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-12-09T11:22:43+00:00"
|
||||
"time": "2021-12-20T16:11:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
@@ -4070,16 +4140,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v5.4.0",
|
||||
"version": "v5.4.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "d2f29dac98e96a98be467627bd49c2efb1bc2590"
|
||||
"reference": "e77046c252be48c48a40816187ed527703c8f76c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/d2f29dac98e96a98be467627bd49c2efb1bc2590",
|
||||
"reference": "d2f29dac98e96a98be467627bd49c2efb1bc2590",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/e77046c252be48c48a40816187ed527703c8f76c",
|
||||
"reference": "e77046c252be48c48a40816187ed527703c8f76c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4113,7 +4183,7 @@
|
||||
"description": "Finds files and directories via an intuitive fluent interface",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/finder/tree/v5.4.0"
|
||||
"source": "https://github.com/symfony/finder/tree/v5.4.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4129,7 +4199,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-28T15:25:38+00:00"
|
||||
"time": "2021-12-15T11:06:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/options-resolver",
|
||||
@@ -4767,16 +4837,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v5.4.0",
|
||||
"version": "v5.4.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "5be20b3830f726e019162b26223110c8f47cf274"
|
||||
"reference": "2b3ba8722c4aaf3e88011be5e7f48710088fb5e4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/5be20b3830f726e019162b26223110c8f47cf274",
|
||||
"reference": "5be20b3830f726e019162b26223110c8f47cf274",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/2b3ba8722c4aaf3e88011be5e7f48710088fb5e4",
|
||||
"reference": "2b3ba8722c4aaf3e88011be5e7f48710088fb5e4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4809,7 +4879,7 @@
|
||||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v5.4.0"
|
||||
"source": "https://github.com/symfony/process/tree/v5.4.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4825,7 +4895,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-28T15:25:38+00:00"
|
||||
"time": "2021-12-27T21:01:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
@@ -4974,16 +5044,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
"version": "v5.4.0",
|
||||
"version": "v5.4.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/string.git",
|
||||
"reference": "9ffaaba53c61ba75a3c7a3a779051d1e9ec4fd2d"
|
||||
"reference": "e6a5d5ecf6589c5247d18e0e74e30b11dfd51a3d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/9ffaaba53c61ba75a3c7a3a779051d1e9ec4fd2d",
|
||||
"reference": "9ffaaba53c61ba75a3c7a3a779051d1e9ec4fd2d",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/e6a5d5ecf6589c5247d18e0e74e30b11dfd51a3d",
|
||||
"reference": "e6a5d5ecf6589c5247d18e0e74e30b11dfd51a3d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5040,7 +5110,7 @@
|
||||
"utf8"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/string/tree/v5.4.0"
|
||||
"source": "https://github.com/symfony/string/tree/v5.4.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -5056,7 +5126,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-24T10:02:00+00:00"
|
||||
"time": "2021-12-16T21:52:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "theseer/tokenizer",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
|
||||
* @copyright Copyright (c) 2021 Vitor Mattos <vitor@php.rio>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
* @author Vitor Mattos <vitor@php.rio>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
@@ -23,80 +23,37 @@
|
||||
|
||||
namespace OCA\Deck\Command;
|
||||
|
||||
use JsonSchema\Validator;
|
||||
use OCA\Deck\Db\AssignmentMapper;
|
||||
use OCA\Deck\Db\Board;
|
||||
use OCA\Deck\Db\BoardMapper;
|
||||
use OCA\Deck\Db\Card;
|
||||
use OCA\Deck\Db\CardMapper;
|
||||
use OCA\Deck\Db\Stack;
|
||||
use OCA\Deck\Db\StackMapper;
|
||||
use OCA\Deck\Service\BoardService;
|
||||
use OCA\Deck\Service\LabelService;
|
||||
use OCA\Deck\Service\PermissionService;
|
||||
use OCA\Deck\Service\StackService;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IUserManager;
|
||||
use OCP\IUserSession;
|
||||
use OCA\Deck\Command\Helper\ImportInterface;
|
||||
use OCA\Deck\Command\Helper\TrelloHelper;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
|
||||
class BoardImport extends Command {
|
||||
/** @var BoardService */
|
||||
private $boardService;
|
||||
// protected $cardMapper;
|
||||
/** @var LabelService */
|
||||
private $labelService;
|
||||
/** @var StackMapper */
|
||||
private $stackMapper;
|
||||
/** @var CardMapper */
|
||||
private $cardMapper;
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
// /** @var IGroupManager */
|
||||
// private $groupManager;
|
||||
// private $assignedUsersMapper;
|
||||
/** @var string */
|
||||
private $system;
|
||||
private $allowedSystems = ['trello'];
|
||||
/** @var Board */
|
||||
private $board;
|
||||
/** @var TrelloHelper */
|
||||
private $trelloHelper;
|
||||
/**
|
||||
* Data object created from settings JSON
|
||||
*
|
||||
* @var \StdClass
|
||||
*/
|
||||
public $settings;
|
||||
|
||||
public function __construct(
|
||||
// BoardMapper $boardMapper,
|
||||
BoardService $boardService,
|
||||
LabelService $labelService,
|
||||
StackMapper $stackMapper,
|
||||
CardMapper $cardMapper,
|
||||
// IUserSession $userSession,
|
||||
// StackMapper $stackMapper,
|
||||
// CardMapper $cardMapper,
|
||||
// AssignmentMapper $assignedUsersMapper,
|
||||
IUserManager $userManager
|
||||
// IGroupManager $groupManager
|
||||
TrelloHelper $trelloHelper
|
||||
) {
|
||||
parent::__construct();
|
||||
|
||||
// $this->cardMapper = $cardMapper;
|
||||
$this->boardService = $boardService;
|
||||
$this->labelService = $labelService;
|
||||
$this->stackMapper = $stackMapper;
|
||||
$this->cardMapper = $cardMapper;
|
||||
|
||||
// $this->userSession = $userSession;
|
||||
// $this->stackMapper = $stackMapper;
|
||||
// $this->assignedUsersMapper = $assignedUsersMapper;
|
||||
// $this->boardMapper = $boardMapper;
|
||||
|
||||
$this->userManager = $userManager;
|
||||
// $this->groupManager = $groupManager;
|
||||
$this->trelloHelper = $trelloHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure() {
|
||||
$this
|
||||
->setName('deck:import')
|
||||
@@ -127,73 +84,35 @@ class BoardImport extends Command {
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function interact(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
protected function interact(InputInterface $input, OutputInterface $output) {
|
||||
$this->validateSystem($input, $output);
|
||||
$this->validateData($input, $output);
|
||||
$this->validateSettings($input, $output);
|
||||
$this->validateUsers();
|
||||
$this->validateOwner();
|
||||
$this->getSystem()
|
||||
->validate($input, $output);
|
||||
}
|
||||
|
||||
public function validateData(InputInterface $input, OutputInterface $output) {
|
||||
$filename = $input->getOption('data');
|
||||
if (!is_file($filename)) {
|
||||
$helper = $this->getHelper('question');
|
||||
$question = new Question(
|
||||
'Please inform a valid data json file: ',
|
||||
'data.json'
|
||||
);
|
||||
$question->setValidator(function ($answer) {
|
||||
if (!is_file($answer)) {
|
||||
throw new \RuntimeException(
|
||||
'Data file not found'
|
||||
);
|
||||
}
|
||||
return $answer;
|
||||
});
|
||||
$data = $helper->ask($input, $output, $question);
|
||||
$input->setOption('data', $data);
|
||||
}
|
||||
$this->data = json_decode(file_get_contents($filename));
|
||||
if (!$this->data) {
|
||||
$output->writeln('<error>Is not a json file: ' . $filename . '</error>');
|
||||
$this->validateData($input, $output);
|
||||
}
|
||||
if (!$this->data) {
|
||||
$this->data = json_decode(file_get_contents($filename));
|
||||
}
|
||||
private function setSystem(string $system): void {
|
||||
$this->system = $system;
|
||||
}
|
||||
|
||||
private function validateOwner() {
|
||||
$this->settings->owner = $this->userManager->get($this->settings->owner);
|
||||
if (!$this->settings->owner) {
|
||||
throw new \LogicException('Owner "' . $this->settings->owner . '" not found on Nextcloud. Check setting json.');
|
||||
}
|
||||
}
|
||||
|
||||
private function validateUsers() {
|
||||
if (empty($this->settings->uidRelation)) {
|
||||
return;
|
||||
}
|
||||
foreach ($this->settings->uidRelation as $trelloUid => $nextcloudUid) {
|
||||
$user = array_filter($this->data->members, fn($u) => $u->username === $trelloUid);
|
||||
if (!$user) {
|
||||
throw new \LogicException('Trello user ' . $trelloUid . ' not found in property "members" of json data');
|
||||
}
|
||||
if (!is_string($nextcloudUid)) {
|
||||
throw new \LogicException('User on setting uidRelation must be a string');
|
||||
}
|
||||
$this->settings->uidRelation->$trelloUid = $this->userManager->get($nextcloudUid);
|
||||
if (!$this->settings->uidRelation->$trelloUid) {
|
||||
throw new \LogicException('User on setting uidRelation not found: ' . $nextcloudUid);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return ImportInterface
|
||||
*/
|
||||
private function getSystem() {
|
||||
$helper = $this->{$this->system . 'Helper'};
|
||||
$helper->setCommand($this);
|
||||
return $helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private function validateSystem(InputInterface $input, OutputInterface $output) {
|
||||
if (in_array($input->getOption('system'), $this->allowedSystems)) {
|
||||
$system = $input->getOption('system');
|
||||
if (in_array($system, $this->allowedSystems)) {
|
||||
$this->setSystem($system);
|
||||
return;
|
||||
}
|
||||
$helper = $this->getHelper('question');
|
||||
@@ -205,220 +124,19 @@ class BoardImport extends Command {
|
||||
$question->setErrorMessage('System %s is invalid.');
|
||||
$system = $helper->ask($input, $output, $question);
|
||||
$input->setOption('system', $system);
|
||||
}
|
||||
|
||||
private function validateSettings(InputInterface $input, OutputInterface $output) {
|
||||
if (!is_file($input->getOption('setting'))) {
|
||||
$helper = $this->getHelper('question');
|
||||
$question = new Question(
|
||||
'Please inform a valid setting json file: ',
|
||||
'config.json'
|
||||
);
|
||||
$question->setValidator(function ($answer) {
|
||||
if (!is_file($answer)) {
|
||||
throw new \RuntimeException(
|
||||
'Setting file not found'
|
||||
);
|
||||
}
|
||||
return $answer;
|
||||
});
|
||||
$setting = $helper->ask($input, $output, $question);
|
||||
$input->setOption('setting', $setting);
|
||||
}
|
||||
|
||||
$this->settings = json_decode(file_get_contents($input->getOption('setting')));
|
||||
$validator = new Validator();
|
||||
$validator->validate(
|
||||
$this->settings,
|
||||
(object)['$ref' => 'file://' . realpath(__DIR__ . '/fixtures/setting-schema.json')]
|
||||
);
|
||||
if (!$validator->isValid()) {
|
||||
$output->writeln('<error>Invalid setting file</error>');
|
||||
$output->writeln(array_map(fn($v) => $v['message'], $validator->getErrors()));
|
||||
$output->writeln('Valid schema:');
|
||||
$output->writeln(print_r(file_get_contents(__DIR__ . '/fixtures/setting-schema.json'), true));
|
||||
$input->setOption('setting', null);
|
||||
$this->validateSettings($input, $output);
|
||||
}
|
||||
$this->setSystem($system);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return void
|
||||
* @throws DoesNotExistException
|
||||
* @throws MultipleObjectsReturnedException
|
||||
* @throws \ReflectionException
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
// $this->boardService->setUserId($this->settings->owner->getUID());
|
||||
$this->setUserId($this->settings->owner->getUID());
|
||||
// $this->userSession->setUser($this->settings->owner);
|
||||
$this->importBoard();
|
||||
$this->importLabels();
|
||||
$this->importStacks();
|
||||
$this->importCards();
|
||||
// $boards = $this->boardService->findAll();
|
||||
|
||||
// $data = [];
|
||||
// foreach ($boards as $board) {
|
||||
// $fullBoard = $this->boardMapper->find($board->getId(), true, true);
|
||||
// $data[$board->getId()] = (array)$fullBoard->jsonSerialize();
|
||||
// $stacks = $this->stackMapper->findAll($board->getId());
|
||||
// foreach ($stacks as $stack) {
|
||||
// $data[$board->getId()]['stacks'][] = (array)$stack->jsonSerialize();
|
||||
// $cards = $this->cardMapper->findAllByStack($stack->getId());
|
||||
// foreach ($cards as $card) {
|
||||
// $fullCard = $this->cardMapper->find($card->getId());
|
||||
// $assignedUsers = $this->assignedUsersMapper->findAll($card->getId());
|
||||
// $fullCard->setAssignedUsers($assignedUsers);
|
||||
// $data[$board->getId()]['stacks'][$stack->getId()]['cards'][] = (array)$fullCard->jsonSerialize();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// $output->writeln(json_encode($data, JSON_PRETTY_PRINT));
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
private function checklistItem($item) {
|
||||
if (($item->state == 'incomplete')) {
|
||||
$string_start = '- [ ]';
|
||||
} else {
|
||||
$string_start = '- [x]';
|
||||
}
|
||||
$check_item_string = $string_start . ' ' . $item->name . "\n";
|
||||
return $check_item_string;
|
||||
}
|
||||
|
||||
function formulateChecklistText($checklist) {
|
||||
$checklist_string = "\n\n## {$checklist->name}\n";
|
||||
foreach ($checklist->checkItems as $item) {
|
||||
$checklist_item_string = $this->checklistItem($item);
|
||||
$checklist_string = $checklist_string . "\n" . $checklist_item_string;
|
||||
}
|
||||
return $checklist_string;
|
||||
}
|
||||
|
||||
private function importCards() {
|
||||
# Save checklist content into a dictionary (_should_ work even if a card has multiple checklists
|
||||
foreach ($this->data->checklists as $checklist) {
|
||||
$checklists[$checklist->idCard][$checklist->id] = $this->formulateChecklistText($checklist);
|
||||
}
|
||||
$this->data->checklists = $checklists;
|
||||
|
||||
foreach ($this->data->cards as $trelloCard) {
|
||||
# Check whether a card is archived, if true, skipping to the next card
|
||||
if ($trelloCard->closed) {
|
||||
continue;
|
||||
}
|
||||
if ((count($trelloCard->idChecklists) !== 0)) {
|
||||
foreach ($this->data->checklists[$trelloCard->id] as $checklist) {
|
||||
$trelloCard->desc .= "\n" . $checklist;
|
||||
}
|
||||
}
|
||||
|
||||
$card = new Card();
|
||||
$card->setTitle($trelloCard->name);
|
||||
$card->setStackId($this->stacks[$trelloCard->idList]);
|
||||
$card->setType('plain');
|
||||
$card->setOrder($trelloCard->idShort);
|
||||
$card->setOwner($this->settings->owner->getUID());
|
||||
$card->setDescription($trelloCard->desc);
|
||||
if ($trelloCard->due) {
|
||||
$duedate = \DateTime::createFromFormat('Y-m-d\TH:i:s.000\Z', $trelloCard->due)
|
||||
->format('Y-m-d H:i:s');
|
||||
$card->setDuedate($duedate);
|
||||
}
|
||||
$card = $this->cardMapper->insert($card);
|
||||
|
||||
$this->associateCardToLabels($card->getId(), $trelloCard);
|
||||
}
|
||||
}
|
||||
|
||||
public function associateCardToLabels($cardId, $card) {
|
||||
foreach ($card->labels as $label) {
|
||||
$this->cardMapper->assignLabel(
|
||||
$cardId,
|
||||
$this->labels[$label->id]->getId()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function importStacks() {
|
||||
$this->stacks = [];
|
||||
foreach ($this->data->lists as $order => $list) {
|
||||
if ($list->closed) {
|
||||
continue;
|
||||
}
|
||||
$stack = new Stack();
|
||||
$stack->setTitle($list->name);
|
||||
$stack->setBoardId($this->board->getId());
|
||||
$stack->setOrder($order + 1);
|
||||
$stack = $this->stackMapper->insert($stack);
|
||||
$this->stacks[$list->id] = $stack;
|
||||
}
|
||||
}
|
||||
|
||||
private function translateColor($color) {
|
||||
switch ($color) {
|
||||
case 'red':
|
||||
return 'ff0000';
|
||||
case 'yellow':
|
||||
return 'ffff00';
|
||||
case 'orange':
|
||||
return 'ff6600';
|
||||
case 'green':
|
||||
return '00ff00';
|
||||
case 'purple':
|
||||
return '9900ff';
|
||||
case 'blue':
|
||||
return '0000ff';
|
||||
case 'sky':
|
||||
return '00ccff';
|
||||
case 'lime':
|
||||
return '00ff99';
|
||||
case 'pink':
|
||||
return 'ff66cc';
|
||||
case 'black':
|
||||
return '000000';
|
||||
default:
|
||||
return 'ffffff';
|
||||
}
|
||||
}
|
||||
|
||||
private function importBoard() {
|
||||
$this->board = $this->boardService->create(
|
||||
$this->data->name,
|
||||
$this->settings->owner->getUID(),
|
||||
$this->settings->color
|
||||
);
|
||||
// $this->boardService->find($this->board->getId());
|
||||
}
|
||||
|
||||
public function importLabels() {
|
||||
$this->labels = [];
|
||||
foreach ($this->data->labels as $label) {
|
||||
if (empty($label->name)) {
|
||||
$labelTitle = 'Unnamed ' . $label->color . ' label';
|
||||
} else {
|
||||
$labelTitle = $label->name;
|
||||
}
|
||||
$newLabel = $this->labelService->create(
|
||||
$labelTitle,
|
||||
$this->translateColor($label->color),
|
||||
$this->board->getId()
|
||||
);
|
||||
$this->labels[$label->id] = $newLabel;
|
||||
}
|
||||
}
|
||||
|
||||
private function setUserId() {
|
||||
$propertyPermissionService = new \ReflectionProperty($this->labelService, 'permissionService');
|
||||
$propertyPermissionService->setAccessible(true);
|
||||
$permissionService = $propertyPermissionService->getValue($this->labelService);
|
||||
|
||||
$propertyUserId = new \ReflectionProperty($permissionService, 'userId');
|
||||
$propertyUserId->setAccessible(true);
|
||||
$propertyUserId->setValue($permissionService, $this->settings->owner->getUID());
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
$this->getSystem()
|
||||
->import($input, $output);
|
||||
$output->writeln('Done!');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
86
lib/Command/Helper/ImportAbstract.php
Normal file
86
lib/Command/Helper/ImportAbstract.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace OCA\Deck\Command\Helper;
|
||||
|
||||
use JsonSchema\Validator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
|
||||
class ImportAbstract {
|
||||
/** @var Command */
|
||||
private $command;
|
||||
/** @var \stdClass */
|
||||
private $settings;
|
||||
|
||||
public function setCommand(Command $command): void {
|
||||
$this->command = $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Command
|
||||
*/
|
||||
public function getCommand() {
|
||||
return $this->command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a setting
|
||||
*
|
||||
* @param string $setting Setting name
|
||||
* @return mixed
|
||||
*/
|
||||
public function getSetting($setting) {
|
||||
return $this->settings->$setting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a setting
|
||||
*
|
||||
* @param string $settingName
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function setSetting($settingName, $value) {
|
||||
$this->settings->$settingName = $value;
|
||||
}
|
||||
|
||||
protected function validateSettings(InputInterface $input, OutputInterface $output): void {
|
||||
$settingFile = $input->getOption('setting');
|
||||
if (!is_file($settingFile)) {
|
||||
$helper = $this->getCommand()->getHelper('question');
|
||||
$question = new Question(
|
||||
'Please inform a valid setting json file: ',
|
||||
'config.json'
|
||||
);
|
||||
$question->setValidator(function ($answer) {
|
||||
if (!is_file($answer)) {
|
||||
throw new \RuntimeException(
|
||||
'Setting file not found'
|
||||
);
|
||||
}
|
||||
return $answer;
|
||||
});
|
||||
$settingFile = $helper->ask($input, $output, $question);
|
||||
$input->setOption('setting', $settingFile);
|
||||
}
|
||||
|
||||
$this->settings = json_decode(file_get_contents($settingFile));
|
||||
$validator = new Validator();
|
||||
$validator->validate(
|
||||
$this->settings,
|
||||
(object)['$ref' => 'file://' . realpath(__DIR__ . '/../fixtures/setting-schema.json')]
|
||||
);
|
||||
if (!$validator->isValid()) {
|
||||
$output->writeln('<error>Invalid setting file</error>');
|
||||
$output->writeln(array_map(function ($v) {
|
||||
return $v['message'];
|
||||
}, $validator->getErrors()));
|
||||
$output->writeln('Valid schema:');
|
||||
$output->writeln(print_r(file_get_contents(__DIR__ . '/fixtures/setting-schema.json'), true));
|
||||
$input->setOption('setting', null);
|
||||
$this->validateSettings($input, $output);
|
||||
}
|
||||
}
|
||||
}
|
||||
47
lib/Command/Helper/ImportInterface.php
Normal file
47
lib/Command/Helper/ImportInterface.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2021 Vitor Mattos <vitor@php.rio>
|
||||
*
|
||||
* @author Vitor Mattos <vitor@php.rio>
|
||||
*
|
||||
* @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\Command\Helper;
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
interface ImportInterface {
|
||||
/**
|
||||
* Validate data before run execute method
|
||||
*
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return void
|
||||
*/
|
||||
public function validate(InputInterface $input, OutputInterface $output): void;
|
||||
|
||||
/**
|
||||
* Run import
|
||||
*
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return void
|
||||
*/
|
||||
public function import(InputInterface $input, OutputInterface $output): void;
|
||||
}
|
||||
429
lib/Command/Helper/TrelloHelper.php
Normal file
429
lib/Command/Helper/TrelloHelper.php
Normal file
@@ -0,0 +1,429 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2021 Vitor Mattos <vitor@php.rio>
|
||||
*
|
||||
* @author Vitor Mattos <vitor@php.rio>
|
||||
*
|
||||
* @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\Command\Helper;
|
||||
|
||||
use OCA\Deck\Db\Acl;
|
||||
use OCA\Deck\Db\AclMapper;
|
||||
use OCA\Deck\Db\Assignment;
|
||||
use OCA\Deck\Db\AssignmentMapper;
|
||||
use OCA\Deck\Db\Board;
|
||||
use OCA\Deck\Db\Card;
|
||||
use OCA\Deck\Db\CardMapper;
|
||||
use OCA\Deck\Db\Label;
|
||||
use OCA\Deck\Db\Stack;
|
||||
use OCA\Deck\Db\StackMapper;
|
||||
use OCA\Deck\Service\BoardService;
|
||||
use OCA\Deck\Service\LabelService;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
|
||||
class TrelloHelper extends ImportAbstract implements ImportInterface {
|
||||
/** @var BoardService */
|
||||
private $boardService;
|
||||
/** @var StackMapper */
|
||||
private $stackMapper;
|
||||
/** @var CardMapper */
|
||||
private $cardMapper;
|
||||
/** @var AssignmentMapper */
|
||||
private $assignmentMapper;
|
||||
/** @var AclMapper */
|
||||
private $aclMapper;
|
||||
/** @var IDBConnection */
|
||||
private $connection;
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
/** @var TrelloActions */
|
||||
private $trelloActions;
|
||||
/** @var Board */
|
||||
private $board;
|
||||
/** @var LabelService */
|
||||
private $labelService;
|
||||
/**
|
||||
* Data object created from JSON of origin system
|
||||
*
|
||||
* @var \StdClass
|
||||
*/
|
||||
private $data;
|
||||
/**
|
||||
* Array of stacks
|
||||
*
|
||||
* @var Stack[]
|
||||
*/
|
||||
private $stacks = [];
|
||||
/**
|
||||
* Array of labels
|
||||
*
|
||||
* @var Label[]
|
||||
*/
|
||||
private $labels = [];
|
||||
/** @var Card[] */
|
||||
private $cards = [];
|
||||
/** @var IUser */
|
||||
private $members = [];
|
||||
|
||||
public function __construct(
|
||||
BoardService $boardService,
|
||||
LabelService $labelService,
|
||||
StackMapper $stackMapper,
|
||||
CardMapper $cardMapper,
|
||||
AssignmentMapper $assignmentMapper,
|
||||
AclMapper $aclMapper,
|
||||
IDBConnection $connection,
|
||||
IUserManager $userManager
|
||||
) {
|
||||
$this->boardService = $boardService;
|
||||
$this->labelService = $labelService;
|
||||
$this->stackMapper = $stackMapper;
|
||||
$this->cardMapper = $cardMapper;
|
||||
$this->assignmentMapper = $assignmentMapper;
|
||||
$this->aclMapper = $aclMapper;
|
||||
$this->connection = $connection;
|
||||
$this->userManager = $userManager;
|
||||
}
|
||||
|
||||
public function validate(InputInterface $input, OutputInterface $output): void {
|
||||
$this->validateData($input, $output);
|
||||
$this->validateSettings($input, $output);
|
||||
$this->validateUsers();
|
||||
$this->validateOwner();
|
||||
}
|
||||
|
||||
public function import(InputInterface $input, OutputInterface $output): void {
|
||||
$this->setUserId();
|
||||
$output->writeln('Importing board...');
|
||||
$this->importBoard();
|
||||
$output->writeln('Assign users to board...');
|
||||
$this->assignUsersToBoard();
|
||||
$output->writeln('Importing labels...');
|
||||
$this->importLabels();
|
||||
$output->writeln('Importing stacks...');
|
||||
$this->importStacks();
|
||||
$output->writeln('Importing cards...');
|
||||
$this->importCards();
|
||||
}
|
||||
|
||||
private function assignUsersToBoard() {
|
||||
foreach ($this->members as $member) {
|
||||
$acl = new Acl();
|
||||
$acl->setBoardId($this->board->getId());
|
||||
$acl->setType(Acl::PERMISSION_TYPE_USER);
|
||||
$acl->setParticipant($member->getUid());
|
||||
$acl->setPermissionEdit(true);
|
||||
$acl->setPermissionShare($member->getUID() === $this->getSetting('owner')->getUID());
|
||||
$acl->setPermissionManage($member->getUID() === $this->getSetting('owner')->getUID());
|
||||
$this->aclMapper->insert($acl);
|
||||
}
|
||||
}
|
||||
|
||||
private function validateData(InputInterface $input, OutputInterface $output): void {
|
||||
$filename = $input->getOption('data');
|
||||
if (!is_file($filename)) {
|
||||
$helper = $this->getCommand()->getHelper('question');
|
||||
$question = new Question(
|
||||
'Please inform a valid data json file: ',
|
||||
'data.json'
|
||||
);
|
||||
$question->setValidator(function ($answer) {
|
||||
if (!is_file($answer)) {
|
||||
throw new \RuntimeException(
|
||||
'Data file not found'
|
||||
);
|
||||
}
|
||||
return $answer;
|
||||
});
|
||||
$data = $helper->ask($input, $output, $question);
|
||||
$input->setOption('data', $data);
|
||||
}
|
||||
$this->data = json_decode(file_get_contents($filename));
|
||||
if (!$this->data) {
|
||||
$output->writeln('<error>Is not a json file: ' . $filename . '</error>');
|
||||
$this->validateData($input, $output);
|
||||
}
|
||||
}
|
||||
|
||||
private function validateOwner(): void {
|
||||
$owner = $this->userManager->get($this->getSetting('owner'));
|
||||
if (!$owner) {
|
||||
throw new \LogicException('Owner "' . $this->getSetting('owner') . '" not found on Nextcloud. Check setting json.');
|
||||
}
|
||||
$this->setSetting('owner', $owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private function validateUsers() {
|
||||
if (empty($this->getSetting('uidRelation'))) {
|
||||
return;
|
||||
}
|
||||
foreach ($this->getSetting('uidRelation') as $trelloUid => $nextcloudUid) {
|
||||
$user = array_filter($this->data->members, function ($u) use ($trelloUid) {
|
||||
return $u->username === $trelloUid;
|
||||
});
|
||||
if (!$user) {
|
||||
throw new \LogicException('Trello user ' . $trelloUid . ' not found in property "members" of json data');
|
||||
}
|
||||
if (!is_string($nextcloudUid)) {
|
||||
throw new \LogicException('User on setting uidRelation must be a string');
|
||||
}
|
||||
$this->getSetting('uidRelation')->$trelloUid = $this->userManager->get($nextcloudUid);
|
||||
if (!$this->getSetting('uidRelation')->$trelloUid) {
|
||||
throw new \LogicException('User on setting uidRelation not found: ' . $nextcloudUid);
|
||||
}
|
||||
$user = current($user);
|
||||
$this->members[$user->id] = $this->getSetting('uidRelation')->$trelloUid;
|
||||
}
|
||||
}
|
||||
|
||||
private function checklistItem($item): string {
|
||||
if (($item->state == 'incomplete')) {
|
||||
$string_start = '- [ ]';
|
||||
} else {
|
||||
$string_start = '- [x]';
|
||||
}
|
||||
$check_item_string = $string_start . ' ' . $item->name . "\n";
|
||||
return $check_item_string;
|
||||
}
|
||||
|
||||
private function formulateChecklistText($checklist): string {
|
||||
$checklist_string = "\n\n## {$checklist->name}\n";
|
||||
foreach ($checklist->checkItems as $item) {
|
||||
$checklist_item_string = $this->checklistItem($item);
|
||||
$checklist_string = $checklist_string . "\n" . $checklist_item_string;
|
||||
}
|
||||
return $checklist_string;
|
||||
}
|
||||
|
||||
private function importCards(): void {
|
||||
$checklists = [];
|
||||
foreach ($this->data->checklists as $checklist) {
|
||||
$checklists[$checklist->idCard][$checklist->id] = $this->formulateChecklistText($checklist);
|
||||
}
|
||||
$this->data->checklists = $checklists;
|
||||
|
||||
foreach ($this->data->cards as $trelloCard) {
|
||||
$card = new Card();
|
||||
$lastModified = \DateTime::createFromFormat('Y-m-d\TH:i:s.v\Z', $trelloCard->dateLastActivity);
|
||||
$card->setLastModified($lastModified->format('Y-m-d H:i:s'));
|
||||
if ($trelloCard->closed) {
|
||||
$card->setDeletedAt($lastModified->format('U'));
|
||||
}
|
||||
if ((count($trelloCard->idChecklists) !== 0)) {
|
||||
foreach ($this->data->checklists[$trelloCard->id] as $checklist) {
|
||||
$trelloCard->desc .= "\n" . $checklist;
|
||||
}
|
||||
}
|
||||
$this->appendAttachmentsToDescription($trelloCard);
|
||||
|
||||
$card->setTitle($trelloCard->name);
|
||||
$card->setStackId($this->stacks[$trelloCard->idList]->getId());
|
||||
$card->setType('plain');
|
||||
$card->setOrder($trelloCard->idShort);
|
||||
$card->setOwner($this->getSetting('owner')->getUID());
|
||||
$card->setDescription($trelloCard->desc);
|
||||
if ($trelloCard->due) {
|
||||
$duedate = \DateTime::createFromFormat('Y-m-d\TH:i:s.v\Z', $trelloCard->due)
|
||||
->format('Y-m-d H:i:s');
|
||||
$card->setDuedate($duedate);
|
||||
}
|
||||
$card = $this->cardMapper->insert($card);
|
||||
$this->cards[$trelloCard->id] = $card;
|
||||
|
||||
$this->associateCardToLabels($card, $trelloCard);
|
||||
$this->importComments($card, $trelloCard);
|
||||
$this->assignToMember($card, $trelloCard);
|
||||
}
|
||||
}
|
||||
|
||||
private function appendAttachmentsToDescription($trelloCard) {
|
||||
if (empty($trelloCard->attachments)) {
|
||||
return;
|
||||
}
|
||||
$translations = $this->getSetting('translations');
|
||||
$attachmentsLabel = empty($translations->{'Attachments'}) ? 'Attachments' : $translations->{'Attachments'};
|
||||
$URLLabel = empty($translations->{'URL'}) ? 'URL' : $translations->{'URL'};
|
||||
$nameLabel = empty($translations->{'Name'}) ? 'Name' : $translations->{'Name'};
|
||||
$dateLabel = empty($translations->{'Date'}) ? 'Date' : $translations->{'Date'};
|
||||
$trelloCard->desc .= "\n\n## {$attachmentsLabel}\n";
|
||||
$trelloCard->desc .= "| $URLLabel | $nameLabel | $dateLabel |\n";
|
||||
$trelloCard->desc .= "|---|---|---|\n";
|
||||
foreach ($trelloCard->attachments as $attachment) {
|
||||
$name = $attachment->name === $attachment->url ? null : $attachment->name;
|
||||
$trelloCard->desc .= "| {$attachment->url} | {$name} | {$attachment->date} |\n";
|
||||
}
|
||||
}
|
||||
|
||||
private function assignToMember(Card $card, $trelloCard) {
|
||||
foreach ($trelloCard->idMembers as $idMember) {
|
||||
$assignment = new Assignment();
|
||||
$assignment->setCardId($card->getId());
|
||||
$assignment->setParticipant($this->members[$idMember]->getUID());
|
||||
$assignment->setType(Assignment::TYPE_USER);
|
||||
$assignment = $this->assignmentMapper->insert($assignment);
|
||||
}
|
||||
}
|
||||
|
||||
private function importComments(\OCP\AppFramework\Db\Entity $card, $trelloCard): void {
|
||||
$comments = array_filter(
|
||||
$this->data->actions,
|
||||
function ($a) use ($trelloCard) {
|
||||
return $a->type === 'commentCard' && $a->data->card->id === $trelloCard->id;
|
||||
}
|
||||
);
|
||||
foreach ($comments as $trelloComment) {
|
||||
if (!empty($this->getSetting('uidRelation')->{$trelloComment->memberCreator->username})) {
|
||||
$actor = $this->getSetting('uidRelation')->{$trelloComment->memberCreator->username}->getUID();
|
||||
} else {
|
||||
$actor = $this->getSetting('owner')->getUID();
|
||||
}
|
||||
$message = $this->replaceUsernames($trelloComment->data->text);
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
|
||||
$values = [
|
||||
'parent_id' => $qb->createNamedParameter(0),
|
||||
'topmost_parent_id' => $qb->createNamedParameter(0),
|
||||
'children_count' => $qb->createNamedParameter(0),
|
||||
'actor_type' => $qb->createNamedParameter('users'),
|
||||
'actor_id' => $qb->createNamedParameter($actor),
|
||||
'message' => $qb->createNamedParameter($message),
|
||||
'verb' => $qb->createNamedParameter('comment'),
|
||||
'creation_timestamp' => $qb->createNamedParameter(
|
||||
\DateTime::createFromFormat('Y-m-d\TH:i:s.v\Z', $trelloComment->date)
|
||||
->format('Y-m-d H:i:s')
|
||||
),
|
||||
'latest_child_timestamp' => $qb->createNamedParameter(null),
|
||||
'object_type' => $qb->createNamedParameter('deckCard'),
|
||||
'object_id' => $qb->createNamedParameter($card->getId()),
|
||||
];
|
||||
|
||||
$qb->insert('comments')
|
||||
->values($values)
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
private function replaceUsernames($text) {
|
||||
foreach ($this->getSetting('uidRelation') as $trello => $nextcloud) {
|
||||
$text = str_replace($trello, $nextcloud->getUID(), $text);
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function associateCardToLabels(\OCP\AppFramework\Db\Entity $card, $trelloCard): void {
|
||||
foreach ($trelloCard->labels as $label) {
|
||||
$this->cardMapper->assignLabel(
|
||||
$card->getId(),
|
||||
$this->labels[$label->id]->getId()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function importStacks(): void {
|
||||
$this->stacks = [];
|
||||
foreach ($this->data->lists as $order => $list) {
|
||||
$stack = new Stack();
|
||||
if ($list->closed) {
|
||||
$stack->setDeletedAt(time());
|
||||
}
|
||||
$stack->setTitle($list->name);
|
||||
$stack->setBoardId($this->board->getId());
|
||||
$stack->setOrder($order + 1);
|
||||
$stack = $this->stackMapper->insert($stack);
|
||||
$this->stacks[$list->id] = $stack;
|
||||
}
|
||||
}
|
||||
|
||||
private function translateColor($color): string {
|
||||
switch ($color) {
|
||||
case 'red':
|
||||
return 'ff0000';
|
||||
case 'yellow':
|
||||
return 'ffff00';
|
||||
case 'orange':
|
||||
return 'ff6600';
|
||||
case 'green':
|
||||
return '00ff00';
|
||||
case 'purple':
|
||||
return '9900ff';
|
||||
case 'blue':
|
||||
return '0000ff';
|
||||
case 'sky':
|
||||
return '00ccff';
|
||||
case 'lime':
|
||||
return '00ff99';
|
||||
case 'pink':
|
||||
return 'ff66cc';
|
||||
case 'black':
|
||||
return '000000';
|
||||
default:
|
||||
return 'ffffff';
|
||||
}
|
||||
}
|
||||
|
||||
private function importBoard(): void {
|
||||
$this->board = $this->boardService->create(
|
||||
$this->data->name,
|
||||
$this->getSetting('owner')->getUID(),
|
||||
$this->getSetting('color')
|
||||
);
|
||||
}
|
||||
|
||||
private function importLabels(): void {
|
||||
$this->labels = [];
|
||||
foreach ($this->data->labels as $label) {
|
||||
if (empty($label->name)) {
|
||||
$labelTitle = 'Unnamed ' . $label->color . ' label';
|
||||
} else {
|
||||
$labelTitle = $label->name;
|
||||
}
|
||||
$newLabel = $this->labelService->create(
|
||||
$labelTitle,
|
||||
$this->translateColor($label->color),
|
||||
$this->board->getId()
|
||||
);
|
||||
$this->labels[$label->id] = $newLabel;
|
||||
}
|
||||
}
|
||||
|
||||
private function setUserId(): void {
|
||||
if (!property_exists($this->labelService, 'permissionService')) {
|
||||
return;
|
||||
}
|
||||
$propertyPermissionService = new \ReflectionProperty($this->labelService, 'permissionService');
|
||||
$propertyPermissionService->setAccessible(true);
|
||||
$permissionService = $propertyPermissionService->getValue($this->labelService);
|
||||
|
||||
if (!property_exists($permissionService, 'userId')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$propertyUserId = new \ReflectionProperty($permissionService, 'userId');
|
||||
$propertyUserId->setAccessible(true);
|
||||
$propertyUserId->setValue($permissionService, $this->getSetting('owner')->getUID());
|
||||
}
|
||||
}
|
||||
@@ -63,6 +63,9 @@ class UserExport extends Command {
|
||||
$this->groupManager = $groupManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure() {
|
||||
$this
|
||||
->setName('deck:export')
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"pattern": "^[0-9a-fA-F]{6}$"
|
||||
},
|
||||
"translations": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
77
tests/unit/Command/BoardImportTest.php
Normal file
77
tests/unit/Command/BoardImportTest.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2021 Vitor Mattos <vitor@php.rio>
|
||||
*
|
||||
* @author Vitor Mattos <vitor@php.rio>
|
||||
*
|
||||
* @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\Command;
|
||||
|
||||
use OCA\Deck\Command\Helper\TrelloHelper;
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class BoardImportTest extends \Test\TestCase {
|
||||
/** @var TrelloHelper */
|
||||
private $trelloHelper;
|
||||
/** @var BoardImport */
|
||||
private $boardImport;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->trelloHelper = $this->createMock(TrelloHelper::class);
|
||||
$this->boardImport = new BoardImport(
|
||||
$this->trelloHelper
|
||||
);
|
||||
$questionHelper = new QuestionHelper();
|
||||
$this->boardImport->setHelperSet(
|
||||
new HelperSet([
|
||||
$questionHelper
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
public function testExecuteWithSuccess() {
|
||||
$input = $this->createMock(InputInterface::class);
|
||||
|
||||
$input->method('getOption')
|
||||
->withConsecutive(
|
||||
[$this->equalTo('system')],
|
||||
[$this->equalTo('setting')],
|
||||
[$this->equalTo('data')]
|
||||
)
|
||||
->will($this->returnValueMap([
|
||||
['system', 'trello'],
|
||||
['setting', __DIR__ . '/fixtures/setting-trello.json'],
|
||||
['data', __DIR__ . '/fixtures/data-trello.json']
|
||||
]));
|
||||
$output = $this->createMock(OutputInterface::class);
|
||||
|
||||
$output
|
||||
->expects($this->once())
|
||||
->method('writeLn')
|
||||
->with('Done!');
|
||||
|
||||
$this->invokePrivate($this->boardImport, 'interact', [$input, $output]);
|
||||
$actual = $this->invokePrivate($this->boardImport, 'execute', [$input, $output]);
|
||||
$this->assertEquals(0, $actual);
|
||||
}
|
||||
}
|
||||
134
tests/unit/Command/Helper/TrelloHelperTest.php
Normal file
134
tests/unit/Command/Helper/TrelloHelperTest.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2021 Vitor Mattos <vitor@php.rio>
|
||||
*
|
||||
* @author Vitor Mattos <vitor@php.rio>
|
||||
*
|
||||
* @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\Command;
|
||||
|
||||
use OCA\Deck\Command\Helper\TrelloHelper;
|
||||
use OCA\Deck\Db\AclMapper;
|
||||
use OCA\Deck\Db\AssignmentMapper;
|
||||
use OCA\Deck\Db\CardMapper;
|
||||
use OCA\Deck\Db\StackMapper;
|
||||
use OCA\Deck\Service\BoardService;
|
||||
use OCA\Deck\Service\LabelService;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IUserManager;
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class TrelloHelperTest extends \Test\TestCase {
|
||||
/** @var BoardService */
|
||||
private $boardService;
|
||||
/** @var LabelService */
|
||||
private $labelService;
|
||||
/** @var StackMapper */
|
||||
private $stackMapper;
|
||||
/** @var CardMapper */
|
||||
private $cardMapper;
|
||||
/** @var IDBConnection */
|
||||
private $connection;
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
/** @var TrelloHelper */
|
||||
private $trelloHelper;
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->boardService = $this->createMock(BoardService::class);
|
||||
$this->labelService = $this->createMock(LabelService::class);
|
||||
$this->stackMapper = $this->createMock(StackMapper::class);
|
||||
$this->cardMapper = $this->createMock(CardMapper::class);
|
||||
$this->assignmentMapper = $this->createMock(AssignmentMapper::class);
|
||||
$this->aclMapper = $this->createMock(AclMapper::class);
|
||||
$this->connection = $this->createMock(IDBConnection::class);
|
||||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
$this->trelloHelper = new TrelloHelper(
|
||||
$this->boardService,
|
||||
$this->labelService,
|
||||
$this->stackMapper,
|
||||
$this->cardMapper,
|
||||
$this->assignmentMapper,
|
||||
$this->aclMapper,
|
||||
$this->connection,
|
||||
$this->userManager
|
||||
);
|
||||
$questionHelper = new QuestionHelper();
|
||||
$command = new BoardImport($this->trelloHelper);
|
||||
$command->setHelperSet(
|
||||
new HelperSet([
|
||||
$questionHelper
|
||||
])
|
||||
);
|
||||
$this->trelloHelper->setCommand($command);
|
||||
}
|
||||
|
||||
public function testImportWithSuccess() {
|
||||
$input = $this->createMock(InputInterface::class);
|
||||
|
||||
$input->method('getOption')
|
||||
->withConsecutive(
|
||||
[$this->equalTo('data')],
|
||||
[$this->equalTo('setting')]
|
||||
)
|
||||
->will($this->returnValueMap([
|
||||
['data', __DIR__ . '/../fixtures/data-trello.json'],
|
||||
['setting', __DIR__ . '/../fixtures/setting-trello.json']
|
||||
]));
|
||||
$output = $this->createMock(OutputInterface::class);
|
||||
|
||||
$user = $this->createMock(\OCP\IUser::class);
|
||||
$user
|
||||
->method('getUID')
|
||||
->willReturn('admin');
|
||||
$this->userManager
|
||||
->method('get')
|
||||
->willReturn($user);
|
||||
$this->userManager
|
||||
->method('get')
|
||||
->willReturn($user);
|
||||
$board = $this->createMock(\OCA\Deck\Db\Board::class);
|
||||
$this->boardService
|
||||
->expects($this->once())
|
||||
->method('create')
|
||||
->willReturn($board);
|
||||
$label = $this->createMock(\OCA\Deck\Db\Label::class);
|
||||
$this->labelService
|
||||
->expects($this->once())
|
||||
->method('create')
|
||||
->willReturn($label);
|
||||
$stack = $this->createMock(\OCA\Deck\Db\Stack::class);
|
||||
$this->stackMapper
|
||||
->expects($this->once())
|
||||
->method('insert')
|
||||
->willReturn($stack);
|
||||
$card = $this->createMock(\OCA\Deck\Db\Card::class);
|
||||
$this->cardMapper
|
||||
->expects($this->once())
|
||||
->method('insert')
|
||||
->willReturn($card);
|
||||
|
||||
$this->trelloHelper->validate($input, $output);
|
||||
$actual = $this->trelloHelper->import($input, $output);
|
||||
$this->assertNull($actual);
|
||||
}
|
||||
}
|
||||
582
tests/unit/Command/fixtures/data-trello.json
Normal file
582
tests/unit/Command/fixtures/data-trello.json
Normal file
@@ -0,0 +1,582 @@
|
||||
{
|
||||
"id": "fakeboardidhash",
|
||||
"name": "Test Board Name",
|
||||
"desc": "",
|
||||
"descData": null,
|
||||
"closed": false,
|
||||
"dateClosed": null,
|
||||
"idOrganization": null,
|
||||
"shortLink": "qwerty",
|
||||
"powerUps": [],
|
||||
"dateLastActivity": "2021-07-10T17:01:58.633Z",
|
||||
"idTags": [],
|
||||
"datePluginDisable": null,
|
||||
"creationMethod": null,
|
||||
"idBoardSource": null,
|
||||
"idMemberCreator": "fakeidmemberhash",
|
||||
"idEnterprise": null,
|
||||
"pinned": false,
|
||||
"starred": false,
|
||||
"url": "https://trello.com/b/qwerty/fakeboardurl",
|
||||
"prefs": {
|
||||
"permissionLevel": "private",
|
||||
"hideVotes": false,
|
||||
"voting": "disabled",
|
||||
"comments": "members",
|
||||
"invitations": "members",
|
||||
"selfJoin": false,
|
||||
"cardCovers": true,
|
||||
"isTemplate": false,
|
||||
"cardAging": "regular",
|
||||
"calendarFeedEnabled": false,
|
||||
"background": "blue",
|
||||
"backgroundImage": null,
|
||||
"backgroundImageScaled": null,
|
||||
"backgroundTile": false,
|
||||
"backgroundBrightness": "dark",
|
||||
"backgroundColor": "#0079BF",
|
||||
"backgroundBottomColor": "#0079BF",
|
||||
"backgroundTopColor": "#0079BF",
|
||||
"canBePublic": true,
|
||||
"canBeEnterprise": true,
|
||||
"canBeOrg": true,
|
||||
"canBePrivate": true,
|
||||
"canInvite": true
|
||||
},
|
||||
"shortUrl": "https://trello.com/b/qwerty",
|
||||
"premiumFeatures": [],
|
||||
"enterpriseOwned": false,
|
||||
"ixUpdate": "67",
|
||||
"limits": {
|
||||
"attachments": {
|
||||
"perBoard": {
|
||||
"status": "ok",
|
||||
"disableAt": 36000,
|
||||
"warnAt": 32400
|
||||
},
|
||||
"perCard": {
|
||||
"status": "ok",
|
||||
"disableAt": 1000,
|
||||
"warnAt": 900
|
||||
}
|
||||
},
|
||||
"boards": {
|
||||
"totalMembersPerBoard": {
|
||||
"status": "ok",
|
||||
"disableAt": 1600,
|
||||
"warnAt": 1440
|
||||
}
|
||||
},
|
||||
"cards": {
|
||||
"openPerBoard": {
|
||||
"status": "ok",
|
||||
"disableAt": 5000,
|
||||
"warnAt": 4500
|
||||
},
|
||||
"openPerList": {
|
||||
"status": "ok",
|
||||
"disableAt": 5000,
|
||||
"warnAt": 4500
|
||||
},
|
||||
"totalPerBoard": {
|
||||
"status": "ok",
|
||||
"disableAt": 2000000,
|
||||
"warnAt": 1800000
|
||||
},
|
||||
"totalPerList": {
|
||||
"status": "ok",
|
||||
"disableAt": 1000000,
|
||||
"warnAt": 900000
|
||||
}
|
||||
},
|
||||
"checklists": {
|
||||
"perBoard": {
|
||||
"status": "ok",
|
||||
"disableAt": 2000000,
|
||||
"warnAt": 1800000
|
||||
},
|
||||
"perCard": {
|
||||
"status": "ok",
|
||||
"disableAt": 500,
|
||||
"warnAt": 450
|
||||
}
|
||||
},
|
||||
"checkItems": {
|
||||
"perChecklist": {
|
||||
"status": "ok",
|
||||
"disableAt": 200,
|
||||
"warnAt": 180
|
||||
}
|
||||
},
|
||||
"customFields": {
|
||||
"perBoard": {
|
||||
"status": "ok",
|
||||
"disableAt": 50,
|
||||
"warnAt": 45
|
||||
}
|
||||
},
|
||||
"customFieldOptions": {
|
||||
"perField": {
|
||||
"status": "ok",
|
||||
"disableAt": 50,
|
||||
"warnAt": 45
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"perBoard": {
|
||||
"status": "ok",
|
||||
"disableAt": 1000,
|
||||
"warnAt": 900
|
||||
}
|
||||
},
|
||||
"lists": {
|
||||
"openPerBoard": {
|
||||
"status": "ok",
|
||||
"disableAt": 500,
|
||||
"warnAt": 450
|
||||
},
|
||||
"totalPerBoard": {
|
||||
"status": "ok",
|
||||
"disableAt": 3000,
|
||||
"warnAt": 2700
|
||||
}
|
||||
},
|
||||
"stickers": {
|
||||
"perCard": {
|
||||
"status": "ok",
|
||||
"disableAt": 70,
|
||||
"warnAt": 63
|
||||
}
|
||||
},
|
||||
"reactions": {
|
||||
"perAction": {
|
||||
"status": "ok",
|
||||
"disableAt": 1000,
|
||||
"warnAt": 900
|
||||
},
|
||||
"uniquePerAction": {
|
||||
"status": "ok",
|
||||
"disableAt": 17,
|
||||
"warnAt": 16
|
||||
}
|
||||
}
|
||||
},
|
||||
"subscribed": false,
|
||||
"templateGallery": null,
|
||||
"dateLastView": "2021-07-10T17:01:58.665Z",
|
||||
"labelNames": {
|
||||
"green": "",
|
||||
"yellow": "",
|
||||
"orange": "",
|
||||
"red": "",
|
||||
"purple": "",
|
||||
"blue": "",
|
||||
"sky": "",
|
||||
"lime": "",
|
||||
"pink": "",
|
||||
"black": ""
|
||||
},
|
||||
"actions": [
|
||||
{
|
||||
"id": "60e9d2869efe2e1141be2798",
|
||||
"idMemberCreator": "fakeidmemberhash",
|
||||
"data": {
|
||||
"idMember": "fakeidmemberhash",
|
||||
"deactivated": false,
|
||||
"card": {
|
||||
"id": "hashcard7",
|
||||
"name": "Name Card 7",
|
||||
"idShort": 7,
|
||||
"shortLink": "fakeshortlinkcard7"
|
||||
},
|
||||
"board": {
|
||||
"id": "fakeboardidhash",
|
||||
"name": "Test Board Name",
|
||||
"shortLink": "qwerty"
|
||||
},
|
||||
"member": {
|
||||
"id": "fakeidmemberhash",
|
||||
"name": "John Doe"
|
||||
}
|
||||
},
|
||||
"type": "removeMemberFromCard",
|
||||
"date": "2021-07-10T17:01:58.636Z",
|
||||
"appCreator": null,
|
||||
"limits": {},
|
||||
"member": {
|
||||
"id": "fakeidmemberhash",
|
||||
"username": "johndoe",
|
||||
"activityBlocked": false,
|
||||
"avatarHash": "fakeavatarhash",
|
||||
"avatarUrl": "https://trello-members.s3.amazonaws.com/fakeidmemberhash/fakeavatarhash",
|
||||
"fullName": "John Doe",
|
||||
"idMemberReferrer": null,
|
||||
"initials": "JD",
|
||||
"nonPublic": {
|
||||
"fullName": "John Doe",
|
||||
"initials": "JD",
|
||||
"avatarUrl": "https://trello-members.s3.amazonaws.com/fakeidmemberhash/fakeavatarhash",
|
||||
"avatarHash": "fakeavatarhash"
|
||||
},
|
||||
"nonPublicAvailable": true
|
||||
},
|
||||
"memberCreator": {
|
||||
"id": "fakeidmemberhash",
|
||||
"username": "johndoe",
|
||||
"activityBlocked": false,
|
||||
"avatarHash": "fakeavatarhash",
|
||||
"avatarUrl": "https://trello-members.s3.amazonaws.com/fakeidmemberhash/fakeavatarhash",
|
||||
"fullName": "John Doe",
|
||||
"idMemberReferrer": null,
|
||||
"initials": "JD",
|
||||
"nonPublic": {
|
||||
"fullName": "John Doe",
|
||||
"initials": "JD",
|
||||
"avatarUrl": "https://trello-members.s3.amazonaws.com/fakeidmemberhash/fakeavatarhash",
|
||||
"avatarHash": "fakeavatarhash"
|
||||
},
|
||||
"nonPublicAvailable": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "60e9d1832ff82d10c0cea6ba",
|
||||
"idMemberCreator": "fakeidmemberhash",
|
||||
"data": {
|
||||
"idMember": "fakeidmemberhash",
|
||||
"card": {
|
||||
"id": "hashcard7",
|
||||
"name": "Name Card 7",
|
||||
"idShort": 7,
|
||||
"shortLink": "fakeshortlinkcard7"
|
||||
},
|
||||
"board": {
|
||||
"id": "fakeboardidhash",
|
||||
"name": "Test Board Name",
|
||||
"shortLink": "qwerty"
|
||||
},
|
||||
"member": {
|
||||
"id": "fakeidmemberhash",
|
||||
"name": "John Doe"
|
||||
}
|
||||
},
|
||||
"type": "addMemberToCard",
|
||||
"date": "2021-07-10T16:57:39.999Z",
|
||||
"appCreator": null,
|
||||
"limits": {},
|
||||
"member": {
|
||||
"id": "fakeidmemberhash",
|
||||
"username": "johndoe",
|
||||
"activityBlocked": false,
|
||||
"avatarHash": "fakeavatarhash",
|
||||
"avatarUrl": "https://trello-members.s3.amazonaws.com/fakeidmemberhash/fakeavatarhash",
|
||||
"fullName": "John Doe",
|
||||
"idMemberReferrer": null,
|
||||
"initials": "JD",
|
||||
"nonPublic": {
|
||||
"fullName": "John Doe",
|
||||
"initials": "JD",
|
||||
"avatarUrl": "https://trello-members.s3.amazonaws.com/fakeidmemberhash/fakeavatarhash",
|
||||
"avatarHash": "fakeavatarhash"
|
||||
},
|
||||
"nonPublicAvailable": true
|
||||
},
|
||||
"memberCreator": {
|
||||
"id": "fakeidmemberhash",
|
||||
"username": "johndoe",
|
||||
"activityBlocked": false,
|
||||
"avatarHash": "fakeavatarhash",
|
||||
"avatarUrl": "https://trello-members.s3.amazonaws.com/fakeidmemberhash/fakeavatarhash",
|
||||
"fullName": "John Doe",
|
||||
"idMemberReferrer": null,
|
||||
"initials": "JD",
|
||||
"nonPublic": {
|
||||
"fullName": "John Doe",
|
||||
"initials": "JD",
|
||||
"avatarUrl": "https://trello-members.s3.amazonaws.com/fakeidmemberhash/fakeavatarhash",
|
||||
"avatarHash": "fakeavatarhash"
|
||||
},
|
||||
"nonPublicAvailable": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "59bbfc4bf36aa0270d6bfd43",
|
||||
"idMemberCreator": "fakeidmemberhash",
|
||||
"data": {
|
||||
"board": {
|
||||
"shortLink": "qwerty",
|
||||
"name": "Test Board Name",
|
||||
"id": "fakeboardidhash"
|
||||
},
|
||||
"list": {
|
||||
"name": "TODO",
|
||||
"id": "hashlisttodo"
|
||||
},
|
||||
"card": {
|
||||
"shortLink": "fakeshortlinkcard7",
|
||||
"idShort": 7,
|
||||
"name": "Name Card 7",
|
||||
"id": "hashcard7"
|
||||
}
|
||||
},
|
||||
"type": "createCard",
|
||||
"date": "2017-09-15T16:14:03.187Z",
|
||||
"appCreator": null,
|
||||
"limits": {},
|
||||
"memberCreator": {
|
||||
"id": "fakeidmemberhash",
|
||||
"username": "johndoe",
|
||||
"activityBlocked": false,
|
||||
"avatarHash": "fakeavatarhash",
|
||||
"avatarUrl": "https://trello-members.s3.amazonaws.com/fakeidmemberhash/fakeavatarhash",
|
||||
"fullName": "John Doe",
|
||||
"idMemberReferrer": null,
|
||||
"initials": "JD",
|
||||
"nonPublic": {
|
||||
"fullName": "John Doe",
|
||||
"initials": "JD",
|
||||
"avatarUrl": "https://trello-members.s3.amazonaws.com/fakeidmemberhash/fakeavatarhash",
|
||||
"avatarHash": "fakeavatarhash"
|
||||
},
|
||||
"nonPublicAvailable": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "59bbfb8e4a6f8ca35be9b82a",
|
||||
"idMemberCreator": "fakeidmemberhash",
|
||||
"data": {
|
||||
"board": {
|
||||
"shortLink": "qwerty",
|
||||
"name": "Test Board Name",
|
||||
"id": "fakeboardidhash"
|
||||
},
|
||||
"list": {
|
||||
"name": "TODO",
|
||||
"id": "hashlisttodo"
|
||||
}
|
||||
},
|
||||
"type": "createList",
|
||||
"date": "2017-09-15T16:10:54.714Z",
|
||||
"appCreator": null,
|
||||
"limits": {},
|
||||
"memberCreator": {
|
||||
"id": "fakeidmemberhash",
|
||||
"username": "johndoe",
|
||||
"activityBlocked": false,
|
||||
"avatarHash": "fakeavatarhash",
|
||||
"avatarUrl": "https://trello-members.s3.amazonaws.com/fakeidmemberhash/fakeavatarhash",
|
||||
"fullName": "John Doe",
|
||||
"idMemberReferrer": null,
|
||||
"initials": "JD",
|
||||
"nonPublic": {
|
||||
"fullName": "John Doe",
|
||||
"initials": "JD",
|
||||
"avatarUrl": "https://trello-members.s3.amazonaws.com/fakeidmemberhash/fakeavatarhash",
|
||||
"avatarHash": "fakeavatarhash"
|
||||
},
|
||||
"nonPublicAvailable": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "59bbfb88973b76e586edec5e",
|
||||
"idMemberCreator": "fakeidmemberhash",
|
||||
"data": {
|
||||
"board": {
|
||||
"shortLink": "qwerty",
|
||||
"name": "Test Board Name",
|
||||
"id": "fakeboardidhash"
|
||||
}
|
||||
},
|
||||
"type": "createBoard",
|
||||
"date": "2017-09-15T16:10:48.069Z",
|
||||
"appCreator": null,
|
||||
"limits": {},
|
||||
"memberCreator": {
|
||||
"id": "fakeidmemberhash",
|
||||
"username": "johndoe",
|
||||
"activityBlocked": false,
|
||||
"avatarHash": "fakeavatarhash",
|
||||
"avatarUrl": "https://trello-members.s3.amazonaws.com/fakeidmemberhash/fakeavatarhash",
|
||||
"fullName": "John Doe",
|
||||
"idMemberReferrer": null,
|
||||
"initials": "JD",
|
||||
"nonPublic": {
|
||||
"fullName": "John Doe",
|
||||
"initials": "JD",
|
||||
"avatarUrl": "https://trello-members.s3.amazonaws.com/fakeidmemberhash/fakeavatarhash",
|
||||
"avatarHash": "fakeavatarhash"
|
||||
},
|
||||
"nonPublicAvailable": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"cards": [
|
||||
{
|
||||
"id": "hashcard7",
|
||||
"address": null,
|
||||
"checkItemStates": null,
|
||||
"closed": false,
|
||||
"coordinates": null,
|
||||
"creationMethod": null,
|
||||
"dateLastActivity": "2021-07-10T17:01:58.633Z",
|
||||
"desc": "",
|
||||
"descData": null,
|
||||
"dueReminder": null,
|
||||
"idBoard": "fakeboardidhash",
|
||||
"idLabels": [],
|
||||
"idList": "hashlisttodo",
|
||||
"idMembersVoted": [],
|
||||
"idShort": 7,
|
||||
"idAttachmentCover": null,
|
||||
"locationName": null,
|
||||
"manualCoverAttachment": false,
|
||||
"name": "Name Card 7",
|
||||
"pos": 65535,
|
||||
"shortLink": "fakeshortlinkcard7",
|
||||
"isTemplate": false,
|
||||
"cardRole": null,
|
||||
"badges": {
|
||||
"attachmentsByType": {
|
||||
"trello": {
|
||||
"board": 0,
|
||||
"card": 0
|
||||
}
|
||||
},
|
||||
"location": false,
|
||||
"votes": 0,
|
||||
"viewingMemberVoted": false,
|
||||
"subscribed": false,
|
||||
"fogbugz": "",
|
||||
"checkItems": 0,
|
||||
"checkItemsChecked": 0,
|
||||
"checkItemsEarliestDue": null,
|
||||
"comments": 0,
|
||||
"attachments": 0,
|
||||
"description": false,
|
||||
"due": null,
|
||||
"dueComplete": false,
|
||||
"start": null
|
||||
},
|
||||
"dueComplete": false,
|
||||
"due": null,
|
||||
"email": "johndoe+card7@boards.trello.com",
|
||||
"idChecklists": [],
|
||||
"idMembers": [],
|
||||
"labels": [],
|
||||
"limits": {
|
||||
"attachments": {
|
||||
"perCard": {
|
||||
"status": "ok",
|
||||
"disableAt": 1000,
|
||||
"warnAt": 900
|
||||
}
|
||||
},
|
||||
"checklists": {
|
||||
"perCard": {
|
||||
"status": "ok",
|
||||
"disableAt": 500,
|
||||
"warnAt": 450
|
||||
}
|
||||
},
|
||||
"stickers": {
|
||||
"perCard": {
|
||||
"status": "ok",
|
||||
"disableAt": 70,
|
||||
"warnAt": 63
|
||||
}
|
||||
}
|
||||
},
|
||||
"shortUrl": "https://trello.com/c/fakeshortlinkcard7",
|
||||
"start": null,
|
||||
"subscribed": false,
|
||||
"url": "https://trello.com/c/fakeshortlinkcard7/7-name-card-7",
|
||||
"cover": {
|
||||
"idAttachment": null,
|
||||
"color": null,
|
||||
"idUploadedBackground": null,
|
||||
"size": "normal",
|
||||
"brightness": "dark",
|
||||
"idPlugin": null
|
||||
},
|
||||
"attachments": [],
|
||||
"pluginData": [],
|
||||
"customFieldItems": []
|
||||
}
|
||||
],
|
||||
"labels": [
|
||||
{
|
||||
"id": "59bbfb881314a339999eb855",
|
||||
"idBoard": "fakeboardidhash",
|
||||
"name": "",
|
||||
"color": "yellow"
|
||||
}
|
||||
],
|
||||
"lists": [
|
||||
{
|
||||
"id": "hashlisttodo",
|
||||
"name": "TODO",
|
||||
"closed": false,
|
||||
"pos": 65535,
|
||||
"softLimit": null,
|
||||
"creationMethod": null,
|
||||
"idBoard": "fakeboardidhash",
|
||||
"limits": {
|
||||
"cards": {
|
||||
"openPerList": {
|
||||
"status": "ok",
|
||||
"disableAt": 5000,
|
||||
"warnAt": 4500
|
||||
},
|
||||
"totalPerList": {
|
||||
"status": "ok",
|
||||
"disableAt": 1000000,
|
||||
"warnAt": 900000
|
||||
}
|
||||
}
|
||||
},
|
||||
"subscribed": false
|
||||
}
|
||||
],
|
||||
"members": [
|
||||
{
|
||||
"id": "fakeidmemberhash",
|
||||
"bio": "",
|
||||
"bioData": {
|
||||
"emoji": {}
|
||||
},
|
||||
"confirmed": true,
|
||||
"memberType": "normal",
|
||||
"username": "johndoe",
|
||||
"activityBlocked": false,
|
||||
"avatarHash": "fakeavatarhash",
|
||||
"avatarUrl": "https://trello-members.s3.amazonaws.com/fakeidmemberhash/fakeavatarhash",
|
||||
"fullName": "John Doe",
|
||||
"idEnterprise": null,
|
||||
"idEnterprisesDeactivated": [],
|
||||
"idMemberReferrer": null,
|
||||
"idPremOrgsAdmin": [],
|
||||
"initials": "JD",
|
||||
"nonPublic": {
|
||||
"fullName": "John Doe",
|
||||
"initials": "JD",
|
||||
"avatarUrl": "https://trello-members.s3.amazonaws.com/fakeidmemberhash/fakeavatarhash",
|
||||
"avatarHash": "fakeavatarhash"
|
||||
},
|
||||
"nonPublicAvailable": true,
|
||||
"products": [],
|
||||
"url": "https://trello.com/johndoe",
|
||||
"status": "disconnected"
|
||||
}
|
||||
],
|
||||
"checklists": [],
|
||||
"customFields": [],
|
||||
"memberships": [
|
||||
{
|
||||
"id": "59bbfb88973b76e586edec5d",
|
||||
"idMember": "fakeidmemberhash",
|
||||
"memberType": "admin",
|
||||
"unconfirmed": false,
|
||||
"deactivated": false
|
||||
}
|
||||
],
|
||||
"pluginData": []
|
||||
}
|
||||
7
tests/unit/Command/fixtures/setting-trello.json
Normal file
7
tests/unit/Command/fixtures/setting-trello.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"owner": "admin",
|
||||
"color": "0800fd",
|
||||
"uidRelation": {
|
||||
"johndoe": "admin"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user