Import participants

Big refactor to create route
Import participants

Signed-off-by: Vitor Mattos <vitor@php.rio>
This commit is contained in:
Vitor Mattos
2021-07-14 23:49:31 -03:00
committed by Julius Härtl
parent fd92fc3c4d
commit c5d10dafb8
18 changed files with 868 additions and 521 deletions

View File

@@ -0,0 +1,53 @@
<?php
namespace OCA\Deck\Service;
use OCA\Deck\Db\Acl;
use OCA\Deck\Db\Board;
use OCA\Deck\Db\Card;
use OCA\Deck\Db\Stack;
abstract class ABoardImportService {
/** @var BoardImportService */
private $boardImportService;
abstract public function getBoard(): ?Board;
/**
* @return Acl[]
*/
abstract public function getAclList(): array;
/**
* @return Stack[]
*/
abstract function getStacks(): array;
/**
* @return Card[]
*/
abstract function getCards(): array;
abstract function updateStack(string $id, Stack $stack): self;
abstract function updateCard(string $id, Card $card): self;
abstract function assignCardsToLabels(): self;
abstract function importParticipants(): self;
abstract function importComments(): self;
abstract public function importLabels(): self;
abstract public function validateUsers(): self;
public function setImportService($service): self {
$this->boardImportService = $service;
return $this;
}
public function getImportService(): BoardImportService {
return $this->boardImportService;
}
}

View File

@@ -1,49 +0,0 @@
<?php
namespace OCA\Deck\Service;
abstract class AImportService {
/**
* Data object created from config JSON
*
* @var \stdClass
*/
public $config;
public function setConfigInstance(\stdClass $config) {
$this->config = $config;
}
/**
* Define a config
*
* @param string $configName
* @param mixed $value
* @return void
*/
public function setConfig(string $configName, $value): void {
if (!$this->config) {
$this->setConfigInstance(new \stdClass);
}
$this->config->$configName = $value;
}
/**
* Get a config
*
* @param string $configName config name
* @return mixed
*/
public function getConfig(string $configName = null) {
if (!is_object($this->config)) {
return;
}
if (!$configName) {
return $this->config;
}
if (!property_exists($this->config, $configName)) {
return;
}
return $this->config->$configName;
}
}

View File

@@ -0,0 +1,180 @@
<?php
namespace OCA\Deck\Service;
use JsonSchema\Constraints\Constraint;
use JsonSchema\Validator;
use OCA\Deck\Command\BoardImport;
use OCA\Deck\Service\BoardImportService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
class BoardImportCommandService extends BoardImportService {
/** @var Command */
private $command;
/** @var InputInterface */
private $input;
/** @var OutputInterface */
private $output;
/**
* Data object created from config JSON
*
* @var \StdClass
*/
public $config;
/**
* Define Command instance
*
* @param Command $command
* @return void
*/
public function setCommand(Command $command): void {
$this->command = $command;
}
/**
* @return BoardImport
*/
public function getCommand() {
return $this->command;
}
public function setInput($input): self {
$this->input = $input;
return $this;
}
public function getInput(): InputInterface {
return $this->input;
}
public function setOutput($output): self {
$this->output = $output;
return $this;
}
public function getOutput(): OutputInterface {
return $this->output;
}
public function validate(): self {
$this->validateSystem();
$this->validateConfig();
$this->validateData();
return $this;
}
private function validateConfig(): void {
$configFile = $this->getInput()->getOption('config');
if (!is_file($configFile)) {
$helper = $this->getCommand()->getHelper('question');
$question = new Question(
'Please inform a valid config json file: ',
'config.json'
);
$question->setValidator(function ($answer) {
if (!is_file($answer)) {
throw new \RuntimeException(
'config file not found'
);
}
return $answer;
});
$configFile = $helper->ask($this->getInput(), $this->getOutput(), $question);
$this->getInput()->setOption('config', $configFile);
}
$config = json_decode(file_get_contents($configFile));
$system = $this->getSystem();
$schemaPath = __DIR__ . '/fixtures/config-' . $system . '-schema.json';
$validator = new Validator();
$validator->validate(
$config,
(object)['$ref' => 'file://' . realpath($schemaPath)],
Constraint::CHECK_MODE_APPLY_DEFAULTS
);
if (!$validator->isValid()) {
$this->getOutput()->writeln('<error>Invalid config file</error>');
$this->getOutput()->writeln(array_map(function ($v) {
return $v['message'];
}, $validator->getErrors()));
$this->getOutput()->writeln('Valid schema:');
$this->getOutput()->writeln(print_r(file_get_contents($schemaPath), true));
$this->getInput()->setOption('config', null);
$this->validateConfig($this->getInput(), $this->getOutput());
}
$this->setConfigInstance($config);
$this->validateOwner();
}
/**
* @return void
*/
private function validateSystem(): self {
$system = $this->getInput()->getOption('system');
if (in_array($system, $this->getAllowedImportSystems())) {
return $this->setSystem($system);
}
$helper = $this->getCommand()->getHelper('question');
$question = new ChoiceQuestion(
'Please inform a source system',
$this->allowedSystems,
0
);
$question->setErrorMessage('System %s is invalid.');
$system = $helper->ask($this->getInput(), $this->getOutput(), $question);
$this->getInput()->setOption('system', $system);
return $this->setSystem($system);
}
private function validateData(): self {
$filename = $this->getInput()->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($this->getInput(), $this->getOutput(), $question);
$this->getInput()->setOption('data', $data);
}
$this->setData(json_decode(file_get_contents($filename)));
if (!$this->getData()) {
$this->getOutput()->writeln('<error>Is not a json file: ' . $filename . '</error>');
$this->validateData($this->getInput(), $this->getOutput());
}
$this->validateUsers();
return $this;
}
public function import(): void {
$this->getOutput()->writeln('Importing board...');
$this->importBoard();
$this->getOutput()->writeln('Assign users to board...');
$this->importAcl();
$this->getOutput()->writeln('Importing labels...');
$this->importLabels();
$this->getOutput()->writeln('Importing stacks...');
$this->importStacks();
$this->getOutput()->writeln('Importing cards...');
$this->importCards();
$this->getOutput()->writeln('Assign cards to labels...');
$this->assignCardsToLabels();
$this->getOutput()->writeln('Iporting comments...');
$this->importComments();
$this->getOutput()->writeln('Iporting participants...');
$this->importParticipants();
}
}

