From 7d4f62f1155ec2f88d284acc90ce92e8657ce2b9 Mon Sep 17 00:00:00 2001 From: SebastianKrupinski Date: Tue, 22 Oct 2024 00:21:11 -0400 Subject: [PATCH 1/8] fix: perform bulk message actions Signed-off-by: SebastianKrupinski --- lib/Controller/MessagesController.php | 28 ++++- lib/Db/MailAccountMapper.php | 19 +++ lib/Db/MailboxMapper.php | 4 +- lib/Db/MessageMapper.php | 22 ++++ lib/IMAP/MessageMapper.php | 38 ++++++ lib/Service/MessageOperationService.php | 153 ++++++++++++++++++++++++ src/components/EnvelopeList.vue | 46 ++++--- src/service/MessageService.js | 9 +- src/store/actions.js | 80 +++++++++---- vite.config.js | 1 + 10 files changed, 357 insertions(+), 43 deletions(-) create mode 100644 lib/Service/MessageOperationService.php diff --git a/lib/Controller/MessagesController.php b/lib/Controller/MessagesController.php index 88b6299e21..f0f037ff89 100755 --- a/lib/Controller/MessagesController.php +++ b/lib/Controller/MessagesController.php @@ -30,11 +30,14 @@ use OCA\Mail\Service\AccountService; use OCA\Mail\Service\AiIntegrations\AiIntegrationsService; use OCA\Mail\Service\ItineraryService; +use OCA\Mail\Service\MessageOperationService; use OCA\Mail\Service\SmimeService; use OCA\Mail\Service\SnoozeService; use OCP\AppFramework\Controller; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\FrontpageRoute; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\OpenAPI; use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\JSONResponse; @@ -74,7 +77,8 @@ class MessagesController extends Controller { private SnoozeService $snoozeService; private AiIntegrationsService $aiIntegrationService; - public function __construct(string $appName, + public function __construct( + string $appName, IRequest $request, AccountService $accountService, IMailManager $mailManager, @@ -94,7 +98,9 @@ public function __construct(string $appName, IDkimService $dkimService, IUserPreferences $preferences, SnoozeService $snoozeService, - AiIntegrationsService $aiIntegrationService) { + AiIntegrationsService $aiIntegrationService, + private MessageOperationService $messageOperationService, + ) { parent::__construct($appName, $request); $this->accountService = $accountService; $this->mailManager = $mailManager; @@ -782,6 +788,24 @@ public function setFlags(int $id, array $flags): JSONResponse { return new JSONResponse(); } + /** + * + * @NoAdminRequired + * + * @param array $identifiers + * @param array $flags + * + * @return JSONResponse + */ + #[FrontpageRoute(verb: 'PUT', url: '/api/messages/flags')] + #[NoAdminRequired] + #[TrapError] + public function changeFlags(array $identifiers, array $flags): JSONResponse { + return new JSONResponse( + $this->messageOperationService->changeFlags($this->currentUserId, $identifiers, $flags) + ); + } + /** * @NoAdminRequired * diff --git a/lib/Db/MailAccountMapper.php b/lib/Db/MailAccountMapper.php index 0e41bbe976..78821b5dfc 100644 --- a/lib/Db/MailAccountMapper.php +++ b/lib/Db/MailAccountMapper.php @@ -66,6 +66,25 @@ public function findById(int $id): MailAccount { return $this->findEntity($query); } + /** + * Finds all mail accounts by account ids + * + * @param array $identifiers + * + * @return array + */ + public function findByIds(array $identifiers): array { + + $cmd = $this->db->getQueryBuilder(); + $cmd->select('*') + ->from($this->getTableName()) + ->where( + $cmd->expr()->in('id', $cmd->createNamedParameter($identifiers, IQueryBuilder::PARAM_STR_ARRAY), IQueryBuilder::PARAM_STR_ARRAY) + ); + + return $this->findEntities($cmd); + } + /** * Finds all Mail Accounts by user id existing for this user * diff --git a/lib/Db/MailboxMapper.php b/lib/Db/MailboxMapper.php index 434c63fd10..3e316d4eb3 100644 --- a/lib/Db/MailboxMapper.php +++ b/lib/Db/MailboxMapper.php @@ -113,7 +113,9 @@ public function findById(int $id): Mailbox { } /** - * @return Mailbox[] + * @param array $ids + * + * @return array * * @throws Exception */ diff --git a/lib/Db/MessageMapper.php b/lib/Db/MessageMapper.php index cdc0e71870..d8a637fbc1 100644 --- a/lib/Db/MessageMapper.php +++ b/lib/Db/MessageMapper.php @@ -178,6 +178,28 @@ public function findUidsForIds(Mailbox $mailbox, array $ids) { }, array_chunk($ids, 1000)); } + /** + * @param array $identifiers + * + * @return array + */ + public function findMailboxAndUid(array $identifiers): array { + + if ($identifiers === []) { + return []; + } + + $cmd = $this->db->getQueryBuilder(); + $cmd->select('id', 'mailbox_id', 'uid') + ->from($this->getTableName()) + ->where( + $cmd->expr()->in('id', $cmd->createNamedParameter($identifiers, IQueryBuilder::PARAM_STR_ARRAY), IQueryBuilder::PARAM_STR_ARRAY) + ); + + return $cmd->executeQuery()->fetchAll(); + + } + /** * @param Account $account * diff --git a/lib/IMAP/MessageMapper.php b/lib/IMAP/MessageMapper.php index f076d6f4f4..e3281ae45d 100644 --- a/lib/IMAP/MessageMapper.php +++ b/lib/IMAP/MessageMapper.php @@ -435,6 +435,44 @@ public function removeFlag(Horde_Imap_Client_Socket $client, ); } + /** + * @throws Horde_Imap_Client_Exception + */ + public function setFlags( + Horde_Imap_Client_Socket $client, + Mailbox $mailbox, + array $uids, + array $addFlags = [], + array $removeFlags = [], + array $replaceFlags = [], + ): array { + + if (count($uids) === 0) { + return []; + } + + $cmd = []; + + if (count($replaceFlags) > 0) { + $cmd['replace'] = $replaceFlags; + } else { + if (count($addFlags) > 0) { + $cmd['add'] = $addFlags; + } + if (count($removeFlags) > 0) { + $cmd['remove'] = $removeFlags; + } + } + + if (count($cmd) > 0) { + $cmd['ids'] = new Horde_Imap_Client_Ids($uids); + $result = $client->store($mailbox->getName(), $cmd); + return $result->ids; + } + + return []; + } + /** * @param Horde_Imap_Client_Socket $client * @param Mailbox $mailbox diff --git a/lib/Service/MessageOperationService.php b/lib/Service/MessageOperationService.php new file mode 100644 index 0000000000..c1e6e8000e --- /dev/null +++ b/lib/Service/MessageOperationService.php @@ -0,0 +1,153 @@ + [[id, uid]]] + * + * @param array $collection + * + * @return array> + */ + protected function groupByMailbox(array $collection): array { + return array_reduce($collection, function ($carry, $pair) { + if (!isset($carry[$pair['mailbox_id']])) { + $carry[$pair['mailbox_id']] = []; + } + $carry[$pair['mailbox_id']][] = ['id' => $pair['id'], 'uid' => $pair['uid']]; + return $carry; + }, []); + } + + /** + * convert mailbox collection to grouped collections by account id + * + * [mailbox] to [account_id => [mailbox]] + * + * @param array<\OCA\Mail\Db\MailBox> $collection + * + * @return array> + */ + protected function groupByAccount(array $collection) { + return array_reduce($collection, function ($carry, $entry) { + if (!isset($carry[$entry->getAccountId()])) { + $carry[$entry->getAccountId()] = []; + } + $carry[$entry->getAccountId()][] = $entry; + return $carry; + }, []); + } + + /** + * generates operation status responses for each message + * + * @param array &$results + * @param bool $value + * @param array<\OCA\Mail\Db\MailBox> $mailboxes + * @param array> $messages + */ + protected function generateResult(array &$results, bool $value, array $mailboxes, array $messages) { + foreach ($mailboxes as $mailbox) { + foreach ($messages[$mailbox->getId()] as $message) { + $results[$message['id']] = $value; + } + } + } + + /** + * Set/Unset system flags or keywords + * + * @param string $userId system user id + * @param array $identifiers message ids + * @param array $flags message flags + * + * @return array operation results + */ + public function changeFlags(string $userId, array $identifiers, array $flags): array { + + // retrieve message meta data [uid, mailbox_id] for all messages and group by mailbox id + $messages = $this->groupByMailbox($this->messageMapper->findMailboxAndUid($identifiers)); + // retrieve all mailboxes and group by account + $mailboxes = $this->groupByAccount($this->mailboxMapper->findByIds(array_keys($messages))); + // retrieve all accounts + $accounts = $this->accountMapper->findByIds(array_keys($mailboxes)); + // process every account + $results = []; + foreach ($accounts as $account) { + $account = new Account($account); + // determine if account belongs to the user and skip if not + if ($account->getUserId() != $userId) { + // add messages to results as failed + $this->generateResult($results, false, $mailboxes[$account->getId()], $messages); + continue; + } + $client = $this->clientFactory->getClient($account); + // process every mailbox + foreach ($mailboxes[$account->getId()] as $mailbox) { + try { + // check if specific flags are supported and group them by action + $addFlags = []; + $removeFlags = []; + foreach ($flags as $flag => $value) { + $value = filter_var($value, FILTER_VALIDATE_BOOLEAN); + $imapFlags = $this->mailManager->filterFlags($client, $account, $flag, $mailbox->getName()); + if (empty($imapFlags)) { + continue; + } + if ($value) { + $addFlags = array_merge($addFlags, $imapFlags); + } else { + $removeFlags = array_merge($removeFlags, $imapFlags); + } + } + // apply flags to messages on server + $this->imapMessageMapper->setFlags( + $client, + $mailbox, + array_column($messages[$mailbox->getId()], 'uid'), + $addFlags, + $removeFlags + ); + // add messages to results as successful + $this->generateResult($results, true, [$mailbox], $messages); + } catch (Throwable $e) { + // add messages to results as failed + $this->generateResult($results, false, [$mailbox], $messages); + } + } + $client->logout(); + } + + return $results; + } + +} diff --git a/src/components/EnvelopeList.vue b/src/components/EnvelopeList.vue index 253678177e..e2b56f66ed 100644 --- a/src/components/EnvelopeList.vue +++ b/src/components/EnvelopeList.vue @@ -67,7 +67,7 @@ }} + @click.prevent="markSelectedFavoriteOrUnfavorite"> @@ -414,12 +414,11 @@ export default { return this.selection.includes(idx) }, markSelectedSeenOrUnseen() { - const seen = !this.areAllSelectedRead - this.selectedEnvelopes.forEach((envelope) => { - this.$store.dispatch('toggleEnvelopeSeen', { - envelope, - seen, - }) + const state = !this.areAllSelectedRead + const envelopes = this.selectedEnvelopes + this.$store.dispatch('markEnvelopesSeenOrUnseen', { + envelopes, + state, }) this.unselectAll() }, @@ -442,6 +441,14 @@ export default { this.unselectAll() }, async markSelectionJunk() { + const state = true + const envelopes = this.selectedEnvelopes + this.$store.dispatch('markEnvelopesJunkOrNotJunk', { + envelopes, + state, + }) + this.unselectAll() + /* for (const envelope of this.selectedEnvelopes) { if (!envelope.flags.$junk) { await this.$store.dispatch('toggleEnvelopeJunk', { @@ -451,8 +458,17 @@ export default { } } this.unselectAll() + */ }, async markSelectionNotJunk() { + const state = false + const envelopes = this.selectedEnvelopes + this.$store.dispatch('markEnvelopesJunkOrNotJunk', { + envelopes, + state, + }) + this.unselectAll() + /* for (const envelope of this.selectedEnvelopes) { if (envelope.flags.$junk) { await this.$store.dispatch('toggleEnvelopeJunk', { @@ -462,14 +478,14 @@ export default { } } this.unselectAll() - }, - favoriteOrUnfavoriteAll() { - const favFlag = !this.areAllSelectedFavorite - this.selectedEnvelopes.forEach((envelope) => { - this.$store.dispatch('markEnvelopeFavoriteOrUnfavorite', { - envelope, - favFlag, - }) + */ + }, + markSelectedFavoriteOrUnfavorite() { + const state = !this.areAllSelectedFavorite + const envelopes = this.selectedEnvelopes + this.$store.dispatch('markEnvelopesFavoriteOrUnfavorite', { + envelopes, + state, }) this.unselectAll() }, diff --git a/src/service/MessageService.js b/src/service/MessageService.js index 7ee429e576..fd4a391b10 100644 --- a/src/service/MessageService.js +++ b/src/service/MessageService.js @@ -115,15 +115,14 @@ export async function clearCache(accountId, id) { /** * Set flags for envelope * - * @param {int} id + * @param {array} identifiers * @param {object} flags */ -export async function setEnvelopeFlags(id, flags) { - const url = generateUrl('/apps/mail/api/messages/{id}/flags', { - id, - }) +export async function setEnvelopeFlags(identifiers, flags) { + const url = generateUrl('/apps/mail/api/messages/flags') return await axios.put(url, { + identifiers, flags, }) } diff --git a/src/store/actions.js b/src/store/actions.js index a6324b58cf..3c4c8013b7 100644 --- a/src/store/actions.js +++ b/src/store/actions.js @@ -912,7 +912,7 @@ export default { }) try { - await setEnvelopeFlags(envelope.databaseId, { + await setEnvelopeFlags([envelope.databaseId], { flagged: !oldState, }) } catch (error) { @@ -960,8 +960,8 @@ export default { }) try { - await setEnvelopeFlags(envelope.databaseId, { - seen: newState, + await setEnvelopeFlags([envelope.databaseId], { + seen: newState }) } catch (error) { console.error('could not toggle message seen state', error) @@ -997,7 +997,7 @@ export default { } try { - await setEnvelopeFlags(envelope.databaseId, { + await setEnvelopeFlags([envelope.databaseId], { $junk: !oldState, $notjunk: oldState, }) @@ -1024,32 +1024,72 @@ export default { } }) }, - async markEnvelopeFavoriteOrUnfavorite({ commit, getters }, { envelope, favFlag }) { + async markEnvelopesSeenOrUnseen({ commit, getters }, { envelopes, state }) { return handleHttpAuthErrors(commit, async () => { - // Change immediately and switch back on error - const oldState = envelope.flags.flagged - commit('flagEnvelope', { - envelope, - flag: 'flagged', - value: favFlag, + try { + const identifiers = [] + envelopes.forEach((envelope) => { + identifiers.push(envelope.databaseId) + }) + await setEnvelopeFlags(identifiers, { seen: state }) + } catch (error) { + console.error('could not mark messages seen or unseen', error) + throw error + } + envelopes.forEach((envelope) => { + commit('flagEnvelope', { + envelope, + flag: 'seen', + value: state, + }) }) - + }) + }, + async markEnvelopesJunkOrNotJunk({ commit, getters }, { envelopes, state }) { + return handleHttpAuthErrors(commit, async () => { try { - await setEnvelopeFlags(envelope.databaseId, { - flagged: favFlag, + const identifiers = [] + envelopes.forEach((envelope) => { + identifiers.push(envelope.databaseId) }) + await setEnvelopeFlags(identifiers, { junk: state, notjunk: !state }) } catch (error) { - console.error('could not favorite/unfavorite message ' + envelope.uid, error) - - // Revert change + console.error('could not mark messages junk or not junk', error) + throw error + } + envelopes.forEach((envelope) => { commit('flagEnvelope', { envelope, - flag: 'flagged', - value: oldState, + flag: '$junk', + value: state, }) - + commit('flagEnvelope', { + envelope, + flag: '$notjunk', + value: !state, + }) + }) + }) + }, + async markEnvelopesFavoriteOrUnfavorite({ commit, getters }, { envelopes, state }) { + return handleHttpAuthErrors(commit, async () => { + try { + const identifiers = [] + envelopes.forEach((envelope) => { + identifiers.push(envelope.databaseId) + }) + await setEnvelopeFlags(identifiers, { flagged: state }) + } catch (error) { + console.error('could not mark messages favorite or unfavorite', error) throw error } + envelopes.forEach((envelope) => { + commit('flagEnvelope', { + envelope, + flag: 'flagged', + value: state, + }) + }) }) }, async markEnvelopeImportantOrUnimportant({ commit, dispatch, getters }, { envelope, addTag }) { diff --git a/vite.config.js b/vite.config.js index 789c28a71a..40f2edf9fb 100644 --- a/vite.config.js +++ b/vite.config.js @@ -22,6 +22,7 @@ export default createAppConfig({ config: { build: { cssCodeSplit: false, + minify: false, }, plugins: [ ckeditor5({ theme: require.resolve('@ckeditor/ckeditor5-theme-lark') }), From 442750b0c7fe798f834cf23d5f89ec66a6d8b885 Mon Sep 17 00:00:00 2001 From: SebastianKrupinski Date: Sat, 23 Nov 2024 00:06:18 -0500 Subject: [PATCH 2/8] fixup! fix: perform bulk message actions Signed-off-by: SebastianKrupinski --- tests/Unit/Controller/MessagesControllerTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/Unit/Controller/MessagesControllerTest.php b/tests/Unit/Controller/MessagesControllerTest.php index e83f5b7fe5..d91ed15e5d 100644 --- a/tests/Unit/Controller/MessagesControllerTest.php +++ b/tests/Unit/Controller/MessagesControllerTest.php @@ -36,6 +36,7 @@ use OCA\Mail\Service\AiIntegrations\AiIntegrationsService; use OCA\Mail\Service\ItineraryService; use OCA\Mail\Service\MailManager; +use OCA\Mail\Service\MessageOperationService; use OCA\Mail\Service\SmimeService; use OCA\Mail\Service\SnoozeService; use OCP\AppFramework\Db\DoesNotExistException; @@ -128,6 +129,8 @@ class MessagesControllerTest extends TestCase { /** @var MockObject|AiIntegrationsService */ private $aiIntegrationsService; + private MessageOperationService|MockObject $messageOperationService; + protected function setUp(): void { parent::setUp(); @@ -153,6 +156,7 @@ protected function setUp(): void { $this->userPreferences = $this->createMock(IUserPreferences::class); $this->snoozeService = $this->createMock(SnoozeService::class); $this->aiIntegrationsService = $this->createMock(AiIntegrationsService::class); + $this->messageOperationService = $this->createMock(MessageOperationService::class); $timeFactory = $this->createMocK(ITimeFactory::class); $timeFactory->expects($this->any()) @@ -185,6 +189,7 @@ protected function setUp(): void { $this->userPreferences, $this->snoozeService, $this->aiIntegrationsService, + $this->messageOperationService ); $this->account = $this->createMock(Account::class); From 47361c7fa7bd7d4746ba2ab32acfde5f3c75e303 Mon Sep 17 00:00:00 2001 From: SebastianKrupinski Date: Tue, 17 Dec 2024 20:02:31 -0500 Subject: [PATCH 3/8] Fix: perform bulk message actions Signed-off-by: SebastianKrupinski --- src/components/EnvelopeList.vue | 92 ++++++++++++--------------------- src/store/actions.js | 2 +- 2 files changed, 35 insertions(+), 59 deletions(-) diff --git a/src/components/EnvelopeList.vue b/src/components/EnvelopeList.vue index 728a9c307c..8d2cef49b3 100644 --- a/src/components/EnvelopeList.vue +++ b/src/components/EnvelopeList.vue @@ -24,28 +24,28 @@ + @click.prevent="markSelectedImportant"> + @click.prevent="markSelectedUnimportant"> + @click.prevent="markSelectedUnfavorite"> + @click.prevent="markSelectedFavorite"> @@ -71,14 +71,14 @@ + @click.prevent="markSelectedJunk"> {{ n('mail', 'Mark {number} as spam', 'Mark {number} as spam', selection.length, { number: selection.length }) }} + @click.prevent="markSelectedNotJunk"> @@ -321,25 +321,25 @@ export default { return this.selection.includes(idx) }, - markSelectedRead() { - this.selectedEnvelopes.forEach((envelope) => { - this.$store.dispatch('toggleEnvelopeSeen', { - envelope, - seen: true, - }) + async markSelectedRead() { + const state = true + const envelopes = this.selectedEnvelopes + this.$store.dispatch('markEnvelopesSeenOrUnseen', { + envelopes, + state, }) this.unselectAll() }, - markSelectedUnread() { - this.selectedEnvelopes.forEach((envelope) => { - this.$store.dispatch('toggleEnvelopeSeen', { - envelope, - seen: false, - }) + async markSelectedUnread() { + const state = false + const envelopes = this.selectedEnvelopes + this.$store.dispatch('markEnvelopesSeenOrUnseen', { + envelopes, + state, }) this.unselectAll() }, - markSelectionImportant() { + markSelectedImportant() { this.selectedEnvelopes.forEach((envelope) => { this.$store.dispatch('markEnvelopeImportantOrUnimportant', { envelope, @@ -348,7 +348,7 @@ export default { }) this.unselectAll() }, - markSelectionUnimportant() { + markSelectedUnimportant() { this.selectedEnvelopes.forEach((envelope) => { this.$store.dispatch('markEnvelopeImportantOrUnimportant', { envelope, @@ -357,7 +357,7 @@ export default { }) this.unselectAll() }, - async markSelectionJunk() { + async markSelectedJunk() { const state = true const envelopes = this.selectedEnvelopes this.$store.dispatch('markEnvelopesJunkOrNotJunk', { @@ -365,19 +365,8 @@ export default { state, }) this.unselectAll() - /* - for (const envelope of this.selectedEnvelopes) { - if (!envelope.flags.$junk) { - await this.$store.dispatch('toggleEnvelopeJunk', { - envelope, - removeEnvelope: await this.$store.dispatch('moveEnvelopeToJunk', envelope), - }) - } - } - this.unselectAll() - */ }, - async markSelectionNotJunk() { + async markSelectedNotJunk() { const state = false const envelopes = this.selectedEnvelopes this.$store.dispatch('markEnvelopesJunkOrNotJunk', { @@ -385,35 +374,22 @@ export default { state, }) this.unselectAll() - /* - for (const envelope of this.selectedEnvelopes) { - if (envelope.flags.$junk) { - await this.$store.dispatch('toggleEnvelopeJunk', { - envelope, - removeEnvelope: await this.$store.dispatch('moveEnvelopeToJunk', envelope), - }) - } - } - this.unselectAll() - */ }, - favoriteAll() { - const favFlag = !this.isAtLeastOneSelectedUnFavorite - this.selectedEnvelopes.forEach((envelope) => { - this.$store.dispatch('markEnvelopeFavoriteOrUnfavorite', { - envelope, - favFlag, - }) + async markSelectedFavorite() { + const state = true + const envelopes = this.selectedEnvelopes + this.$store.dispatch('markEnvelopesFavoriteOrUnfavorite', { + envelopes, + state, }) this.unselectAll() }, - unFavoriteAll() { - const favFlag = !this.isAtLeastOneSelectedFavorite - this.selectedEnvelopes.forEach((envelope) => { - this.$store.dispatch('markEnvelopeFavoriteOrUnfavorite', { - envelope, - favFlag, - }) + async markSelectedUnfavorite() { + const state = false + const envelopes = this.selectedEnvelopes + this.$store.dispatch('markEnvelopesFavoriteOrUnfavorite', { + envelopes, + state, }) this.unselectAll() }, diff --git a/src/store/actions.js b/src/store/actions.js index 3c4c8013b7..cdca55be8a 100644 --- a/src/store/actions.js +++ b/src/store/actions.js @@ -1052,7 +1052,7 @@ export default { envelopes.forEach((envelope) => { identifiers.push(envelope.databaseId) }) - await setEnvelopeFlags(identifiers, { junk: state, notjunk: !state }) + await setEnvelopeFlags(identifiers, { $junk: state, $notjunk: !state }) } catch (error) { console.error('could not mark messages junk or not junk', error) throw error From def7d68fd46bc75f58af209f024c55b129332686 Mon Sep 17 00:00:00 2001 From: SebastianKrupinski Date: Tue, 17 Dec 2024 20:33:10 -0500 Subject: [PATCH 4/8] fixup! Fix: perform bulk message actions Signed-off-by: SebastianKrupinski --- lib/Controller/MessagesController.php | 2 +- lib/Db/MailboxMapper.php | 2 +- lib/Service/MessageOperationService.php | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/Controller/MessagesController.php b/lib/Controller/MessagesController.php index 495aa09807..2457d5cda2 100755 --- a/lib/Controller/MessagesController.php +++ b/lib/Controller/MessagesController.php @@ -793,7 +793,7 @@ public function setFlags(int $id, array $flags): JSONResponse { * @NoAdminRequired * * @param array $identifiers - * @param array $flags + * @param array $flags * * @return JSONResponse */ diff --git a/lib/Db/MailboxMapper.php b/lib/Db/MailboxMapper.php index 3e316d4eb3..eacb4981b3 100644 --- a/lib/Db/MailboxMapper.php +++ b/lib/Db/MailboxMapper.php @@ -113,7 +113,7 @@ public function findById(int $id): Mailbox { } /** - * @param array $ids + * @param array $ids * * @return array * diff --git a/lib/Service/MessageOperationService.php b/lib/Service/MessageOperationService.php index c1e6e8000e..2b01ffd077 100644 --- a/lib/Service/MessageOperationService.php +++ b/lib/Service/MessageOperationService.php @@ -36,7 +36,7 @@ public function __construct( * * @param array $collection * - * @return array> + * @return array> */ protected function groupByMailbox(array $collection): array { return array_reduce($collection, function ($carry, $pair) { @@ -53,9 +53,9 @@ protected function groupByMailbox(array $collection): array { * * [mailbox] to [account_id => [mailbox]] * - * @param array<\OCA\Mail\Db\MailBox> $collection + * @param array $collection * - * @return array> + * @return array> */ protected function groupByAccount(array $collection) { return array_reduce($collection, function ($carry, $entry) { @@ -72,8 +72,8 @@ protected function groupByAccount(array $collection) { * * @param array &$results * @param bool $value - * @param array<\OCA\Mail\Db\MailBox> $mailboxes - * @param array> $messages + * @param array $mailboxes + * @param array> $messages */ protected function generateResult(array &$results, bool $value, array $mailboxes, array $messages) { foreach ($mailboxes as $mailbox) { From 8a4525e75e3878c1e1d1dfa3cc850d4a65865a03 Mon Sep 17 00:00:00 2001 From: SebastianKrupinski Date: Tue, 17 Dec 2024 20:50:15 -0500 Subject: [PATCH 5/8] fixup! Fix: perform bulk message actions Signed-off-by: SebastianKrupinski --- lib/Db/MailAccountMapper.php | 14 +++++++++----- lib/Db/MessageMapper.php | 8 ++++---- lib/Service/MessageOperationService.php | 14 ++++---------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/lib/Db/MailAccountMapper.php b/lib/Db/MailAccountMapper.php index 78821b5dfc..76a9b3cb3e 100644 --- a/lib/Db/MailAccountMapper.php +++ b/lib/Db/MailAccountMapper.php @@ -69,20 +69,24 @@ public function findById(int $id): MailAccount { /** * Finds all mail accounts by account ids * + * @param string $userId * @param array $identifiers * * @return array */ - public function findByIds(array $identifiers): array { + public function findByIds(string $userId, array $identifiers): array { - $cmd = $this->db->getQueryBuilder(); - $cmd->select('*') + $qb = $this->db->getQueryBuilder(); + $qb->select('*') ->from($this->getTableName()) ->where( - $cmd->expr()->in('id', $cmd->createNamedParameter($identifiers, IQueryBuilder::PARAM_STR_ARRAY), IQueryBuilder::PARAM_STR_ARRAY) + $qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR), IQueryBuilder::PARAM_STR) + ) + ->andWhere( + $qb->expr()->in('id', $qb->createNamedParameter($identifiers, IQueryBuilder::PARAM_STR_ARRAY), IQueryBuilder::PARAM_STR_ARRAY) ); - return $this->findEntities($cmd); + return $this->findEntities($qb); } /** diff --git a/lib/Db/MessageMapper.php b/lib/Db/MessageMapper.php index d8a637fbc1..555a090fe6 100644 --- a/lib/Db/MessageMapper.php +++ b/lib/Db/MessageMapper.php @@ -189,14 +189,14 @@ public function findMailboxAndUid(array $identifiers): array { return []; } - $cmd = $this->db->getQueryBuilder(); - $cmd->select('id', 'mailbox_id', 'uid') + $qb = $this->db->getQueryBuilder(); + $qb->select('id', 'mailbox_id', 'uid') ->from($this->getTableName()) ->where( - $cmd->expr()->in('id', $cmd->createNamedParameter($identifiers, IQueryBuilder::PARAM_STR_ARRAY), IQueryBuilder::PARAM_STR_ARRAY) + $qb->expr()->in('id', $qb->createNamedParameter($identifiers, IQueryBuilder::PARAM_STR_ARRAY), IQueryBuilder::PARAM_STR_ARRAY) ); - return $cmd->executeQuery()->fetchAll(); + return $qb->executeQuery()->fetchAll(); } diff --git a/lib/Service/MessageOperationService.php b/lib/Service/MessageOperationService.php index 2b01ffd077..ae596f8178 100644 --- a/lib/Service/MessageOperationService.php +++ b/lib/Service/MessageOperationService.php @@ -53,9 +53,9 @@ protected function groupByMailbox(array $collection): array { * * [mailbox] to [account_id => [mailbox]] * - * @param array $collection + * @param array<\OCA\Mail\Db\Mailbox> $collection * - * @return array> + * @return array> */ protected function groupByAccount(array $collection) { return array_reduce($collection, function ($carry, $entry) { @@ -72,7 +72,7 @@ protected function groupByAccount(array $collection) { * * @param array &$results * @param bool $value - * @param array $mailboxes + * @param array<\OCA\Mail\Db\Mailbox> $mailboxes * @param array> $messages */ protected function generateResult(array &$results, bool $value, array $mailboxes, array $messages) { @@ -99,17 +99,11 @@ public function changeFlags(string $userId, array $identifiers, array $flags): a // retrieve all mailboxes and group by account $mailboxes = $this->groupByAccount($this->mailboxMapper->findByIds(array_keys($messages))); // retrieve all accounts - $accounts = $this->accountMapper->findByIds(array_keys($mailboxes)); + $accounts = $this->accountMapper->findByIds($userId, array_keys($mailboxes)); // process every account $results = []; foreach ($accounts as $account) { $account = new Account($account); - // determine if account belongs to the user and skip if not - if ($account->getUserId() != $userId) { - // add messages to results as failed - $this->generateResult($results, false, $mailboxes[$account->getId()], $messages); - continue; - } $client = $this->clientFactory->getClient($account); // process every mailbox foreach ($mailboxes[$account->getId()] as $mailbox) { From bdfe6f2edceb04226d932a65b37216fef9e45c0a Mon Sep 17 00:00:00 2001 From: SebastianKrupinski Date: Tue, 17 Dec 2024 20:54:16 -0500 Subject: [PATCH 6/8] fixup! Fix: perform bulk message actions Signed-off-by: SebastianKrupinski --- lib/Service/MessageOperationService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Service/MessageOperationService.php b/lib/Service/MessageOperationService.php index ae596f8178..62bf3e1a31 100644 --- a/lib/Service/MessageOperationService.php +++ b/lib/Service/MessageOperationService.php @@ -36,7 +36,7 @@ public function __construct( * * @param array $collection * - * @return array> + * @return array */ protected function groupByMailbox(array $collection): array { return array_reduce($collection, function ($carry, $pair) { From fa0f79a4eb2a9e6d18fa46a8719a02cdb8f093ba Mon Sep 17 00:00:00 2001 From: SebastianKrupinski Date: Tue, 17 Dec 2024 21:02:45 -0500 Subject: [PATCH 7/8] fixup! Fix: perform bulk message actions Signed-off-by: SebastianKrupinski --- lib/Service/MessageOperationService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Service/MessageOperationService.php b/lib/Service/MessageOperationService.php index 62bf3e1a31..40ccd220b8 100644 --- a/lib/Service/MessageOperationService.php +++ b/lib/Service/MessageOperationService.php @@ -43,7 +43,7 @@ protected function groupByMailbox(array $collection): array { if (!isset($carry[$pair['mailbox_id']])) { $carry[$pair['mailbox_id']] = []; } - $carry[$pair['mailbox_id']][] = ['id' => $pair['id'], 'uid' => $pair['uid']]; + $carry[(int)$pair['mailbox_id']][] = ['id' => (int)$pair['id'], 'uid' => (int)$pair['uid']]; return $carry; }, []); } From ae9a9a8f2243e4c95b2e1dec71c94d4d56d0f789 Mon Sep 17 00:00:00 2001 From: SebastianKrupinski Date: Tue, 17 Dec 2024 21:08:52 -0500 Subject: [PATCH 8/8] fixup! Fix: perform bulk message actions Signed-off-by: SebastianKrupinski --- lib/Service/MessageOperationService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Service/MessageOperationService.php b/lib/Service/MessageOperationService.php index 40ccd220b8..ffe286cc30 100644 --- a/lib/Service/MessageOperationService.php +++ b/lib/Service/MessageOperationService.php @@ -36,7 +36,7 @@ public function __construct( * * @param array $collection * - * @return array + * @return array> */ protected function groupByMailbox(array $collection): array { return array_reduce($collection, function ($carry, $pair) {