From 3648b3eb601e11e8f67afaa17838a2a3f7e2d822 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 13 Feb 2024 19:44:40 +0100 Subject: [PATCH] Resolve #64 --- dev/Model/FolderCollection.js | 26 +++++----- dev/Remote/User/Fetch.js | 10 ---- dev/View/Popup/Folder.js | 30 +++++++++-- .../libraries/MailSo/Imap/NamespaceResult.php | 12 ++--- .../app/libraries/MailSo/Mail/MailClient.php | 50 ++++--------------- .../libraries/RainLoop/Actions/Folders.php | 36 ++----------- .../templates/Views/User/PopupsFolder.html | 38 ++++++++------ 7 files changed, 83 insertions(+), 119 deletions(-) diff --git a/dev/Model/FolderCollection.js b/dev/Model/FolderCollection.js index 501543518..040c79c04 100644 --- a/dev/Model/FolderCollection.js +++ b/dev/Model/FolderCollection.js @@ -116,6 +116,8 @@ export class FolderCollectionModel extends AbstractCollectionModel this.namespace; this.optimized this.capabilities + this.allow; // allow adding +// this.exist; } */ @@ -303,6 +305,7 @@ export class FolderModel extends AbstractModel { super(); this.fullName = ''; + this.parentName = ''; this.delimiter = ''; this.deep = 0; this.expires = 0; @@ -323,12 +326,10 @@ export class FolderModel extends AbstractModel { focused: false, selected: false, - editing: false, isSubscribed: true, checkable: false, // Check for new messages askDelete: false, - nameForEdit: '', errorMsg: '', totalEmails: 0, @@ -348,7 +349,6 @@ export class FolderModel extends AbstractModel { this.addSubscribables({ kolabType: sValue => this.metadata[FolderMetadataKeys.KolabFolderType] = sValue, permanentFlags: aValue => this.tagsAllowed(aValue.includes('\\*')), - editing: value => value && this.nameForEdit(this.name()), unreadEmails: unread => FolderType.Inbox === this.type() && fireEvent('mailbox.inbox-unread-count', unread) }); @@ -485,18 +485,21 @@ export class FolderModel extends AbstractModel { showScreenPopup(FolderPopupView, [this]); } - rename() { + rename(nameToEdit, parentName) { + nameToEdit = nameToEdit.trim(); const folder = this, - nameToEdit = folder.nameForEdit().trim(); - if (nameToEdit && folder.name() !== nameToEdit) { + oldFullname = folder.fullName, + newFullname = parentName + folder.delimiter + nameToEdit; + if (nameToEdit && newFullname != oldFullname) { Remote.abort('Folders').post('FolderRename', FolderUserStore.foldersRenaming, { - folder: folder.fullName, - newFolderName: nameToEdit, + oldName: oldFullname, + newName: newFullname, subscribe: folder.isSubscribed() ? 1 : 0 }) - .then(data => { - folder.name(nameToEdit/*data.name*/); - if (folder.subFolders.length) { + .then(() => { + folder.fullName = newFullname; + folder.name(nameToEdit); + if (folder.subFolders.length || folder.parentName != parentName) { Remote.setTrigger(FolderUserStore.foldersLoading, true); // clearTimeout(Remote.foldersTimeout); // Remote.foldersTimeout = setTimeout(loadFolders, 500); @@ -504,7 +507,6 @@ export class FolderModel extends AbstractModel { // TODO: rename all subfolders with folder.delimiter to prevent reload? } else { removeFolderFromCacheList(folder.fullName); - folder.fullName = data.Result.fullName; setFolder(folder); const parent = getFolderFromCacheList(folder.parentName); sortFolders(parent ? parent.subFolders : FolderUserStore.folderList); diff --git a/dev/Remote/User/Fetch.js b/dev/Remote/User/Fetch.js index ddf624509..e235cc62f 100644 --- a/dev/Remote/User/Fetch.js +++ b/dev/Remote/User/Fetch.js @@ -64,16 +64,6 @@ class RemoteUserFetch extends AbstractFetchRemote { [key]: value }); } - -/* - folderMove(sPrevFolderFullName, sNewFolderFullName, bSubscribe) { - return this.post('FolderMove', FolderUserStore.foldersRenaming, { - folder: sPrevFolderFullName, - newFolder: sNewFolderFullName, - subscribe: bSubscribe ? 1 : 0 - }); - } -*/ } export default new RemoteUserFetch(); diff --git a/dev/View/Popup/Folder.js b/dev/View/Popup/Folder.js index eb4f13974..221d4172c 100644 --- a/dev/View/Popup/Folder.js +++ b/dev/View/Popup/Folder.js @@ -1,24 +1,42 @@ import { AbstractViewPopup } from 'Knoin/AbstractViews'; -import { addObservablesTo } from 'External/ko'; +import { addObservablesTo, koComputable } from 'External/ko'; import Remote from 'Remote/User/Fetch'; import { FolderUserStore } from 'Stores/User/Folder'; +import { defaultOptionsAfterRender } from 'Common/Utils'; +import { folderListOptionsBuilder } from 'Common/Folders'; + export class FolderPopupView extends AbstractViewPopup { constructor() { super('Folder'); addObservablesTo(this, { - folder: null // FolderModel + folder: null, // FolderModel + parentFolder: '', + name: '', + editing: false }); this.ACLAllowed = FolderUserStore.hasCapability('ACL'); this.ACL = ko.observableArray(); + + this.parentFolderSelectList = koComputable(() => + folderListOptionsBuilder( + [], + [['', '']], + oItem => oItem ? oItem.detailedName() : '', + item => !item.subFolders.allow + || (FolderUserStore.namespace && !item.fullName.startsWith(FolderUserStore.namespace)) + ) + ); + + this.defaultOptionsAfterRender = defaultOptionsAfterRender; } afterHide() { - this.folder().editing(false); + this.editing(false); } submitForm(form) { - this.folder().rename(); + this.folder().rename(this.name(), this.parentFolder()); console.dir({form}); this.close(); } @@ -32,7 +50,9 @@ export class FolderPopupView extends AbstractViewPopup { }, { folder: folder.fullName }); - folder.editing(!folder.type() && folder.exists && folder.selectable()); + this.editing(!folder.type() && folder.exists && folder.selectable()); + this.name(folder.name()), + this.parentFolder(folder.parentName); this.folder(folder); } } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/NamespaceResult.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/NamespaceResult.php index f92b39a80..a0d2eb971 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/NamespaceResult.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/NamespaceResult.php @@ -29,7 +29,7 @@ class NamespaceResult implements \JsonSerializable if (\is_array($entry) && 2 <= \count($entry)) { $this->aPersonal[] = [ 'prefix' => \array_shift($entry), - 'separator' => \array_shift($entry), + 'delimiter' => \array_shift($entry), 'extension' => $entry ]; } @@ -40,7 +40,7 @@ class NamespaceResult implements \JsonSerializable if (\is_array($entry) && 2 <= \count($entry)) { $this->aOtherUsers[] = [ 'prefix' => \array_shift($entry), - 'separator' => \array_shift($entry), + 'delimiter' => \array_shift($entry), 'extension' => $entry ]; } @@ -51,7 +51,7 @@ class NamespaceResult implements \JsonSerializable if (\is_array($entry) && 2 <= \count($entry)) { $this->aShared[] = [ 'prefix' => \array_shift($entry), - 'separator' => \array_shift($entry), + 'delimiter' => \array_shift($entry), 'extension' => $entry ]; } @@ -64,9 +64,9 @@ class NamespaceResult implements \JsonSerializable $sPrefix = ''; if (isset($this->aPersonal[0])) { $sPrefix = $this->aPersonal[0]['prefix']; - $sSeparator = $this->aPersonal[0]['separator']; - if ('INBOX'.$sSeparator === \substr(\strtoupper($sPrefix), 0, 6)) { - $sPrefix = 'INBOX'.$sSeparator.\substr($sPrefix, 6); + $sDelimiter = $this->aPersonal[0]['delimiter']; + if ('INBOX'.$sDelimiter === \substr(\strtoupper($sPrefix), 0, 6)) { + $sPrefix = 'INBOX'.$sDelimiter.\substr($sPrefix, 6); }; } return $sPrefix; diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php index 93231d38d..958e5b818 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php @@ -947,50 +947,22 @@ class MailClient /** * @throws \InvalidArgumentException */ - public function FolderMove(string $sPrevFolderFullName, string $sNextFolderFullNameInUtf, bool $bSubscribeOnMove = true) : self - { - if (!$this->oImapClient->FolderHierarchyDelimiter($sPrevFolderFullName)) { - // TODO: Translate - throw new \MailSo\RuntimeException('Cannot move non-existent folder.'); - } - return $this->folderModify($sPrevFolderFullName, $sNextFolderFullNameInUtf, $bSubscribeOnMove); - } - - /** - * @throws \InvalidArgumentException - */ - public function FolderRename(string $sPrevFolderFullName, string $sNewTopFolderNameInUtf, bool $bSubscribeOnRename = true) : string - { - $sDelimiter = $this->oImapClient->FolderHierarchyDelimiter($sPrevFolderFullName); - if (!$sDelimiter) { - // TODO: Translate - throw new \MailSo\RuntimeException('Cannot rename non-existent folder.'); - } - - if (\strlen($sDelimiter) && false !== \strpos($sNewTopFolderNameInUtf, $sDelimiter)) { - // TODO: Translate - throw new \MailSo\RuntimeException('New folder name contains delimiter.'); - } - - $iLast = \strrpos($sPrevFolderFullName, $sDelimiter); - $sNewFolderFullName = (false === $iLast ? '' : \substr($sPrevFolderFullName, 0, $iLast + 1)) - . $sNewTopFolderNameInUtf; - - $this->folderModify($sPrevFolderFullName, $sNewFolderFullName, $bSubscribeOnRename); - - return $sNewFolderFullName; - } - - /** - * @throws \ValueError - * @throws \MailSo\RuntimeException - */ - protected function folderModify(string $sPrevFolderFullName, string $sNewFolderFullName, bool $bSubscribe) : self + public function FolderRename(string $sPrevFolderFullName, string $sNewFolderFullName, bool $bSubscribe = true) : self { if (!\strlen($sPrevFolderFullName) || !\strlen($sNewFolderFullName)) { throw new \ValueError; } + if (!$this->oImapClient->FolderHierarchyDelimiter($sPrevFolderFullName)) { + // TODO: Translate + throw new \MailSo\RuntimeException('Cannot rename non-existent folder.'); + } +/* + if (\strlen($sDelimiter) && false !== \strpos($sNewFolderFullName, $sDelimiter)) { + // TODO: Translate + throw new \MailSo\RuntimeException('New folder name contains delimiter.'); + } +*/ $oSubscribedFolders = array(); if ($bSubscribe) { $oSubscribedFolders = $this->oImapClient->FolderSubscribeList($sPrevFolderFullName, '*'); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php index 23703c738..c16aafd4e 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php @@ -190,15 +190,15 @@ trait Folders /** * @throws \MailSo\RuntimeException */ - public function DoFolderMove() : array + public function DoFolderRename() : array { $this->initMailClientConnection(); try { - $this->MailClient()->FolderMove( - $this->GetActionParam('folder', ''), - $this->GetActionParam('newFolder', ''), + $sFullName = $this->MailClient()->FolderRename( + $this->GetActionParam('oldName', ''), + $this->GetActionParam('newName', ''), !empty($this->GetActionParam('subscribe', 1)) ); } @@ -210,34 +210,6 @@ trait Folders return $this->TrueResponse(); } - /** - * @throws \MailSo\RuntimeException - */ - public function DoFolderRename() : array - { - $this->initMailClientConnection(); - - $sName = $this->GetActionParam('newFolderName', ''); - try - { - $sFullName = $this->MailClient()->FolderRename( - $this->GetActionParam('folder', ''), - $sName, - !empty($this->GetActionParam('subscribe', 1)) - ); - } - catch (\Throwable $oException) - { - throw new ClientException(Notifications::CantRenameFolder, $oException); - } - -// FolderInformation(string $sFolderName, int $iPrevUidNext = 0, array $aUids = array()) - return $this->DefaultResponse(array( - 'name' => $sName, - 'fullName' => $sFullName, - )); - } - /** * @throws \MailSo\RuntimeException */ diff --git a/snappymail/v/0.0.0/app/templates/Views/User/PopupsFolder.html b/snappymail/v/0.0.0/app/templates/Views/User/PopupsFolder.html index 5b198593a..7e98d6a7b 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/PopupsFolder.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/PopupsFolder.html @@ -3,13 +3,31 @@