View File

@@ -0,0 +1,363 @@
<?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\Service;
use JsonSchema\Constraints\Constraint;
use JsonSchema\Validator;
use OCA\Deck\AppInfo\Application;
use OCA\Deck\BadRequestException;
use OCA\Deck\Db\AclMapper;
use OCA\Deck\Db\Board;
use OCA\Deck\Db\BoardMapper;
use OCA\Deck\Db\Card;
use OCA\Deck\Db\CardMapper;
use OCA\Deck\Db\Label;
use OCA\Deck\Db\LabelMapper;
use OCA\Deck\Db\StackMapper;
use OCA\Deck\NotFoundException;
use OCP\Comments\IComment;
use OCP\Comments\ICommentsManager;
use OCP\Comments\MessageTooLongException;
use OCP\Comments\NotFoundException as CommentNotFoundException;
use OCP\IDBConnection;
use OCP\IUserManager;
class BoardImportService {
/** @var IDBConnection */
protected $dbConn;
/** @var IUserManager */
private $userManager;
/** @var BoardMapper */
private $boardMapper;
/** @var AclMapper */
private $aclMapper;
/** @var LabelMapper */
private $labelMapper;
/** @var StackMapper */
private $stackMapper;
/** @var CardMapper */
private $cardMapper;
/** @var ICommentsManager */
private $commentsManager;
/** @var string */
private $system;
/** @var ABoardImportService */
private $systemInstance;
/** @var string[] */
private $allowedSystems;
/**
* Data object created from config JSON
*
* @var \stdClass
*/
public $config;
/**
* Data object created from JSON of origin system
*
* @var \stdClass
*/
private $data;
/** @var Board */
private $board;
public function __construct(
IDBConnection $dbConn,
IUserManager $userManager,
BoardMapper $boardMapper,
AclMapper $aclMapper,
LabelMapper $labelMapper,
StackMapper $stackMapper,
CardMapper $cardMapper,
ICommentsManager $commentsManager
) {
$this->dbConn = $dbConn;
$this->userManager = $userManager;
$this->boardMapper = $boardMapper;
$this->aclMapper = $aclMapper;
$this->labelMapper = $labelMapper;
$this->stackMapper = $stackMapper;
$this->cardMapper = $cardMapper;
$this->commentsManager = $commentsManager;
}
public function import(): void {
$this->validate();
$schemaPath = __DIR__ . '/fixtures/config-' . $system . '-schema.json';
$validator = new Validator();
$validator->validate(
$config,
(object)['$ref' => 'file://' . realpath($schemaPath)],
Constraint::CHECK_MODE_APPLY_DEFAULTS
);
if (!$validator->isValid()) {
throw new BadRequestException('invalid config');
}
if (empty($data)) {
throw new BadRequestException('data must be provided');
}
$this->getImportService()->setData($data);
$this->getImportService()->import();
// return $newBoard;
}
public function validate(): self {
if (is_string($system) === false) {
throw new BadRequestException('system must be provided');
}
if (!in_array($system, $this->getAllowedImportSystems())) {
throw new BadRequestException('not allowed system');
}
if (empty($config)) {
throw new BadRequestException('config must be provided');
}
return $this;
}
public function setSystem(string $system): self {
$this->system = $system;
return $this;
}
public function getSystem() {
return $this->system;
}
public function getAllowedImportSystems(): array {
if (!$this->allowedSystems) {
$allowedSystems = glob(__DIR__ . '/BoardImport*Service.php');
$allowedSystems = array_filter($allowedSystems, function($name) {
$name = basename($name);
switch($name) {
case 'ABoardImportService.php':
case 'BoardImportService.php':
case 'BoardImportCommandService.php':
return false;
}
return true;
});
$this->allowedSystems = array_map(function ($name) {
preg_match('/\/BoardImport(?<system>\w+)Service\.php$/', $name, $matches);
return lcfirst($matches['system']);
}, $allowedSystems);
}
return $this->allowedSystems;
}
public function getImportSystem(): ABoardImportService {
$systemClass = 'OCA\\Deck\\Service\\BoardImport' . ucfirst($this->getSystem()) . 'Service';
if (!is_object($this->systemInstance)) {
$this->systemInstance = \OC::$server->get($systemClass);
$this->systemInstance->setImportService($this);
}
return $this->systemInstance;
}
public function importBoard() {
$board = $this->getImportSystem()->getBoard();
if ($board) {
$this->boardMapper->insert($board);
$this->board = $board;
}
return $this;
}
public function getBoard(): Board {
return $this->board;
}
public function importAcl(): self {
$aclList = $this->getImportSystem()->getAclList();
foreach ($aclList as $acl) {
$this->aclMapper->insert($acl);
}
return $this;
}
public function importLabels(): self {
$this->getImportSystem()->importLabels();
return $this;
}
public function createLabel($title, $color, $boardId): Label {
$label = new Label();
$label->setTitle($title);
$label->setColor($color);
$label->setBoardId($boardId);
return $this->labelMapper->insert($label);
}
public function importStacks(): self {
$stack = $this->getImportSystem()->getStacks();
foreach ($stack as $code => $stack) {
$this->stackMapper->insert($stack);
$this->getImportSystem()->updateStack($code, $stack);
}
return $this;
}
public function importCards(): self {
$cards = $this->getImportSystem()->getCards();
foreach ($cards as $code => $card) {
$this->cardMapper->insert($card);
$this->getImportSystem()->updateCard($code, $card);
}
return $this;
}
public function assignCardToLabel($cardId, $labelId): self {
$this->cardMapper->assignLabel(
$cardId,
$labelId
);
return $this;
}
public function assignCardsToLabels(): self {
$this->getImportSystem()->assignCardsToLabels();
return $this;
}
public function importComments(): self {
$this->getImportSystem()->importComments();
return $this;
}
public function insertComment($cardId, IComment $comment): IComment {
$comment->setObject('deckCard', (string) $cardId);
$comment->setVerb('comment');
// Check if parent is a comment on the same card
if ($comment->getParentId() !== '0') {
try {
$comment = $this->commentsManager->get($comment->getParentId());
if ($comment->getObjectType() !== Application::COMMENT_ENTITY_TYPE || $comment->getObjectId() !== $cardId) {
throw new CommentNotFoundException();
}
} catch (CommentNotFoundException $e) {
throw new BadRequestException('Invalid parent id: The parent comment was not found or belongs to a different card');
}
}
try {
$qb = $this->dbConn->getQueryBuilder();
$values = [
'parent_id' => $qb->createNamedParameter($comment->getParentId()),
'topmost_parent_id' => $qb->createNamedParameter($comment->getTopmostParentId()),
'children_count' => $qb->createNamedParameter($comment->getChildrenCount()),
'actor_type' => $qb->createNamedParameter($comment->getActorType()),
'actor_id' => $qb->createNamedParameter($comment->getActorId()),
'message' => $qb->createNamedParameter($comment->getMessage()),
'verb' => $qb->createNamedParameter($comment->getVerb()),
'creation_timestamp' => $qb->createNamedParameter($comment->getCreationDateTime(), 'datetime'),
'latest_child_timestamp' => $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'),
'object_type' => $qb->createNamedParameter($comment->getObjectType()),
'object_id' => $qb->createNamedParameter($comment->getObjectId()),
'reference_id' => $qb->createNamedParameter($comment->getReferenceId())
];
$affectedRows = $qb->insert('comments')
->values($values)
->execute();
if ($affectedRows > 0) {
$comment->setId((string)$qb->getLastInsertId());
}
return $comment;
} catch (\InvalidArgumentException $e) {
throw new BadRequestException('Invalid input values');
} catch (CommentNotFoundException $e) {
throw new NotFoundException('Could not create comment.');
}
}
public function importParticipants() {
$this->getImportSystem()->importParticipants();
}
public function setData(\stdClass $data): self {
$this->data = $data;
return $this;
}
public function getData() {
return $this->data;
}
/**
* Define a config
*
* @param string $configName
* @param mixed $value
* @return self
*/
public function setConfig(string $configName, $value): self {
if (!$this->config) {
$this->setConfigInstance(new \stdClass);
}
$this->config->$configName = $value;
return $this;
}
/**
* Get a config
*
* @param string $configName config name
* @return mixed
*/
public function getConfig(string $configName = null) {
if (!is_object($this->config)) {
return;
}
if (!$configName) {
return $this->config;
}
if (!property_exists($this->config, $configName)) {
return;
}
return $this->config->$configName;
}
public function setConfigInstance(\stdClass $config): self {
$this->config = $config;
return $this;
}
public function validateOwner(): self {
$owner = $this->userManager->get($this->getConfig('owner'));
if (!$owner) {
throw new \LogicException('Owner "' . $this->getConfigboardImportService->getConfig('owner')->getUID() . '" not found on Nextcloud. Check setting json.');
}
$this->setConfig('owner', $owner);
return $this;
}
public function validateUsers(): self {
$this->getImportSystem()->validateUsers();
return $this;
}
}

