diff --git a/dev/Model/FolderCollection.js b/dev/Model/FolderCollection.js index eef7f5e77..74ac6ed3e 100644 --- a/dev/Model/FolderCollection.js +++ b/dev/Model/FolderCollection.js @@ -402,8 +402,6 @@ export class FolderModel extends AbstractModel { } ), - canBeEdited: () => !this.type() && this.exists/* && this.selectable()*/, - isSystemFolder: () => this.type() | (FolderUserStore.allowKolab() && !!this.kolabType() & !SettingsUserStore.unhideKolabFolders()), @@ -484,8 +482,7 @@ export class FolderModel extends AbstractModel { } edit() { -// this.canBeEdited() && this.editing(true); - this.canBeEdited() && showScreenPopup(FolderPopupView, [this]); + showScreenPopup(FolderPopupView, [this]); } unedit() { diff --git a/dev/Styles/User/SettingsFolders.less b/dev/Styles/User/SettingsFolders.less index 026a651df..fe6b7b276 100644 --- a/dev/Styles/User/SettingsFolders.less +++ b/dev/Styles/User/SettingsFolders.less @@ -15,6 +15,7 @@ } .folder-name { + cursor: pointer; word-break: break-all; white-space: pre-wrap; } @@ -24,10 +25,6 @@ opacity: 0.6; } - .folder-name.can-be-edited { - cursor: pointer; - } - select { width: auto; } diff --git a/dev/View/Popup/Folder.js b/dev/View/Popup/Folder.js index d9502d58a..10b0f3e60 100644 --- a/dev/View/Popup/Folder.js +++ b/dev/View/Popup/Folder.js @@ -1,8 +1,7 @@ -import { addObservablesTo } from 'External/ko'; - import { AbstractViewPopup } from 'Knoin/AbstractViews'; - +import { addObservablesTo } from 'External/ko'; import Remote from 'Remote/User/Fetch'; +import { FolderUserStore } from 'Stores/User/Folder'; export class FolderPopupView extends AbstractViewPopup { constructor() { @@ -10,7 +9,7 @@ export class FolderPopupView extends AbstractViewPopup { addObservablesTo(this, { folder: null // FolderModel }); - + this.ACLAllowed = FolderUserStore.hasCapability('ACL'); this.ACL = ko.observableArray(); } @@ -26,19 +25,14 @@ export class FolderPopupView extends AbstractViewPopup { beforeShow(folder) { this.ACL([]); - folder.editing(true); - this.folder(folder); - Remote.request('FolderACL', (iError, data) => { + this.ACLAllowed && Remote.request('FolderACL', (iError, data) => { if (!iError && data.Result) { - this.ACL( - Object.entries(data.Result).map(([key, value]) => { - value.identifier = key; - return value; - }) - ); + this.ACL(Object.values(data.Result)); } }, { folder: folder.fullName }); + !folder.type() && folder.exists && folder.selectable() && folder.editing(true); + this.folder(folder); } } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Commands/ACL.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Commands/ACL.php index 93d011e3e..109079887 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Commands/ACL.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Commands/ACL.php @@ -26,68 +26,82 @@ trait ACL */ public function ACLAllow(string $sFolderName, string $command) : bool { - if ($this->ACLDisabled) { + if ($this->ACLDisabled || !$this->hasCapability('ACL')) { return false; } // The "RIGHTS=" capability MUST NOT include any of the rights defined in RFC 2086: // "l", "r", "s", "w", "i", "p", "a", "c", "d", and the digits ("0" .. "9") // So it is: RIGHTS=texk - $mainRights = \str_split($this->CapabilityValue('RIGHTS') ?: ''); - if ($this->hasCapability('ACL') || $mainRights) { - if ('MYRIGHTS' === $command) { - // at least one of the "l", "r", "i", "k", "x", "a" rights is required - return true; - } - $rights = $this->FolderMyRights($sFolderName); - if ($rights) { - switch ($command) - { - case 'LIST': - case 'LSUB': - return $rights->hasRight('LOOKUP'); - case 'CREATE': - return true; // $parent->$rights->hasRight('k'); - case 'DELETE': - return $rights->hasRight('x'); - case 'RENAME': - return $rights->hasRight('k') && $rights->hasRight('x'); - case 'SELECT': - case 'EXAMINE': - case 'STATUS': - return $rights->hasRight('r'); - case 'APPEND': - case 'COPY': - return $rights->hasRight('i'); - case 'EXPUNGE': - return $rights->hasRight('e'); +// $mainRights = \str_split($this->CapabilityValue('RIGHTS') ?: ''); + + if ('MYRIGHTS' === $command) { + // at least one of the "l", "r", "i", "k", "x", "a" rights is required + return true; + } + + if (\in_array($command, ['GETACL','SETACL','LISTRIGHTS','DELETEACL'])) { + return true; + } + + $rights = $this->FolderMyRights($sFolderName); + if ($rights) { + switch ($command) + { + case 'LIST': + case 'LSUB': + return $rights->hasRight('LOOKUP'); + case 'CREATE': + return true; // $parent->$rights->hasRight('k'); + case 'DELETE': + return $rights->hasRight('x'); + case 'RENAME': + return $rights->hasRight('k') && $rights->hasRight('x'); + case 'SELECT': + case 'EXAMINE': + case 'STATUS': + return $rights->hasRight('r'); + case 'APPEND': + case 'COPY': + return $rights->hasRight('i'); + case 'EXPUNGE': + return $rights->hasRight('e'); /* - case 'SUBSCRIBE': - return $rights->hasRight('l') || true; - case 'UNSUBSCRIBE': - return true; - case 'CLOSE': - return $rights->hasRight('e') || true; - case 'FETCH': - return $rights->hasRight('s') || true; - case 'STORE': - return $rights->hasRight('s') || $rights->hasRight('w') || $rights->hasRight('t'); + case 'SUBSCRIBE': + return $rights->hasRight('l') || true; + case 'UNSUBSCRIBE': + return true; + case 'CLOSE': + return $rights->hasRight('e') || true; + case 'FETCH': + return $rights->hasRight('s') || true; + case 'STORE': + return $rights->hasRight('s') || $rights->hasRight('w') || $rights->hasRight('t'); */ - case 'GETACL': - case 'SETACL': - case 'LISTRIGHTS': - case 'DELETEACL': - return $rights->hasRight('a'); - } + case 'GETACL': + case 'SETACL': + case 'LISTRIGHTS': + case 'DELETEACL': + return $rights->hasRight('a'); } } - return !\in_array($command, ['GETACL','SETACL','LISTRIGHTS','DELETEACL','MYRIGHTS']); + return true; + } + + private function FolderACLRequest(string $sFolderName, string $sCommand, array $aParams) : \MailSo\Imap\ResponseCollection + { + if ($this->ACLAllow($sFolderName, $sCommand)) try { + return $this->SendRequestGetResponse($sCommand, $aParams); + } catch (\Throwable $oException) { + // Error in IMAP command $sCommand: ACLs disabled + $this->ACLDisabled = true; + throw $oException; + } } public function FolderSetACL(string $sFolderName, string $sIdentifier, string $sAccessRights) : void { -// if ($this->ACLAllow($sFolderName, 'SETACL')) { - $this->SendRequestGetResponse('SETACL', array( + $this->FolderACLRequest($sFolderName, 'SETACL', array( $this->EscapeFolderName($sFolderName), $this->EscapeString($sIdentifier), $this->EscapeString($sAccessRights) @@ -96,8 +110,7 @@ trait ACL public function FolderDeleteACL(string $sFolderName, string $sIdentifier) : void { -// if ($this->ACLAllow($sFolderName, 'DELETEACL')) { - $this->SendRequestGetResponse('DELETEACL', array( + $this->FolderACLRequest($sFolderName, 'DELETEACL', array( $this->EscapeFolderName($sFolderName), $this->EscapeString($sIdentifier) )); @@ -105,9 +118,8 @@ trait ACL public function FolderGetACL(string $sFolderName) : array { -// if ($this->ACLAllow($sFolderName, 'GETACL')) { - $oResponses = $this->SendRequestGetResponse('GETACL', array($this->EscapeFolderName($sFolderName))); $aResult = array(); + $oResponses = $this->FolderACLRequest($sFolderName, 'GETACL', array($this->EscapeFolderName($sFolderName))); foreach ($oResponses as $oResponse) { // * ACL INBOX.shared demo@snappymail.eu akxeilprwtscd foobar@snappymail.eu akxeilprwtscd demo2@snappymail.eu lrwstipekxacd if (\MailSo\Imap\Enumerations\ResponseType::UNTAGGED === $oResponse->ResponseType @@ -118,7 +130,10 @@ trait ACL { $c = \count($oResponse->ResponseList); for ($i = 3; $i < $c; $i += 2) { - $aResult[$oResponse->ResponseList[$i]] = new ACLResponse(\str_split($oResponse->ResponseList[$i+1])); + $aResult[] = new ACLResponse( + $oResponse->ResponseList[$i], + $oResponse->ResponseList[$i+1] + ); } } } @@ -127,8 +142,7 @@ trait ACL public function FolderListRights(string $sFolderName, string $sIdentifier) : ?ACLResponse { -// if ($this->ACLAllow($sFolderName, 'LISTRIGHTS')) { - $oResponses = $this->SendRequestGetResponse('LISTRIGHTS', array( + $oResponses = $this->FolderACLRequest($sFolderName, 'LISTRIGHTS', array( $this->EscapeFolderName($sFolderName), $this->EscapeString($sIdentifier) )); @@ -140,7 +154,10 @@ trait ACL && $sIdentifier === $oResponse->ResponseList[3] ) { - return static::aclRightsToClass(\array_slice($oResponse->ResponseList, 4)); + foreach (\array_slice($oResponse->ResponseList, 4) as $rule) { + $result = \array_merge($result, \str_split($rule)); + } + return new ACLResponse($sIdentifier, \implode('', \array_unique($result))); } } return null; @@ -148,35 +165,17 @@ trait ACL public function FolderMyRights(string $sFolderName) : ?ACLResponse { -// if ($this->ACLAllow($sFolderName, 'MYRIGHTS')) { -// if ($this->hasCapability('ACL')) { - try { - $oResponses = $this->SendRequestGetResponse('MYRIGHTS', array($this->EscapeFolderName($sFolderName))); - foreach ($oResponses as $oResponse) { - if (\MailSo\Imap\Enumerations\ResponseType::UNTAGGED === $oResponse->ResponseType - && isset($oResponse->ResponseList[3]) - && 'MYRIGHTS' === $oResponse->ResponseList[1] - && $sFolderName === $oResponse->ResponseList[2] - ) - { - return static::aclRightsToClass(\array_slice($oResponse->ResponseList, 3)); - } + $oResponses = $this->FolderACLRequest($sFolderName, 'MYRIGHTS', array($this->EscapeFolderName($sFolderName))); + foreach ($oResponses as $oResponse) { + if (\MailSo\Imap\Enumerations\ResponseType::UNTAGGED === $oResponse->ResponseType + && isset($oResponse->ResponseList[3]) + && 'MYRIGHTS' === $oResponse->ResponseList[1] + && $sFolderName === $oResponse->ResponseList[2] + ) + { + return new ACLResponse('', $oResponse->ResponseList[3]); } - } catch (\Throwable $oException) { - // \MailSo\Imap\Exceptions\NegativeResponseException: Error in IMAP command MYRIGHTS: ACLs disabled - $this->ACLDisabled = true; - throw $oException; } return null; } - - private static function aclRightsToClass(array $rules) : ACLResponse - { - $result = array(); - foreach ($rules as $rule) { - $result = \array_merge($result, \str_split($rule)); - } - return new ACLResponse(\array_unique($result)); - } - } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Commands/Folders.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Commands/Folders.php index 311e7358d..5ea93bcd8 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Commands/Folders.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Commands/Folders.php @@ -576,7 +576,7 @@ trait Folders } /* // RFC 4314 - if ($this->hasCapability('ACL') || $this->CapabilityValue('RIGHTS')) { + if ($this->hasCapability('ACL')) { foreach ($oFolderCollection as $oFolder) { if ($oFolder->Selectable()) try { $oFolder->myRights = $this->FolderMyRights($oFolder->FullName); diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Responses/ACL.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Responses/ACL.php index e0a0cbbbd..7f5ef5d6f 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Responses/ACL.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Responses/ACL.php @@ -17,18 +17,26 @@ namespace MailSo\Imap\Responses; */ class ACL implements \JsonSerializable { - private array $rights; + private string + $identifier, + $rights; - function __construct(array $rights) + function __construct(string $identifier, string $rights) { + $this->identifier = $identifier; $this->rights = $rights; } + public function identifier() : string + { + return $this->identifier; + } + /** PHP 8.1 public function hasRight(string|\MailSo\Imap\Enumerations\FolderACL $right) { if ($right instanceof \BackedEnum) { - return \in_array($right->value, $this->rights); + return \str_contains($this->rights, $right->value); } */ public function hasRight(string $right) : bool @@ -37,13 +45,15 @@ class ACL implements \JsonSerializable if (\defined($const)) { $right = \constant($const); } - return \in_array($right, $this->rights); + return \str_contains($this->rights, $right); } #[\ReturnTypeWillChange] public function jsonSerialize() { return [ + 'identifier' => $this->identifier, + 'rights' => $this->rights, 'mayReadItems' => ($this->hasRight('l') && $this->hasRight('r')), 'mayAddItems' => $this->hasRight('i'), 'mayRemoveItems' => ($this->hasRight('t') && $this->hasRight('e')), @@ -52,8 +62,7 @@ class ACL implements \JsonSerializable 'mayCreateChild' => $this->hasRight('k'), 'mayRename' => $this->hasRight('x'), 'mayDelete' => $this->hasRight('x'), - 'maySubmit' => $this->hasRight('p'), - 'raw' => \implode('', $this->rights) + 'maySubmit' => $this->hasRight('p') ]; } 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 42c279045..eca283096 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 @@ -4,7 +4,7 @@