diff --git a/appinfo/routes.php b/appinfo/routes.php index ea6758ce25..e10e058e48 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -89,6 +89,11 @@ 'url' => '/api/accounts/{id}/quota', 'verb' => 'GET' ], + [ + 'name' => 'mailboxes#patch', + 'url' => '/api/mailboxes/{id}', + 'verb' => 'PATCH' + ], [ 'name' => 'mailboxes#sync', 'url' => '/api/mailboxes/{id}/sync', diff --git a/lib/Contracts/IMailManager.php b/lib/Contracts/IMailManager.php index 212aee8a83..9c470412b3 100644 --- a/lib/Contracts/IMailManager.php +++ b/lib/Contracts/IMailManager.php @@ -176,6 +176,19 @@ public function flagMessage(Account $account, string $mailbox, int $uid, string */ public function getQuota(Account $account): ?Quota; + /** + * Rename a mailbox and get the new (cached) version + * + * @param Account $account + * @param Mailbox $mailbox + * @param string $name + * + * @return Mailbox + * + * @throw ServiceException + */ + public function renameMailbox(Account $account, Mailbox $mailbox, string $name): Mailbox; + /** * @param Account $account * @param Mailbox $mailbox diff --git a/lib/Controller/MailboxesController.php b/lib/Controller/MailboxesController.php index c7ad55007b..4a37192856 100644 --- a/lib/Controller/MailboxesController.php +++ b/lib/Controller/MailboxesController.php @@ -80,6 +80,7 @@ public function __construct(string $appName, * @TrapError * * @param int $accountId + * * @return JSONResponse * * @throws ClientException @@ -97,6 +98,31 @@ public function index(int $accountId): JSONResponse { ]); } + /** + * @NoAdminRequired + * @TrapError + * + * @param int $id + * @param string $name + * + * @return JSONResponse + */ + public function patch(int $id, + ?string $name = null): JSONResponse { + $mailbox = $this->mailManager->getMailbox($this->currentUserId, $id); + $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId()); + + if ($name !== null) { + $mailbox = $this->mailManager->renameMailbox( + $account, + $mailbox, + $name + ); + } + + return new JSONResponse($mailbox); + } + /** * @NoAdminRequired * @TrapError @@ -121,7 +147,7 @@ public function sync(int $id, array $ids = [], bool $init = false, string $query $mailbox, Horde_Imap_Client::SYNC_NEWMSGSUIDS | Horde_Imap_Client::SYNC_FLAGSUIDS | Horde_Imap_Client::SYNC_VANISHEDUIDS, array_map(function ($id) { - return (int) $id; + return (int)$id; }, $ids), !$init, $query @@ -235,7 +261,7 @@ public function destroy(int $id): JSONResponse { $mailbox = $this->mailManager->getMailbox($this->currentUserId, $id); $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId()); - $this->mailManager->deleteMailbox($account, $mailbox); + $this->mailManager->deleteMailbox($account, $mailbox); return new JSONResponse(); } } diff --git a/lib/IMAP/FolderMapper.php b/lib/IMAP/FolderMapper.php index 7f3f2795ef..b40d5abeba 100644 --- a/lib/IMAP/FolderMapper.php +++ b/lib/IMAP/FolderMapper.php @@ -139,6 +139,19 @@ public function getFoldersStatusAsObject(Horde_Imap_Client_Socket $client, ); } + /** + * @param Horde_Imap_Client_Socket $client + * @param string $oldName + * @param string $newName + * + * @throws Horde_Imap_Client_Exception + */ + public function renameFolder(Horde_Imap_Client_Socket $client, + string $oldName, + string $newName): void { + $client->renameMailbox($oldName, $newName); + } + /** * @param Folder[] $folders * diff --git a/lib/Service/MailManager.php b/lib/Service/MailManager.php index 09d04dcc38..7d417bec5a 100644 --- a/lib/Service/MailManager.php +++ b/lib/Service/MailManager.php @@ -389,6 +389,31 @@ public function getQuota(Account $account): ?Quota { ); } + public function renameMailbox(Account $account, Mailbox $mailbox, string $name): Mailbox { + /* + * 1. Rename on IMAP + */ + $this->folderMapper->renameFolder( + $this->imapClientFactory->getClient($account), + $mailbox->getName(), + $name + ); + + /** + * 2. Get the IMAP changes into our database cache + */ + $this->mailboxSync->sync($account, true); + + /** + * 3. Return the cached object with the new ID + */ + try { + return $this->mailboxMapper->find($account, $name); + } catch (DoesNotExistException $e) { + throw new ServiceException("The renamed mailbox $name does not exist", 0, $e); + } + } + /** * @param Account $account * @param Mailbox $mailbox diff --git a/src/components/NavigationMailbox.vue b/src/components/NavigationMailbox.vue index c649d49b97..49c07416e2 100644 --- a/src/components/NavigationMailbox.vue +++ b/src/components/NavigationMailbox.vue @@ -50,7 +50,6 @@ @click="markAsRead"> {{ t('mail', 'Mark all messages of this mailbox as read') }} - + + {{ t('mail', 'Edit name') }} + + {{ t('mail', 'Saving') }} @@ -101,6 +111,7 @@ import { getMailboxStatus } from '../service/MailboxService' import logger from '../logger' import { translatePlural as n } from '@nextcloud/l10n' import { translate as translateMailboxName } from '../i18n/MailboxTranslator' +import { showInfo } from '@nextcloud/dialogs' export default { name: 'NavigationMailbox', @@ -140,6 +151,10 @@ export default { editing: false, showSubMailboxes: false, menuOpen: false, + renameLabel: true, + renameInput: false, + mailboxName: this.mailbox.displayName, + } }, computed: { @@ -320,6 +335,33 @@ export default { } ) }, + async renameMailbox() { + this.renameInput = false + this.showSaving = false + + try { + await this.$store.dispatch('renameMailbox', { + account: this.account, + mailbox: this.mailbox, + newName: this.mailboxName, + }) + this.renameLabel = true + this.renameInput = false + this.showSaving = false + } catch (error) { + showInfo(t('mail', 'An error occurred, unable to rename the mailbox.')) + console.error(error) + this.renameLabel = false + this.renameInput = false + this.showSaving = true + } + }, + openRenameInput() { + // Hide label and show input + this.renameLabel = false + this.renameInput = true + this.showSaving = false + }, }, } diff --git a/src/service/MailboxService.js b/src/service/MailboxService.js index 0b0e38c4ed..5f8a1e30f1 100644 --- a/src/service/MailboxService.js +++ b/src/service/MailboxService.js @@ -46,3 +46,11 @@ export const deleteMailbox = async(id) => { await axios.delete(url) } +export async function patchMailbox(id, data) { + const url = generateUrl('/apps/mail/api/mailboxes/{id}', { + id, + }) + + const response = await axios.patch(url, data) + return response.data +} diff --git a/src/store/actions.js b/src/store/actions.js index a6d29a3b18..69a021f672 100644 --- a/src/store/actions.js +++ b/src/store/actions.js @@ -55,6 +55,7 @@ import { deleteMailbox, fetchAll as fetchAllMailboxes, markMailboxRead, + patchMailbox, } from '../service/MailboxService' import { deleteMessage, @@ -674,4 +675,13 @@ export default { await deleteAlias(account, aliasToDelete) commit('deleteAlias', { account, alias: aliasToDelete }) }, + async renameMailbox({ commit }, { account, mailbox, newName }) { + const newMailbox = await patchMailbox(mailbox.databaseId, { + name: newName, + }) + + console.debug(`mailbox ${mailbox.databaseId} renamed to ${newName}`, { mailbox }) + commit('removeMailbox', { id: mailbox.databaseId }) + commit('addMailbox', { account, mailbox: newMailbox }) + }, } diff --git a/src/store/mutations.js b/src/store/mutations.js index 13a54cadcb..3294cde990 100644 --- a/src/store/mutations.js +++ b/src/store/mutations.js @@ -222,4 +222,5 @@ export default { deleteAlias(state, { account, alias }) { account.aliases.splice(account.aliases.indexOf(alias), 1) }, + }