View File

@@ -23,6 +23,7 @@
namespace OCA\Deck\Service;
use OC\Comments\Comment;
use OCA\Deck\Db\Acl;
use OCA\Deck\Db\AclMapper;
use OCA\Deck\Db\Assignment;
@@ -38,9 +39,7 @@ use OCP\IL10N;
use OCP\IUser;
use OCP\IUserManager;
class TrelloImportService extends AImportService {
/** @var BoardService */
private $boardService;
class BoardImportTrelloService extends ABoardImportService {
/** @var LabelService */
private $labelService;
/** @var StackMapper */
@@ -57,8 +56,6 @@ class TrelloImportService extends AImportService {
private $userManager;
/** @var IL10N */
private $l10n;
/** @var Board */
private $board;
/**
* Array of stacks
*
@@ -75,12 +72,6 @@ class TrelloImportService extends AImportService {
private $cards = [];
/** @var IUser[] */
private $members = [];
/**
* Data object created from JSON of origin system
*
* @var \StdClass
*/
private $data;
public function __construct(
BoardService $boardService,
@@ -104,31 +95,21 @@ class TrelloImportService extends AImportService {
$this->l10n = $l10n;
}
public function setData(\stdClass $data) {
$this->data = $data;
}
public function getData() {
return $this->data;
}
public function validateOwner(): void {
$owner = $this->userManager->get($this->getConfig('owner'));
if (!$owner) {
throw new \LogicException('Owner "' . $this->getConfig('owner')->getUID() . '" not found on Nextcloud. Check setting json.');
}
$this->setConfig('owner', $owner);
public function validate(): ABoardImportService {
$this->boardImportTrelloService->validateOwner();
$this->boardImportTrelloService->validateUsers();
return $this;
}
/**
* @return void
* @return ABoardImportService
*/
public function validateUsers() {
if (empty($this->getConfig('uidRelation'))) {
return;
public function validateUsers(): self {
if (empty($this->getImportService()->getConfig('uidRelation'))) {
return $this;
}
foreach ($this->getConfig('uidRelation') as $trelloUid => $nextcloudUid) {
$user = array_filter($this->data->members, function ($u) use ($trelloUid) {
foreach ($this->getImportService()->getConfig('uidRelation') as $trelloUid => $nextcloudUid) {
$user = array_filter($this->getImportService()->getData()->members, function ($u) use ($trelloUid) {
return $u->username === $trelloUid;
});
if (!$user) {
@@ -137,29 +118,35 @@ class TrelloImportService extends AImportService {
if (!is_string($nextcloudUid)) {
throw new \LogicException('User on setting uidRelation must be a string');
}
$this->getConfig('uidRelation')->$trelloUid = $this->userManager->get($nextcloudUid);
if (!$this->getConfig('uidRelation')->$trelloUid) {
$this->getImportService()->getConfig('uidRelation')->$trelloUid = $this->userManager->get($nextcloudUid);
if (!$this->getImportService()->getConfig('uidRelation')->$trelloUid) {
throw new \LogicException('User on setting uidRelation not found: ' . $nextcloudUid);
}
$user = current($user);
$this->members[$user->id] = $this->getConfig('uidRelation')->$trelloUid;
$this->members[$user->id] = $this->getImportService()->getConfig('uidRelation')->$trelloUid;
}
return $this;
}
public function assignUsersToBoard(): void {
/**
* @return Acl[]
*/
public function getAclList(): array {
$return = [];
foreach ($this->members as $member) {
if ($member->getUID() === $this->getConfig('owner')->getUID()) {
if ($member->getUID() === $this->getImportService()->getConfig('owner')->getUID()) {
continue;
}
$acl = new Acl();
$acl->setBoardId($this->board->getId());
$acl->setBoardId($this->getImportService()->getBoard()->getId());
$acl->setType(Acl::PERMISSION_TYPE_USER);
$acl->setParticipant($member->getUID());
$acl->setPermissionEdit(false);
$acl->setPermissionShare(false);
$acl->setPermissionManage(false);
$this->aclMapper->insert($acl);
$return[] = $acl;
}
return $return;
}
private function checklistItem($item): string {
@@ -181,14 +168,17 @@ class TrelloImportService extends AImportService {
return $checklist_string;
}
public function importCards(): void {
/**
* @return Card[]
*/
public function getCards(): array {
$checklists = [];
foreach ($this->data->checklists as $checklist) {
foreach ($this->getImportService()->getData()->checklists as $checklist) {
$checklists[$checklist->idCard][$checklist->id] = $this->formulateChecklistText($checklist);
}
$this->data->checklists = $checklists;
$this->getImportService()->getData()->checklists = $checklists;
foreach ($this->data->cards as $trelloCard) {
foreach ($this->getImportService()->getData()->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'));
@@ -196,7 +186,7 @@ class TrelloImportService extends AImportService {
$card->setDeletedAt($lastModified->format('U'));
}
if ((count($trelloCard->idChecklists) !== 0)) {
foreach ($this->data->checklists[$trelloCard->id] as $checklist) {
foreach ($this->getImportService()->getData()->checklists[$trelloCard->id] as $checklist) {
$trelloCard->desc .= "\n" . $checklist;
}
}
@@ -206,24 +196,25 @@ class TrelloImportService extends AImportService {
$card->setStackId($this->stacks[$trelloCard->idList]->getId());
$card->setType('plain');
$card->setOrder($trelloCard->idShort);
$card->setOwner($this->getConfig('owner')->getUID());
$card->setOwner($this->getImportService()->getConfig('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);
}
return $this->cards;
}
public function updateCard($cardTrelloId, Card $card): self {
$this->cards[$cardTrelloId] = $card;
return $this;
}
/**
* @return void
* @return ABoardImportService
*/
private function appendAttachmentsToDescription($trelloCard) {
if (empty($trelloCard->attachments)) {
@@ -236,86 +227,92 @@ class TrelloImportService extends AImportService {
$name = $attachment->name === $attachment->url ? null : $attachment->name;
$trelloCard->desc .= "| {$attachment->url} | {$name} | {$attachment->date} |\n";
}
return $this;
}
private function assignToMember(Card $card, $trelloCard): void {
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);
public function importParticipants(): ABoardImportService {
foreach ($this->getImportService()->getData()->cards as $trelloCard) {
foreach ($trelloCard->idMembers as $idMember) {
$assignment = new Assignment();
$assignment->setCardId($this->cards[$trelloCard->id]->getId());
$assignment->setParticipant($this->members[$idMember]->getUID());
$assignment->setType(Assignment::TYPE_USER);
$assignment = $this->assignmentMapper->insert($assignment);
}
}
return $this;
}
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;
public function importComments(): ABoardImportService {
foreach ($this->getImportService()->getData()->cards as $trelloCard) {
$comments = array_filter(
$this->getImportService()->getData()->actions,
function ($a) use ($trelloCard) {
return $a->type === 'commentCard' && $a->data->card->id === $trelloCard->id;
}
);
foreach ($comments as $trelloComment) {
if (!empty($this->getImportService()->getConfig('uidRelation')->{$trelloComment->memberCreator->username})) {
$actor = $this->getImportService()->getConfig('uidRelation')->{$trelloComment->memberCreator->username}->getUID();
} else {
$actor = $this->getImportService()->getConfig('owner')->getUID();
}
$comment = new Comment();
$comment
->setActor('users', $actor)
->setMessage($this->replaceUsernames($trelloComment->data->text), 0)
->setCreationDateTime(
\DateTime::createFromFormat('Y-m-d\TH:i:s.v\Z', $trelloComment->date)
);
$this->getImportService()->insertComment(
$this->cards[$trelloCard->id]->getId(),
$comment
);
}
);
foreach ($comments as $trelloComment) {
if (!empty($this->getConfig('uidRelation')->{$trelloComment->memberCreator->username})) {
$actor = $this->getConfig('uidRelation')->{$trelloComment->memberCreator->username}->getUID();
} else {
$actor = $this->getConfig('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();
}
return $this;
}
private function replaceUsernames($text) {
foreach ($this->getConfig('uidRelation') as $trello => $nextcloud) {
foreach ($this->getImportService()->getConfig('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()
);
public function assignCardsToLabels(): self {
foreach ($this->getImportService()->getData()->cards as $trelloCard) {
foreach ($trelloCard->labels as $label) {
$this->getImportService()->assignCardToLabel(
$this->cards[$trelloCard->id]->getId(),
$this->labels[$label->id]->getId()
);
}
}
return $this;
}
public function importStacks(): void {
$this->stacks = [];
foreach ($this->data->lists as $order => $list) {
/**
* @return Stack[]
*/
public function getStacks(): array {
$return = [];
foreach ($this->getImportService()->getData()->lists as $order => $list) {
$stack = new Stack();
if ($list->closed) {
$stack->setDeletedAt(time());
}
$stack->setTitle($list->name);
$stack->setBoardId($this->board->getId());
$stack->setBoardId($this->getImportService()->getBoard()->getId());
$stack->setOrder($order + 1);
$stack = $this->stackMapper->insert($stack);
$this->stacks[$list->id] = $stack;
$return[$list->id] = $stack;
}
return $return;
}
public function updateStack($id, $stack): self {
$this->stacks[$id] = $stack;
return $this;
}
private function translateColor($color): string {
@@ -345,45 +342,28 @@ class TrelloImportService extends AImportService {
}
}
public function importBoard(): void {
$this->board = $this->boardService->create(
$this->data->name,
$this->getConfig('owner')->getUID(),
$this->getConfig('color')
);
public function getBoard(): Board {
$board = new Board();
$board->setTitle($this->getImportService()->getData()->name);
$board->setOwner($this->getImportService()->getConfig('owner')->getUID());
$board->setColor($this->getImportService()->getConfig('color'));
return $board;
}
public function importLabels(): void {
$this->labels = [];
foreach ($this->data->labels as $label) {
public function importLabels(): self {
foreach ($this->getImportService()->getData()->labels as $label) {
if (empty($label->name)) {
$labelTitle = 'Unnamed ' . $label->color . ' label';
} else {
$labelTitle = $label->name;
}
$newLabel = $this->labelService->create(
$newLabel = $this->getImportService()->createLabel(
$labelTitle,
$this->translateColor($label->color),
$this->board->getId()
$this->getImportService()->getBoard()->getId()
);
$this->labels[$label->id] = $newLabel;
}
}
public 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->getConfig('owner')->getUID());
return $this;
}
}

View File

@@ -0,0 +1,24 @@
{
"type": "object",
"properties": {
"uidRelation": {
"type": "object",
"comment": "Relationship between Trello and Nextcloud usernames",
"example": {
"johndoe": "admin"
}
},
"owner": {
"type": "string",
"required": true,
"comment": "Nextcloud owner username"
},
"color": {
"type": "string",
"required": true,
"pattern": "^[0-9a-fA-F]{6}$",
"comment": "Default color for the board. If you don't inform, the default color will be used.",
"default": "0800fd"
}
}
}