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:
Vitor Mattos
2021-07-09 23:35:56 -03:00
committed by Julius Härtl
parent 89028c74cb
commit 48df98ce67
13 changed files with 1513 additions and 355 deletions

View File

@@ -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;
}
}

View 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);
}
}
}

View 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;
}

View 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());
}
}

View File

@@ -63,6 +63,9 @@ class UserExport extends Command {
$this->groupManager = $groupManager;
}
/**
* @return void
*/
protected function configure() {
$this
->setName('deck:export')

View File

@@ -12,6 +12,9 @@
"type": "string",
"required": true,
"pattern": "^[0-9a-fA-F]{6}$"
},
"translations": {
"type": "object"
}
}
}