Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/mail.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAnna Larch <anna@nextcloud.com>2021-03-04 19:31:21 +0300
committerChristoph Wurst <christoph@winzerhof-wurst.at>2021-03-22 20:06:02 +0300
commit83a58623b116cd3f8c79f751a3df5963b3392692 (patch)
tree8f9660e75ea8748057e752c4f1573e3c618b5b85 /lib
parentf27fb61c0fe15ef6a278a40d7774cde4b7c748b8 (diff)
Add tagging to messages
Signed-off-by: Anna Larch <anna@nextcloud.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/Account.php7
-rw-r--r--lib/Contracts/IMailManager.php29
-rwxr-xr-xlib/Controller/MessagesController.php67
-rw-r--r--lib/Db/MailAccountMapper.php9
-rw-r--r--lib/Db/Message.php46
-rw-r--r--lib/Db/MessageMapper.php143
-rw-r--r--lib/Db/Tag.php68
-rw-r--r--lib/Db/TagMapper.php219
-rw-r--r--lib/Migration/Version1100Date20210304143008.php120
-rw-r--r--lib/Model/IMAPMessage.php99
-rw-r--r--lib/Service/MailManager.php113
-rw-r--r--lib/Service/Search/Flag.php4
-rw-r--r--lib/Service/SetupService.php16
-rw-r--r--lib/Service/Sync/ImapToDbSynchronizer.php14
14 files changed, 859 insertions, 95 deletions
diff --git a/lib/Account.php b/lib/Account.php
index 456fac531..c6c3bb2f4 100644
--- a/lib/Account.php
+++ b/lib/Account.php
@@ -218,6 +218,13 @@ class Account implements JsonSerializable {
}
/**
+ * @return string
+ */
+ public function getUserId() {
+ return $this->account->getUserId();
+ }
+
+ /**
* @deprecated
*
* @return void
diff --git a/lib/Contracts/IMailManager.php b/lib/Contracts/IMailManager.php
index 9b3f48e41..33fe6d970 100644
--- a/lib/Contracts/IMailManager.php
+++ b/lib/Contracts/IMailManager.php
@@ -23,15 +23,16 @@ declare(strict_types=1);
namespace OCA\Mail\Contracts;
+use OCA\Mail\Db\Tag;
+use OCA\Mail\Folder;
use OCA\Mail\Account;
use OCA\Mail\Db\Mailbox;
use OCA\Mail\Db\Message;
-use OCA\Mail\Exception\ClientException;
-use OCA\Mail\Exception\ServiceException;
-use OCA\Mail\Folder;
+use OCA\Mail\Service\Quota;
use OCA\Mail\IMAP\FolderStats;
use OCA\Mail\Model\IMAPMessage;
-use OCA\Mail\Service\Quota;
+use OCA\Mail\Exception\ClientException;
+use OCA\Mail\Exception\ServiceException;
use OCP\AppFramework\Db\DoesNotExistException;
interface IMailManager {
@@ -172,6 +173,18 @@ interface IMailManager {
/**
* @param Account $account
+ * @param string $mailbox
+ * @param int $uid
+ * @param Tag $tag
+ * @param bool $value
+ *
+ * @throws ClientException
+ * @throws ServiceException
+ */
+ public function tagMessage(Account $account, string $mailbox, Message $message, Tag $tag, bool $value): void;
+
+ /**
+ * @param Account $account
*
* @return Quota|null
*/
@@ -229,4 +242,12 @@ interface IMailManager {
* @return array
*/
public function getMailAttachments(Account $account, Mailbox $mailbox, Message $message) : array;
+
+ /**
+ * @param string $imapLabel
+ * @param string $userId
+ * @return Tag
+ * @throws DoesNotExistException
+ */
+ public function getTagByImapLabel(string $imapLabel, string $userId): Tag;
}
diff --git a/lib/Controller/MessagesController.php b/lib/Controller/MessagesController.php
index 6361d415e..0cfa3a8eb 100755
--- a/lib/Controller/MessagesController.php
+++ b/lib/Controller/MessagesController.php
@@ -41,7 +41,6 @@ use OCA\Mail\Exception\ClientException;
use OCA\Mail\Exception\ServiceException;
use OCA\Mail\Http\AttachmentDownloadResponse;
use OCA\Mail\Http\HtmlResponse;
-use OCA\Mail\Model\IMAPMessage;
use OCA\Mail\Service\AccountService;
use OCA\Mail\Service\ItineraryService;
use OCP\AppFramework\Controller;
@@ -653,6 +652,68 @@ class MessagesController extends Controller {
* @NoAdminRequired
* @TrapError
*
+ * @param int $id
+ * @param string $imapLabel
+ *
+ * @return JSONResponse
+ *
+ * @throws ClientException
+ * @throws ServiceException
+ */
+ public function setTag(int $id, string $imapLabel): JSONResponse {
+ try {
+ $message = $this->mailManager->getMessage($this->currentUserId, $id);
+ $mailbox = $this->mailManager->getMailbox($this->currentUserId, $message->getMailboxId());
+ $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId());
+ } catch (DoesNotExistException $e) {
+ return new JSONResponse([], Http::STATUS_FORBIDDEN);
+ }
+
+ try {
+ $tag = $this->mailManager->getTagByImapLabel($imapLabel, $this->currentUserId);
+ } catch (DoesNotExistException $e) {
+ return new JSONResponse([], Http::STATUS_FORBIDDEN);
+ }
+
+ $this->mailManager->tagMessage($account, $mailbox->getName(), $message, $tag, true);
+ return new JSONResponse();
+ }
+
+ /**
+ * @NoAdminRequired
+ * @TrapError
+ *
+ * @param int $id
+ * @param string $imapLabel
+ *
+ * @return JSONResponse
+ *
+ * @throws ClientException
+ * @throws ServiceException
+ */
+ public function removeTag(int $id, string $imapLabel): JSONResponse {
+ try {
+ $message = $this->mailManager->getMessage($this->currentUserId, $id);
+ $mailbox = $this->mailManager->getMailbox($this->currentUserId, $message->getMailboxId());
+ $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId());
+ } catch (DoesNotExistException $e) {
+ return new JSONResponse([], Http::STATUS_FORBIDDEN);
+ }
+
+ try {
+ $tag = $this->mailManager->getTagByImapLabel($imapLabel, $this->currentUserId);
+ } catch (DoesNotExistException $e) {
+ return new JSONResponse([], Http::STATUS_FORBIDDEN);
+ }
+
+ $this->mailManager->tagMessage($account, $mailbox->getName(), $message, $tag, false);
+ return new JSONResponse();
+ }
+
+ /**
+ * @NoAdminRequired
+ * @TrapError
+ *
* @param int $accountId
* @param string $folderId
* @param int $id
@@ -705,10 +766,10 @@ class MessagesController extends Controller {
}
/**
- * @param array $attachment
- *
* Determines if the content of this attachment is an image
*
+ * @param array $attachment
+ *
* @return boolean
*/
private function attachmentIsImage(array $attachment): bool {
diff --git a/lib/Db/MailAccountMapper.php b/lib/Db/MailAccountMapper.php
index 5b4fec756..1dccbb674 100644
--- a/lib/Db/MailAccountMapper.php
+++ b/lib/Db/MailAccountMapper.php
@@ -148,4 +148,13 @@ class MailAccountMapper extends QBMapper {
return $this->findEntities($query);
}
+
+ public function getAllUserIdsWithAccounts(): array {
+ $qb = $this->db->getQueryBuilder();
+ $query = $qb
+ ->selectDistinct('user_id')
+ ->from($this->getTableName());
+
+ return $this->findEntities($query);
+ }
}
diff --git a/lib/Db/Message.php b/lib/Db/Message.php
index 3c6a46743..ea27288b0 100644
--- a/lib/Db/Message.php
+++ b/lib/Db/Message.php
@@ -86,8 +86,9 @@ class Message extends Entity implements JsonSerializable {
'forwarded',
'junk',
'notjunk',
- 'important',
- 'mdnsent'
+ 'mdnsent',
+ Tag::LABEL_IMPORTANT,
+ '$important' // @todo remove this when we have removed all references on IMAP to $important @link https://github.com/nextcloud/mail/issues/25
];
protected $uid;
@@ -125,6 +126,9 @@ class Message extends Entity implements JsonSerializable {
/** @var AddressList */
private $bcc;
+ /** @var Tag[] */
+ private $tags = [];
+
public function __construct() {
$this->from = new AddressList([]);
$this->to = new AddressList([]);
@@ -200,6 +204,20 @@ class Message extends Entity implements JsonSerializable {
}
/**
+ * @return Tag[]
+ */
+ public function getTags(): array {
+ return $this->tags;
+ }
+
+ /**
+ * @param array $tags
+ */
+ public function setTags(array $tags): void {
+ $this->tags = $tags;
+ }
+
+ /**
* @return AddressList
*/
public function getCc(): AddressList {
@@ -232,14 +250,26 @@ class Message extends Entity implements JsonSerializable {
// Ignore
return;
}
-
- $this->setter(
- $this->columnToProperty("flag_$flag"),
- [$value]
- );
+ if ($flag === Tag::LABEL_IMPORTANT) {
+ $this->setFlagImportant($value);
+ } else {
+ $this->setter(
+ $this->columnToProperty("flag_$flag"),
+ [$value]
+ );
+ }
}
public function jsonSerialize() {
+ $tags = $this->getTags();
+ $indexed = array_combine(
+ array_map(
+ function (Tag $tag) {
+ return $tag->getImapLabel();
+ }, $tags),
+ $tags
+ );
+
return [
'databaseId' => $this->getId(),
'uid' => $this->getUid(),
@@ -253,10 +283,10 @@ class Message extends Entity implements JsonSerializable {
'draft' => $this->getFlagDraft(),
'forwarded' => $this->getFlagForwarded(),
'hasAttachments' => $this->getFlagAttachments() ?? false,
- 'important' => $this->getFlagImportant(),
'junk' => $this->getFlagJunk(),
'mdnsent' => $this->getFlagMdnsent(),
],
+ 'tags' => $indexed,
'from' => $this->getFrom()->jsonSerialize(),
'to' => $this->getTo()->jsonSerialize(),
'cc' => $this->getCc()->jsonSerialize(),
diff --git a/lib/Db/MessageMapper.php b/lib/Db/MessageMapper.php
index 796c2b551..b51217e5c 100644
--- a/lib/Db/MessageMapper.php
+++ b/lib/Db/MessageMapper.php
@@ -25,26 +25,28 @@ declare(strict_types=1);
namespace OCA\Mail\Db;
+use OCP\IUser;
+use function ltrim;
use OCA\Mail\Account;
use OCA\Mail\Address;
-use OCA\Mail\AddressList;
-use OCA\Mail\IMAP\Threading\DatabaseMessage;
-use OCA\Mail\Service\Search\Flag;
-use OCA\Mail\Service\Search\FlagExpression;
-use OCA\Mail\Service\Search\SearchQuery;
-use OCP\AppFramework\Db\DoesNotExistException;
-use OCP\AppFramework\Db\QBMapper;
-use OCP\AppFramework\Utility\ITimeFactory;
-use OCP\DB\QueryBuilder\IQueryBuilder;
-use OCP\IDBConnection;
-use OCP\IUser;
use RuntimeException;
-use function array_combine;
-use function array_keys;
+use OCP\IDBConnection;
use function array_map;
use function get_class;
-use function ltrim;
use function mb_substr;
+use function array_keys;
+use function array_combine;
+use function array_udiff;
+use OCA\Mail\AddressList;
+use OCA\Mail\Service\Search\Flag;
+use OCP\AppFramework\Db\QBMapper;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCA\Mail\Service\Search\SearchQuery;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCA\Mail\Service\Search\FlagExpression;
+use OCA\Mail\IMAP\Threading\DatabaseMessage;
+use OCA\Mail\Support\PerformanceLogger;
+use OCP\AppFramework\Db\DoesNotExistException;
/**
* @template-extends QBMapper<Message>
@@ -54,10 +56,21 @@ class MessageMapper extends QBMapper {
/** @var ITimeFactory */
private $timeFactory;
+ /** @var TagMapper */
+ private $tagMapper;
+
+ /** @var PerformanceLogger */
+ private $performanceLogger;
+
public function __construct(IDBConnection $db,
- ITimeFactory $timeFactory) {
+ ITimeFactory $timeFactory,
+ TagMapper $tagMapper,
+ PerformanceLogger $performanceLogger
+ ) {
parent::__construct($db, 'mail_messages');
$this->timeFactory = $timeFactory;
+ $this->tagMapper = $tagMapper;
+ $this->performanceLogger = $performanceLogger;
}
/**
@@ -119,7 +132,7 @@ class MessageMapper extends QBMapper {
$query->expr()->eq('m.id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT)
);
- $results = $this->findRecipients($this->findEntities($query));
+ $results = $this->findRelatedData($this->findEntities($query));
if (empty($results)) {
throw new DoesNotExistException("Message $id does not exist");
}
@@ -229,7 +242,11 @@ class MessageMapper extends QBMapper {
$this->db->commit();
}
- public function insertBulk(Message ...$messages): void {
+ /**
+ * @param Message ...$messages
+ * @return void
+ */
+ public function insertBulk(Account $account, Message ...$messages): void {
$this->db->beginTransaction();
$qb1 = $this->db->getQueryBuilder();
@@ -309,19 +326,26 @@ class MessageMapper extends QBMapper {
$qb2->execute();
}
}
+ foreach ($message->getTags() as $tag) {
+ $this->tagMapper->tagMessage($tag, $message->getMessageId(), $account->getUserId());
+ }
}
$this->db->commit();
}
/**
- * @param Message ...$messages
- *
+ * @param Account $account
+ * @param Message[] $messages
* @return Message[]
*/
- public function updateBulk(Message ...$messages): array {
+ public function updateBulk(Account $account, Message ...$messages): array {
$this->db->beginTransaction();
+ $perf = $this->performanceLogger->start(
+ 'partial sync ' . $account->getId() . ':' . $account->getName()
+ );
+
$query = $this->db->getQueryBuilder();
$query->update($this->getTableName())
->set('flag_answered', $query->createParameter('flag_answered'))
@@ -339,7 +363,9 @@ class MessageMapper extends QBMapper {
$query->expr()->eq('uid', $query->createParameter('uid')),
$query->expr()->eq('mailbox_id', $query->createParameter('mailbox_id'))
));
-
+ // get all tags before the loop and create a mapping [message_id => [tag,...]]
+ $tags = $this->tagMapper->getAllTagsForMessages($messages);
+ $perf->step("Selected Tags for all messages");
foreach ($messages as $message) {
if (empty($message->getUpdatedFields())) {
// Micro optimization
@@ -360,13 +386,59 @@ class MessageMapper extends QBMapper {
$query->setParameter('flag_important', $message->getFlagImportant(), IQueryBuilder::PARAM_BOOL);
$query->execute();
+ $perf->step('Updated message ' . $message->getId());
+
+
+ $imapTags = $message->getTags();
+ $dbTags = $tags[$message->getMessageId()] ?? [];
+
+ if (empty($imapTags) === true && empty($dbTags) === true) {
+ // neither old nor new tags
+ continue;
+ }
+
+ $toAdd = array_udiff($imapTags, $dbTags, function (Tag $a, Tag $b) {
+ return strcmp($a->getImapLabel(), $b->getImapLabel());
+ });
+ foreach ($toAdd as $tag) {
+ // add logging
+ $this->tagMapper->tagMessage($tag, $message->getMessageId(), $account->getUserId());
+ }
+ $perf->step("Tagged messages");
+
+ if (empty($dbTags) === true) {
+ // we have nothing to possibly remove
+ continue;
+ }
+
+ $toRemove = array_udiff($dbTags, $imapTags, function (Tag $a, Tag $b) {
+ return strcmp($a->getImapLabel(), $b->getImapLabel());
+ });
+ foreach ($toRemove as $tag) {
+ //add logging
+ $this->tagMapper->untagMessage($tag, $message->getMessageId());
+ }
+ $perf->step("Untagged messages");
}
$this->db->commit();
+ $perf->end();
return $messages;
}
+ public function getTags(Message $message) : array {
+ $mqb = $this->db->getQueryBuilder();
+ $mqb->select('tag_id')
+ ->from('mail_message_tags')
+ ->where($mqb->expr()->eq('imap_message_id', $mqb->createNamedParameter($message->getMessageId())));
+ $result = $mqb->execute();
+ $ids = array_map(function (array $row) {
+ return (int)$row['tag_id'];
+ }, $result->fetchAll());
+
+ return $ids;
+ }
/**
* @param Message ...$messages
*
@@ -480,7 +552,7 @@ class MessageMapper extends QBMapper {
$qb->expr()->isNotNull('m1.thread_root_id')
)
->orderBy('sent_at', 'desc');
- return $this->findRecipients($this->findEntities($selectMessages));
+ return $this->findRelatedData($this->findEntities($selectMessages));
}
/**
@@ -711,6 +783,10 @@ class MessageMapper extends QBMapper {
}
private function flagToColumnName(Flag $flag): string {
+ // workaround for @link https://github.com/nextcloud/mail/issues/25
+ if ($flag->getFlag() === Tag::LABEL_IMPORTANT) {
+ return "flag_important";
+ }
$key = ltrim($flag->getFlag(), '\\$');
return "flag_$key";
}
@@ -733,7 +809,7 @@ class MessageMapper extends QBMapper {
)
->orderBy('sent_at', 'desc');
- return $this->findRecipients($this->findEntities($select));
+ return $this->findRelatedData($this->findEntities($select));
}
/**
@@ -755,7 +831,7 @@ class MessageMapper extends QBMapper {
)
->orderBy('sent_at', 'desc');
- return $this->findRecipients($this->findEntities($select));
+ return $this->findRelatedData($this->findEntities($select));
}
/**
@@ -809,6 +885,21 @@ class MessageMapper extends QBMapper {
}
/**
+ * @param Message[] $messages
+ * @return Message[]
+ */
+ public function findRelatedData(array $messages): array {
+ $messages = $this->findRecipients($messages);
+ $tags = $this->tagMapper->getAllTagsForMessages($messages);
+ /** @var Message $message */
+ $messages = array_map(function ($message) use ($tags) {
+ $message->setTags($tags[$message->getMessageId()] ?? []);
+ return $message;
+ }, $messages);
+ return $messages;
+ }
+
+ /**
* @param Mailbox $mailbox
* @param int $highest
*
@@ -847,7 +938,7 @@ class MessageMapper extends QBMapper {
$qb->expr()->gt('updated_at', $qb->createNamedParameter($since, IQueryBuilder::PARAM_INT))
);
- return $this->findRecipients($this->findEntities($select));
+ return $this->findRelatedData($this->findEntities($select));
}
/**
@@ -870,7 +961,7 @@ class MessageMapper extends QBMapper {
->orderBy('sent_at', 'desc')
->setMaxResults($limit);
- return $this->findRecipients($this->findEntities($select));
+ return $this->findRelatedData($this->findEntities($select));
}
public function deleteOrphans(): void {
diff --git a/lib/Db/Tag.php b/lib/Db/Tag.php
new file mode 100644
index 000000000..261f9c393
--- /dev/null
+++ b/lib/Db/Tag.php
@@ -0,0 +1,68 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2021 Anna Larch <anna@nextcloud.com>
+ *
+ * @author 2021 Anna Larch <anna@nextcloud.com>
+ *
+ * @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\Mail\Db;
+
+use JsonSerializable;
+use OCP\AppFramework\Db\Entity;
+
+/**
+ * @method string getUserId()
+ * @method void setUserId(string $userId)
+ * @method string getDisplayName()
+ * @method void setDisplayName(string $displayName)
+ * @method string getImapLabel()
+ * @method void setImapLabel(string $imapLabel)
+ * @method string getColor()
+ * @method void setColor(string $color)
+ * @method bool getIsDefaultTag()
+ * @method void setIsDefaultTag(bool $flag)
+ */
+class Tag extends Entity implements JsonSerializable {
+ protected $userId;
+ protected $displayName;
+ protected $imapLabel;
+ protected $color;
+ protected $isDefaultTag;
+
+ public const LABEL_IMPORTANT = '$label1';
+
+ public function __construct() {
+ $this->addType('isDefaultTag', 'boolean');
+ }
+ /**
+ * @return array
+ */
+ public function jsonSerialize() {
+ return [
+ 'id' => $this->getId(),
+ 'userId' => $this->getUserId(),
+ 'displayName' => $this->getDisplayName(),
+ 'imapLabel' => $this->getImapLabel(),
+ 'color' => $this->getColor(),
+ 'isDefaultTag' => $this->getIsDefaultTag(),
+ ];
+ }
+}
diff --git a/lib/Db/TagMapper.php b/lib/Db/TagMapper.php
new file mode 100644
index 000000000..219590223
--- /dev/null
+++ b/lib/Db/TagMapper.php
@@ -0,0 +1,219 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2021 Anna Larch <anna@nextcloud.com>
+ *
+ * @author 2021 Anna Larch <anna@nextcloud.com>
+ *
+ * @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\Mail\Db;
+
+use function array_map;
+use OCP\AppFramework\Db\DoesNotExistException;
+use OCP\AppFramework\Db\Entity;
+use OCP\AppFramework\Db\QBMapper;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+use OCP\IL10N;
+
+/**
+ * @template-extends QBMapper<Tag>
+ */
+class TagMapper extends QBMapper {
+
+ /** @var IL10N */
+ private $l10n;
+
+ public function __construct(IDBConnection $db,
+ IL10N $l10n) {
+ parent::__construct($db, 'mail_tags');
+ $this->l10n = $l10n;
+ }
+
+ /**
+ * @throws DoesNotExistException
+ */
+ public function getTagByImapLabel(string $imapLabel, string $userId): Entity {
+ $qb = $this->db->getQueryBuilder();
+ $qb->select('*')
+ ->from($this->getTableName())
+ ->where(
+ $qb->expr()->eq('imap_label', $qb->createNamedParameter($imapLabel)),
+ $qb->expr()->eq('user_id', $qb->createNamedParameter($userId))
+ );
+ return $this->findEntity($qb);
+ }
+
+ /**
+ * @throws DoesNotExistException
+ */
+ public function getTagForUser(int $id, string $userId): Entity {
+ $qb = $this->db->getQueryBuilder();
+ $qb->select('*')
+ ->from($this->getTableName())
+ ->where(
+ $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)),
+ $qb->expr()->eq('user_id', $qb->createNamedParameter($userId))
+ );
+ return $this->findEntity($qb);
+ }
+
+ /**
+ * @return Tag[]
+ * @throws DoesNotExistException
+ */
+ public function getAllTagForUser(string $userId): array {
+ $qb = $this->db->getQueryBuilder();
+ $qb->select('*')
+ ->from($this->getTableName())
+ ->where(
+ $qb->expr()->eq('user_id', $qb->createNamedParameter($userId))
+ );
+ return $this->findEntities($qb);
+ }
+
+ /**
+ * @throws DoesNotExistException
+ */
+ public function getTag(int $id): Entity {
+ $qb = $this->db->getQueryBuilder();
+ $qb->select('*')
+ ->from($this->getTableName())
+ ->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
+ return $this->findEntity($qb);
+ }
+
+ /**
+ * Tag a message in the DB
+ *
+ * To tag (flag) a message on IMAP, @see \OCA\Mail\Service\MailManager::tagMessage
+ */
+ public function tagMessage(Tag $tag, string $messageId, string $userId): void {
+ /** @var Tag $exists */
+ try {
+ $exists = $this->getTagByImapLabel($tag->getImapLabel(), $userId);
+ $tag->setId($exists->getId());
+ } catch (DoesNotExistException $e) {
+ $tag = $this->insert($tag);
+ }
+
+ $qb = $this->db->getQueryBuilder();
+ $qb->insert('mail_message_tags');
+ $qb->setValue('imap_message_id', $qb->createNamedParameter($messageId));
+ $qb->setValue('tag_id', $qb->createNamedParameter($tag->getId(), IQueryBuilder::PARAM_INT));
+ $qb->execute();
+ }
+
+ /**
+ * Remove a tag from a DB message
+ *
+ * This does not(!) untag a message on IMAP
+ */
+ public function untagMessage(Tag $tag, string $messageId): void {
+ $qb = $this->db->getQueryBuilder();
+ $qb->delete('mail_message_tags')
+ ->where($qb->expr()->eq('imap_message_id', $qb->createNamedParameter($messageId)))
+ ->where($qb->expr()->eq('tag_id', $qb->createNamedParameter($tag->getId())));
+ $qb->execute();
+ }
+
+ /**
+ * @param Message[] $messages
+ * @return Tag[][]
+ */
+ public function getAllTagsForMessages(array $messages): array {
+ $ids = array_map(function (Message $message) {
+ return $message->getMessageId();
+ }, $messages);
+
+ $qb = $this->db->getQueryBuilder();
+ $idsQuery = $qb->select('mt.*')
+ ->from('mail_message_tags', 'mt')
+ ->where(
+ $qb->expr()->in('imap_message_id', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_STR_ARRAY))
+ );
+ $idsQuery = $idsQuery->execute();
+ $queryResult = $idsQuery->fetchAll();
+ if (empty($queryResult)) {
+ return [];
+ }
+ $result = [];
+ foreach ($queryResult as $qr) {
+ $result[] = $qr['imap_message_id'];
+ $result[$qr['imap_message_id']][] = $this->getTag((int)$qr['tag_id']);
+ };
+ return $result;
+ }
+
+ /**
+ * Create some default system tags
+ *
+ * This is designed to be similar to Thunderbird's email tags
+ * $label1 to $label5 with the according states and colours
+ *
+ * <i>The array_udiff can be removed and the insert warpped in
+ * an exception as soon as NC20 is not supported any more</i>
+ *
+ * @link https://github.com/nextcloud/mail/issues/25
+ */
+ public function createDefaultTags(MailAccount $account): void {
+ $tags = [];
+ for ($i = 1; $i < 6; $i++) {
+ $tag = new Tag();
+ $tag->setImapLabel('$label' . $i);
+ $tag->setUserId($account->getUserId());
+ switch ($i) {
+ case 1:
+ $tag->setDisplayName($this->l10n->t('Important'));
+ $tag->setColor('#FF0000');
+ $tag->setIsDefaultTag(true);
+ break;
+ case 2:
+ $tag->setDisplayName($this->l10n->t('Work'));
+ $tag->setColor('#FFC300');
+ $tag->setIsDefaultTag(true);
+ break;
+ case 3:
+ $tag->setDisplayName($this->l10n->t('Personal'));
+ $tag->setColor('#008000');
+ $tag->setIsDefaultTag(true);
+ break;
+ case 4:
+ $tag->setDisplayName($this->l10n->t('To Do'));
+ $tag->setColor('#000080');
+ $tag->setIsDefaultTag(true);
+ break;
+ case 5:
+ $tag->setDisplayName($this->l10n->t('Later'));
+ $tag->setColor('#800080');
+ $tag->setIsDefaultTag(true);
+ break;
+ }
+ $tags[] = $tag;
+ }
+ $dbTags = $this->getAllTagForUser($account->getUserId());
+ $toInsert = array_udiff($tags, $dbTags, function (Tag $a, Tag $b) {
+ return strcmp($a->getImapLabel(), $b->getImapLabel());
+ });
+ foreach ($toInsert as $entity) {
+ $this->insert($entity);
+ }
+ }
+}
diff --git a/lib/Migration/Version1100Date20210304143008.php b/lib/Migration/Version1100Date20210304143008.php
new file mode 100644
index 000000000..06c6f6578
--- /dev/null
+++ b/lib/Migration/Version1100Date20210304143008.php
@@ -0,0 +1,120 @@
+<?php
+
+declare(strict_types=1);
+
+namespace OCA\Mail\Migration;
+
+use Closure;
+use OCA\Mail\Db\MailAccountMapper;
+use OCA\Mail\Db\TagMapper;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+/**
+ * @link https://github.com/nextcloud/mail/issues/25
+ */
+class Version1100Date20210304143008 extends SimpleMigrationStep {
+
+ /**
+ * @var TagMapper
+ */
+ protected $tagMapper;
+
+ /**
+ * @var MailAccountMapper
+ */
+ protected $mailAccountMapper;
+
+ public function __construct(TagMapper $tagMapper, MailAccountMapper $mailAccountMapper) {
+ $this->tagMapper = $tagMapper;
+ $this->mailAccountMapper = $mailAccountMapper;
+ }
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
+ $schema = $schemaClosure();
+
+ if (!$schema->hasTable('mail_tags')) {
+ $tagsTable = $schema->createTable('mail_tags');
+ $tagsTable->addColumn('id', 'integer', [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 4,
+ ]);
+ $tagsTable->addColumn('user_id', 'string', [
+ 'notnull' => true,
+ 'length' => 64,
+ ]);
+ $tagsTable->addColumn('imap_label', 'string', [
+ 'notnull' => true,
+ 'length' => 64,
+ ]);
+ $tagsTable->addColumn('display_name', 'string', [
+ 'notnull' => true,
+ 'length' => 128,
+ ]);
+ $tagsTable->addColumn('color', 'string', [
+ 'notnull' => false,
+ 'length' => 9,
+ 'default' => "#fff"
+ ]);
+ $tagsTable->addColumn('is_default_tag', 'boolean', [
+ 'notnull' => false,
+ 'default' => false
+ ]);
+ $tagsTable->setPrimaryKey(['id']);
+ $tagsTable->addIndex(['user_id'], 'mail_msg_tags_usr_id_index');
+ $tagsTable->addUniqueIndex(
+ [
+ 'user_id',
+ 'imap_label',
+ ],
+ 'mail_msg_tags_usr_lbl_idx'
+ );
+ }
+
+ if (!$schema->hasTable('mail_message_tags')) {
+ $tagsMessageTable = $schema->createTable('mail_message_tags');
+ $tagsMessageTable->addColumn('id', 'integer', [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 4,
+ ]);
+ $tagsMessageTable->addColumn('imap_message_id', 'string', [
+ 'notnull' => true,
+ 'length' => 1023,
+ ]);
+ $tagsMessageTable->addColumn('tag_id', 'integer', [
+ 'notnull' => true,
+ 'length' => 4,
+ ]);
+ $tagsMessageTable->setPrimaryKey(['id']);
+ $tagsMessageTable->addUniqueIndex(
+ [
+ 'imap_message_id',
+ 'tag_id',
+ ],
+ 'mail_msg_tag_id_idx'
+ );
+ }
+ return $schema;
+ }
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ */
+ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
+ $accounts = $this->mailAccountMapper->getAllUserIdsWithAccounts();
+ foreach ($accounts as $account) {
+ $this->tagMapper->createDefaultTags($account);
+ }
+ }
+}
diff --git a/lib/Model/IMAPMessage.php b/lib/Model/IMAPMessage.php
index 46005a0c3..24743ef44 100644
--- a/lib/Model/IMAPMessage.php
+++ b/lib/Model/IMAPMessage.php
@@ -29,29 +29,32 @@ declare(strict_types=1);
namespace OCA\Mail\Model;
+use OC;
use Exception;
-use Horde_Imap_Client;
-use Horde_Imap_Client_Data_Envelope;
-use Horde_Imap_Client_Data_Fetch;
-use Horde_Imap_Client_DateTime;
-use Horde_Imap_Client_Fetch_Query;
-use Horde_Imap_Client_Ids;
-use Horde_Imap_Client_Mailbox;
-use Horde_Imap_Client_Socket;
-use Horde_Mime_Headers;
-use Horde_Mime_Headers_MessageId;
+use function trim;
+use OCP\Files\File;
use Horde_Mime_Part;
+use OCA\Mail\Db\Tag;
use JsonSerializable;
-use OC;
+use function in_array;
+use Horde_Imap_Client;
+use Horde_Mime_Headers;
+use OCA\Mail\Db\Message;
use OCA\Mail\AddressList;
-use OCA\Mail\Db\LocalAttachment;
+use Horde_Imap_Client_Ids;
use OCA\Mail\Service\Html;
-use OCP\AppFramework\Db\DoesNotExistException;
-use OCP\Files\File;
-use OCP\Files\SimpleFS\ISimpleFile;
-use function in_array;
+use OCA\Mail\Db\MailAccount;
+use Horde_Imap_Client_Socket;
+use Horde_Imap_Client_Mailbox;
+use Horde_Imap_Client_DateTime;
+use OCA\Mail\Db\LocalAttachment;
use function mb_convert_encoding;
-use function trim;
+use Horde_Imap_Client_Data_Fetch;
+use Horde_Mime_Headers_MessageId;
+use Horde_Imap_Client_Fetch_Query;
+use OCP\Files\SimpleFS\ISimpleFile;
+use Horde_Imap_Client_Data_Envelope;
+use OCP\AppFramework\Db\DoesNotExistException;
class IMAPMessage implements IMessage, JsonSerializable {
use ConvertAddresses;
@@ -149,7 +152,7 @@ class IMAPMessage implements IMessage, JsonSerializable {
'forwarded' => in_array(Horde_Imap_Client::FLAG_FORWARDED, $flags),
'hasAttachments' => $this->hasAttachments($this->fetch->getStructure()),
'mdnsent' => in_array(Horde_Imap_Client::FLAG_MDNSENT, $flags, true),
- 'important' => in_array('$important', $flags, true)
+ 'important' => in_array(Tag::LABEL_IMPORTANT, $flags, true)
];
}
@@ -673,8 +676,14 @@ class IMAPMessage implements IMessage, JsonSerializable {
throw new Exception('not implemented');
}
- public function toDbMessage(int $mailboxId): \OCA\Mail\Db\Message {
- $msg = new \OCA\Mail\Db\Message();
+ /**
+ * Cast all values from an IMAP message into the correct DB format
+ *
+ * @param integer $mailboxId
+ * @return Message
+ */
+ public function toDbMessage(int $mailboxId, MailAccount $account): Message {
+ $msg = new Message();
$messageId = $this->getMessageId();
if (empty(trim($messageId))) {
@@ -707,10 +716,58 @@ class IMAPMessage implements IMessage, JsonSerializable {
in_array('junk', $flags, true)
);
$msg->setFlagNotjunk(in_array(Horde_Imap_Client::FLAG_NOTJUNK, $flags, true));
- $msg->setFlagImportant(in_array('$important', $flags, true));
+ // @todo remove this as soon as possible @link https://github.com/nextcloud/mail/issues/25
+ $msg->setFlagImportant(in_array('$important', $flags, true) || in_array(Tag::LABEL_IMPORTANT, $flags, true));
$msg->setFlagAttachments(false);
$msg->setFlagMdnsent(in_array(Horde_Imap_Client::FLAG_MDNSENT, $flags, true));
+ $allowed = [
+ Horde_Imap_Client::FLAG_SEEN,
+ Horde_Imap_Client::FLAG_ANSWERED,
+ Horde_Imap_Client::FLAG_FLAGGED,
+ Horde_Imap_Client::FLAG_DELETED,
+ Horde_Imap_Client::FLAG_DRAFT,
+ Horde_Imap_Client::FLAG_RECENT,
+ Horde_Imap_Client::FLAG_JUNK,
+ Horde_Imap_Client::FLAG_MDNSENT,
+ ];
+ // remove all standard IMAP flags from $filters
+ $tags = array_filter($flags, function ($flag) use ($allowed) {
+ return in_array($flag, $allowed, true) === false;
+ });
+
+ if (empty($tags) === true) {
+ return $msg;
+ }
+ // cast all leftover $flags to be used as tags
+ $msg->setTags($this->generateTagEntites($tags, $account->getUserId()));
return $msg;
}
+
+ /**
+ * Build tag entities from keywords sent by IMAP
+ *
+ * Will use IMAP keyword '$xxx' to create a value for
+ * display_name like 'xxx'
+ *
+ * @link https://github.com/nextcloud/mail/issues/25
+ *
+ * @param string[] $tags
+ * @return Tag[]
+ */
+ private function generateTagEntites(array $tags, string $userId): array {
+ $t = [];
+ foreach ($tags as $keyword) {
+ // Map the old $important to $label1 until we have caught them all
+ if ($keyword === '$important') {
+ $keyword = Tag::LABEL_IMPORTANT;
+ }
+ $tag = new Tag();
+ $tag->setImapLabel($keyword);
+ $tag->setDisplayName(str_replace('$', '', $keyword));
+ $tag->setUserId($userId);
+ $t[] = $tag;
+ }
+ return $t;
+ }
}
diff --git a/lib/Service/MailManager.php b/lib/Service/MailManager.php
index d37278477..782946e6a 100644
--- a/lib/Service/MailManager.php
+++ b/lib/Service/MailManager.php
@@ -23,34 +23,36 @@ declare(strict_types=1);
namespace OCA\Mail\Service;
-use Horde_Imap_Client;
-use Horde_Imap_Client_Exception;
-use Horde_Imap_Client_Exception_NoSupportExtension;
-use Horde_Imap_Client_Socket;
+use OCA\Mail\Db\Tag;
+use OCA\Mail\Folder;
use OCA\Mail\Account;
-use OCA\Mail\Contracts\IMailManager;
+use Horde_Imap_Client;
+use function array_map;
use OCA\Mail\Db\Mailbox;
-use OCA\Mail\Db\MailboxMapper;
use OCA\Mail\Db\Message;
-use OCA\Mail\Db\MessageMapper as DbMessageMapper;
-use OCA\Mail\Events\BeforeMessageDeletedEvent;
+use function array_values;
+use OCA\Mail\Db\TagMapper;
+use Psr\Log\LoggerInterface;
+use Horde_Imap_Client_Socket;
+use OCA\Mail\Db\MailboxMapper;
+use OCA\Mail\IMAP\FolderStats;
+use OCA\Mail\IMAP\MailboxSync;
+use OCA\Mail\IMAP\FolderMapper;
+use OCA\Mail\Model\IMAPMessage;
+use Horde_Imap_Client_Exception;
+use OCA\Mail\Contracts\IMailManager;
+use OCA\Mail\IMAP\IMAPClientFactory;
+use OCA\Mail\Exception\ClientException;
use OCA\Mail\Events\MessageDeletedEvent;
use OCA\Mail\Events\MessageFlaggedEvent;
-use OCA\Mail\Exception\ClientException;
use OCA\Mail\Exception\ServiceException;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCA\Mail\Events\BeforeMessageDeletedEvent;
+use OCP\AppFramework\Db\DoesNotExistException;
+use OCA\Mail\Db\MessageMapper as DbMessageMapper;
+use Horde_Imap_Client_Exception_NoSupportExtension;
use OCA\Mail\Exception\TrashMailboxNotSetException;
-use OCA\Mail\Folder;
-use OCA\Mail\IMAP\FolderMapper;
-use OCA\Mail\IMAP\FolderStats;
-use OCA\Mail\IMAP\IMAPClientFactory;
-use OCA\Mail\IMAP\MailboxSync;
use OCA\Mail\IMAP\MessageMapper as ImapMessageMapper;
-use OCA\Mail\Model\IMAPMessage;
-use OCP\AppFramework\Db\DoesNotExistException;
-use OCP\EventDispatcher\IEventDispatcher;
-use Psr\Log\LoggerInterface;
-use function array_map;
-use function array_values;
class MailManager implements IMailManager {
@@ -86,8 +88,12 @@ class MailManager implements IMailManager {
/** @var DbMessageMapper */
private $dbMessageMapper;
+ /** @var TagMapper */
+ private $tagMapper;
+
/** @var IEventDispatcher */
private $eventDispatcher;
+
/**
* @var LoggerInterface
*/
@@ -100,7 +106,8 @@ class MailManager implements IMailManager {
ImapMessageMapper $messageMapper,
DbMessageMapper $dbMessageMapper,
IEventDispatcher $eventDispatcher,
- LoggerInterface $logger) {
+ LoggerInterface $logger,
+ TagMapper $tagMapper) {
$this->imapClientFactory = $imapClientFactory;
$this->mailboxMapper = $mailboxMapper;
$this->mailboxSync = $mailboxSync;
@@ -109,6 +116,7 @@ class MailManager implements IMailManager {
$this->dbMessageMapper = $dbMessageMapper;
$this->eventDispatcher = $eventDispatcher;
$this->logger = $logger;
+ $this->tagMapper = $tagMapper;
}
public function getMailbox(string $uid, int $id): Mailbox {
@@ -393,7 +401,7 @@ class MailManager implements IMailManager {
if (empty($imapFlag) === true) {
continue;
}
- if ($value === true) {
+ if ($value) {
$this->imapMessageMapper->addFlag($client, $mb, $uid, $imapFlag);
} else {
$this->imapMessageMapper->removeFlag($client, $mb, $uid, $imapFlag);
@@ -420,6 +428,50 @@ class MailManager implements IMailManager {
}
/**
+ * Tag (flag) a message on IMAP
+ *
+ * @param Account $account
+ * @param string $mailbox
+ * @param integer $uid
+ * @param Tag $tag
+ * @param boolean $value
+ * @return void
+ *
+ * @uses
+ *
+ * @link https://github.com/nextcloud/mail/issues/25
+ */
+ public function tagMessage(Account $account, string $mailbox, Message $message, Tag $tag, bool $value): void {
+ $client = $this->imapClientFactory->getClient($account);
+ try {
+ $mb = $this->mailboxMapper->find($account, $mailbox);
+ } catch (DoesNotExistException $e) {
+ throw new ClientException("Mailbox $mailbox does not exist", 0, $e);
+ }
+ if ($this->isPermflagsEnabled($account, $mailbox) === true) {
+ try {
+ if ($value) {
+ // imap keywords and flags work the same way
+ $this->imapMessageMapper->addFlag($client, $mb, $message->getUid(), $tag->getImapLabel());
+ } else {
+ $this->imapMessageMapper->removeFlag($client, $mb, $message->getUid(), $tag->getImapLabel());
+ }
+ } catch (Horde_Imap_Client_Exception $e) {
+ throw new ServiceException(
+ "Could not set message keyword on IMAP: " . $e->getMessage(),
+ (int) $e->getCode(),
+ $e
+ );
+ }
+ }
+ if ($value) {
+ $this->tagMapper->tagMessage($tag, $message->getMessageId(), $account->getUserId());
+ } else {
+ $this->tagMapper->untagMessage($tag, $message->getMessageId());
+ }
+ }
+
+ /**
* @param Account $account
*
* @return Quota|null
@@ -518,6 +570,21 @@ class MailManager implements IMailManager {
}
/**
+ * @param string $imapLabel
+ * @param string $userId
+ * @return Tag
+ * @throws DoesNotExistException
+ */
+ public function getTagByImapLabel(string $imapLabel, string $userId): Tag {
+ try {
+ return $this->tagMapper->getTagByImapLabel($imapLabel, $userId);
+ } catch (DoesNotExistException $e) {
+ throw new ClientException('Unknow Tag', (int)$e->getCode(), $e);
+ }
+ }
+
+
+ /**
* Filter out IMAP flags that aren't supported by the client server
*
* @param Horde_Imap_Client_Socket $client
@@ -534,7 +601,7 @@ class MailManager implements IMailManager {
// Only allow flag setting if IMAP supports Permaflags
// @TODO check if there are length & char limits on permflags
if ($this->isPermflagsEnabled($account, $mailbox) === true) {
- return ["$" . $flag];
+ return [$flag];
}
return [];
}
diff --git a/lib/Service/Search/Flag.php b/lib/Service/Search/Flag.php
index 79294813a..491a63aca 100644
--- a/lib/Service/Search/Flag.php
+++ b/lib/Service/Search/Flag.php
@@ -26,6 +26,7 @@ declare(strict_types=1);
namespace OCA\Mail\Service\Search;
use Horde_Imap_Client;
+use OCA\Mail\Db\Tag;
/**
* @psalm-immutable
@@ -34,7 +35,8 @@ class Flag {
public const ANSWERED = Horde_Imap_Client::FLAG_ANSWERED;
public const SEEN = Horde_Imap_Client::FLAG_SEEN;
public const FLAGGED = Horde_Imap_Client::FLAG_FLAGGED;
- public const IMPORTANT = '\\important';
+ /** @deprecated */
+ public const IMPORTANT = Tag::LABEL_IMPORTANT;
public const DELETED = Horde_Imap_Client::FLAG_DELETED;
/** @var string */
diff --git a/lib/Service/SetupService.php b/lib/Service/SetupService.php
index ffd6146ac..78b423425 100644
--- a/lib/Service/SetupService.php
+++ b/lib/Service/SetupService.php
@@ -31,6 +31,7 @@ use Horde_Mail_Exception;
use Horde_Mail_Transport_Smtphorde;
use OCA\Mail\Account;
use OCA\Mail\Db\MailAccount;
+use OCA\Mail\Db\TagMapper;
use OCA\Mail\Exception\CouldNotConnectException;
use OCA\Mail\Exception\ServiceException;
use OCA\Mail\IMAP\IMAPClientFactory;
@@ -56,21 +57,26 @@ class SetupService {
/** @var IMAPClientFactory */
private $imapClientFactory;
- /** var LoggerInterface */
+ /** @var LoggerInterface */
private $logger;
+ /** @var TagMapper */
+ private $tagMapper;
+
public function __construct(AutoConfig $autoConfig,
AccountService $accountService,
ICrypto $crypto,
SmtpClientFactory $smtpClientFactory,
IMAPClientFactory $imapClientFactory,
- LoggerInterface $logger) {
+ LoggerInterface $logger,
+ TagMapper $tagMapper) {
$this->autoConfig = $autoConfig;
$this->accountService = $accountService;
$this->crypto = $crypto;
$this->smtpClientFactory = $smtpClientFactory;
$this->imapClientFactory = $imapClientFactory;
$this->logger = $logger;
+ $this->tagMapper = $tagMapper;
}
/**
@@ -78,6 +84,8 @@ class SetupService {
* @param string $emailAddress
* @param string $password
* @return Account|null
+ *
+ * @link https://github.com/nextcloud/mail/issues/25
*/
public function createNewAutoConfiguredAccount($accountName, $emailAddress, $password) {
$this->logger->info('setting up auto detected account');
@@ -88,6 +96,8 @@ class SetupService {
$this->accountService->save($mailAccount);
+ $this->tagMapper->createDefaultTags($mailAccount);
+
return new Account($mailAccount);
}
@@ -139,6 +149,8 @@ class SetupService {
$this->accountService->save($newAccount);
$this->logger->debug("account created " . $newAccount->getId());
+ $this->tagMapper->createDefaultTags($newAccount);
+
return $account;
}
diff --git a/lib/Service/Sync/ImapToDbSynchronizer.php b/lib/Service/Sync/ImapToDbSynchronizer.php
index 3132aad06..2adba1ee2 100644
--- a/lib/Service/Sync/ImapToDbSynchronizer.php
+++ b/lib/Service/Sync/ImapToDbSynchronizer.php
@@ -292,8 +292,8 @@ class ImapToDbSynchronizer {
}
foreach (array_chunk($imapMessages['messages'], 500) as $chunk) {
- $this->dbMapper->insertBulk(...array_map(function (IMAPMessage $imapMessage) use ($mailbox) {
- return $imapMessage->toDbMessage($mailbox->getId());
+ $this->dbMapper->insertBulk($account, ...array_map(function (IMAPMessage $imapMessage) use ($mailbox, $account) {
+ return $imapMessage->toDbMessage($mailbox->getId(), $account->getMailAccount());
}, $chunk));
}
$perf->step('persist messages in database');
@@ -349,8 +349,8 @@ class ImapToDbSynchronizer {
$perf->step('get new messages via Horde');
foreach (array_chunk($response->getNewMessages(), 500) as $chunk) {
- $dbMessages = array_map(function (IMAPMessage $imapMessage) use ($mailbox) {
- return $imapMessage->toDbMessage($mailbox->getId());
+ $dbMessages = array_map(function (IMAPMessage $imapMessage) use ($mailbox, $account) {
+ return $imapMessage->toDbMessage($mailbox->getId(), $account->getMailAccount());
}, $chunk);
$this->dispatcher->dispatch(
@@ -359,7 +359,7 @@ class ImapToDbSynchronizer {
);
$perf->step('classified a chunk of new messages');
- $this->dbMapper->insertBulk(...$dbMessages);
+ $this->dbMapper->insertBulk($account, ...$dbMessages);
}
$perf->step('persist new messages');
@@ -378,8 +378,8 @@ class ImapToDbSynchronizer {
$perf->step('get changed messages via Horde');
foreach (array_chunk($response->getChangedMessages(), 500) as $chunk) {
- $this->dbMapper->updateBulk(...array_map(function (IMAPMessage $imapMessage) use ($mailbox) {
- return $imapMessage->toDbMessage($mailbox->getId());
+ $this->dbMapper->updateBulk($account, ...array_map(function (IMAPMessage $imapMessage) use ($mailbox, $account) {
+ return $imapMessage->toDbMessage($mailbox->getId(), $account->getMailAccount());
}, $chunk));
}
$perf->step('persist changed messages');