Improved FolderCollection handling

This commit is contained in:
the-djmaze 2023-03-03 15:50:27 +01:00
parent a61ef0fa1c
commit 33c10365f0
10 changed files with 79 additions and 97 deletions

View file

@ -227,8 +227,6 @@ export class FolderCollectionModel extends AbstractCollectionModel
name: name, name: name,
fullName: parentName, fullName: parentName,
delimiter: delimiter, delimiter: delimiter,
exists: false,
isSubscribed: false,
flags: ['\\nonexistent'] flags: ['\\nonexistent']
}); });
setFolder(pfolder); setFolder(pfolder);
@ -380,6 +378,8 @@ export class FolderModel extends AbstractModel {
isFlagged: () => FolderUserStore.currentFolder() === this isFlagged: () => FolderUserStore.currentFolder() === this
&& MessagelistUserStore.listSearch().includes('flagged'), && MessagelistUserStore.listSearch().includes('flagged'),
// isSubscribed: () => this.flags().includes('\\subscribed'),
hasVisibleSubfolders: () => !!this.subFolders().find(folder => folder.visible()), hasVisibleSubfolders: () => !!this.subFolders().find(folder => folder.visible()),
hasSubscriptions: () => this.isSubscribed() | !!this.subFolders().find( hasSubscriptions: () => this.isSubscribed() | !!this.subFolders().find(
@ -535,6 +535,10 @@ export class FolderModel extends AbstractModel {
path.pop(); path.pop();
folder.parentName = path.join(folder.delimiter); folder.parentName = path.join(folder.delimiter);
folder.isSubscribed(folder.flags.includes('\\subscribed'));
folder.exists = !folder.flags.includes('\\nonexistent');
folder.selectable(folder.exists && !folder.flags.includes('\\noselect'));
type && 'mail' != type && folder.kolabType(type); type && 'mail' != type && folder.kolabType(type);
} }
return folder; return folder;

View file

@ -476,6 +476,9 @@ html.sm-msgView-bottom {
} }
} }
/*
@media all and (display-mode: fullscreen) {
*/
html.rl-fullscreen { html.rl-fullscreen {
#rl-left, #rl-left,

View file

@ -39,6 +39,11 @@ abstract class Collection extends \ArrayObject implements \JsonSerializable
} }
} }
public function keys() : array
{
return \array_keys($this->getArrayCopy());
}
public function Add($mItem, bool $bToTop = false) : self public function Add($mItem, bool $bToTop = false) : self
{ {
$this->append($mItem, $bToTop); $this->append($mItem, $bToTop);

View file

@ -13,6 +13,7 @@
namespace MailSo\Imap\Commands; namespace MailSo\Imap\Commands;
use MailSo\Imap\Folder; use MailSo\Imap\Folder;
use MailSo\Imap\FolderCollection;
use MailSo\Imap\FolderInformation; use MailSo\Imap\FolderInformation;
use MailSo\Imap\SequenceSet; use MailSo\Imap\SequenceSet;
use MailSo\Imap\Enumerations\FolderStatus; use MailSo\Imap\Enumerations\FolderStatus;
@ -171,7 +172,7 @@ trait Folders
$oFolderInfo = $this->oCurrentFolderInfo; $oFolderInfo = $this->oCurrentFolderInfo;
$bReselect = false; $bReselect = false;
$bWritable = false; $bWritable = false;
if ($oFolderInfo && $sFolderName === $oFolderInfo->FolderName) { if ($oFolderInfo && $sFolderName === $oFolderInfo->FullName) {
if ($oFolderInfo->hasStatus) { if ($oFolderInfo->hasStatus) {
return $oFolderInfo; return $oFolderInfo;
} }
@ -351,7 +352,7 @@ trait Folders
{ {
if (!$bForceReselect if (!$bForceReselect
&& $this->oCurrentFolderInfo && $this->oCurrentFolderInfo
&& $sFolderName === $this->oCurrentFolderInfo->FolderName && $sFolderName === $this->oCurrentFolderInfo->FullName
&& ($bIsWritable === $this->oCurrentFolderInfo->IsWritable || $this->oCurrentFolderInfo->IsWritable) && ($bIsWritable === $this->oCurrentFolderInfo->IsWritable || $this->oCurrentFolderInfo->IsWritable)
) { ) {
return $this->oCurrentFolderInfo; return $this->oCurrentFolderInfo;
@ -444,7 +445,7 @@ trait Folders
* @throws \MailSo\Net\Exceptions\* * @throws \MailSo\Net\Exceptions\*
* @throws \MailSo\Imap\Exceptions\* * @throws \MailSo\Imap\Exceptions\*
*/ */
public function FolderList(string $sParentFolderName, string $sListPattern, bool $bIsSubscribeList = false, bool $bUseListStatus = false) : array public function FolderList(string $sParentFolderName, string $sListPattern, bool $bIsSubscribeList = false, bool $bUseListStatus = false) : FolderCollection
{ {
$sCmd = 'LIST'; $sCmd = 'LIST';
@ -504,16 +505,17 @@ trait Folders
$aMetadata = $bMetadata ? $this->getAllMetadata() : null; $aMetadata = $bMetadata ? $this->getAllMetadata() : null;
$this->SendRequest($sCmd, $aParameters); $this->SendRequest($sCmd, $aParameters);
$aReturn = array(); $aFolders = array();
$oFolderCollection = new FolderCollection;
$sDelimiter = ''; $sDelimiter = '';
$bInbox = false; $bInbox = false;
foreach ($this->yieldUntaggedResponses() as $oResponse) { foreach ($this->yieldUntaggedResponses() as $oResponse) {
if ('STATUS' === $oResponse->StatusOrIndex && isset($oResponse->ResponseList[2])) { if ('STATUS' === $oResponse->StatusOrIndex && isset($oResponse->ResponseList[2])) {
$sFullName = $this->toUTF8($oResponse->ResponseList[2]); $sFullName = $this->toUTF8($oResponse->ResponseList[2]);
if (!isset($aReturn[$sFullName])) { if (!isset($oFolderCollection[$sFullName])) {
$aReturn[$sFullName] = new Folder($sFullName); $oFolderCollection[$sFullName] = new Folder($sFullName);
} }
$aReturn[$sFullName]->setStatusFromResponse($oResponse); $oFolderCollection[$sFullName]->setStatusFromResponse($oResponse);
} }
else if ($sCmd === $oResponse->StatusOrIndex && 5 === \count($oResponse->ResponseList)) { else if ($sCmd === $oResponse->StatusOrIndex && 5 === \count($oResponse->ResponseList)) {
try try
@ -527,18 +529,16 @@ trait Folders
* $oResponse->ResponseList[3] = Delimiter * $oResponse->ResponseList[3] = Delimiter
* $oResponse->ResponseList[4] = FullName * $oResponse->ResponseList[4] = FullName
*/ */
if (isset($aReturn[$sFullName])) { if (isset($oFolderCollection[$sFullName])) {
$oFolder = $aReturn[$sFullName]; $oFolder = $oFolderCollection[$sFullName];
$oFolder->setDelimiter($oResponse->ResponseList[3]); $oFolder->setDelimiter($oResponse->ResponseList[3]);
$oFolder->setFlags($oResponse->ResponseList[2]); $oFolder->setFlags($oResponse->ResponseList[2]);
} else { } else {
$oFolder = new Folder($sFullName, $oResponse->ResponseList[3], $oResponse->ResponseList[2]); $oFolder = new Folder($sFullName, $oResponse->ResponseList[3], $oResponse->ResponseList[2]);
$aReturn[$sFullName] = $oFolder; $oFolderCollection[$sFullName] = $oFolder;
} }
if ($oFolder->IsInbox()) { $bInbox = $bInbox || $oFolder->IsInbox();
$bInbox = true;
}
if (!$sDelimiter) { if (!$sDelimiter) {
$sDelimiter = $oFolder->Delimiter(); $sDelimiter = $oFolder->Delimiter();
@ -547,8 +547,6 @@ trait Folders
if (isset($aMetadata[$oResponse->ResponseList[4]])) { if (isset($aMetadata[$oResponse->ResponseList[4]])) {
$oFolder->SetAllMetadata($aMetadata[$oResponse->ResponseList[4]]); $oFolder->SetAllMetadata($aMetadata[$oResponse->ResponseList[4]]);
} }
$aReturn[$sFullName] = $oFolder;
} }
catch (\Throwable $oException) catch (\Throwable $oException)
{ {
@ -557,13 +555,16 @@ trait Folders
} }
} }
// $iOptimizationLimit = $this->Settings->folder_list_limit;
// $oFolderCollection->Optimized = 10 < $iOptimizationLimit && $oFolderCollection->count() > $iOptimizationLimit;
// RFC 5464 // RFC 5464
if ($bMetadata && !$aMetadata /*&& 50 < \count($aReturn)*/) { if ($bMetadata && !$aMetadata /*&& 50 < $oFolderCollection->count()*/) {
foreach ($aReturn as $oFolder) { foreach ($oFolderCollection as $oFolder) {
// if (2 > \substr_count($oFolder->FullName(), $oFolder->Delimiter())) // if (2 > \substr_count($oFolder->FullName, $oFolder->Delimiter()))
try { try {
$oFolder->SetAllMetadata( $oFolder->SetAllMetadata(
$this->getMetadata($oFolder->FullName(), ['/shared', '/private'], ['DEPTH'=>'infinity']) $this->getMetadata($oFolder->FullName, ['/shared', '/private'], ['DEPTH'=>'infinity'])
); );
} catch (\Throwable $oException) { } catch (\Throwable $oException) {
// Ignore error // Ignore error
@ -571,11 +572,11 @@ trait Folders
} }
} }
if (!$bInbox && !$sParentFolderName && !isset($aReturn['INBOX'])) { if (!$bInbox && !$sParentFolderName && !isset($oFolderCollection['INBOX'])) {
$aReturn['INBOX'] = new Folder('INBOX', $sDelimiter); $oFolderCollection['INBOX'] = new Folder('INBOX', $sDelimiter);
} }
return $aReturn; return $oFolderCollection;
} }
/** /**
@ -583,7 +584,7 @@ trait Folders
* @throws \MailSo\Net\Exceptions\* * @throws \MailSo\Net\Exceptions\*
* @throws \MailSo\Imap\Exceptions\* * @throws \MailSo\Imap\Exceptions\*
*/ */
public function FolderSubscribeList(string $sParentFolderName, string $sListPattern) : array public function FolderSubscribeList(string $sParentFolderName, string $sListPattern) : FolderCollection
{ {
return $this->FolderList($sParentFolderName, $sListPattern, true); return $this->FolderList($sParentFolderName, $sListPattern, true);
} }
@ -593,7 +594,7 @@ trait Folders
* @throws \MailSo\Net\Exceptions\* * @throws \MailSo\Net\Exceptions\*
* @throws \MailSo\Imap\Exceptions\* * @throws \MailSo\Imap\Exceptions\*
*/ */
public function FolderStatusList(string $sParentFolderName, string $sListPattern) : array public function FolderStatusList(string $sParentFolderName, string $sListPattern) : FolderCollection
{ {
return $this->FolderList($sParentFolderName, $sListPattern, false, true); return $this->FolderList($sParentFolderName, $sListPattern, false, true);
} }

View file

@ -39,7 +39,7 @@ class Folder implements \JsonSerializable
if (!\strlen($sFullName)) { if (!\strlen($sFullName)) {
throw new \InvalidArgumentException; throw new \InvalidArgumentException;
} }
$this->FolderName = $sFullName; $this->FullName = $sFullName;
$this->setDelimiter($sDelimiter); $this->setDelimiter($sDelimiter);
$this->setFlags($aFlags); $this->setFlags($aFlags);
/* /*
@ -68,7 +68,7 @@ class Folder implements \JsonSerializable
public function Name() : string public function Name() : string
{ {
$sNameRaw = $this->FolderName; $sNameRaw = $this->FullName;
if ($this->sDelimiter) { if ($this->sDelimiter) {
$aNames = \explode($this->sDelimiter, $sNameRaw); $aNames = \explode($this->sDelimiter, $sNameRaw);
return \end($aNames); return \end($aNames);
@ -76,11 +76,6 @@ class Folder implements \JsonSerializable
return $sNameRaw; return $sNameRaw;
} }
public function FullName() : string
{
return $this->FolderName;
}
public function Delimiter() : ?string public function Delimiter() : ?string
{ {
return $this->sDelimiter; return $this->sDelimiter;
@ -91,14 +86,10 @@ class Folder implements \JsonSerializable
return $this->aFlagsLowerCase; return $this->aFlagsLowerCase;
} }
public function Exists() : bool
{
return !\in_array('\\nonexistent', $this->aFlagsLowerCase);
}
public function Selectable() : bool public function Selectable() : bool
{ {
return !\in_array('\\noselect', $this->aFlagsLowerCase) && $this->Exists(); return !\in_array('\\noselect', $this->aFlagsLowerCase)
&& !\in_array('\\nonexistent', $this->aFlagsLowerCase);
} }
public function IsSubscribed() : bool public function IsSubscribed() : bool
@ -108,7 +99,7 @@ class Folder implements \JsonSerializable
public function IsInbox() : bool public function IsInbox() : bool
{ {
return 'INBOX' === \strtoupper($this->FolderName) || \in_array('\\inbox', $this->aFlagsLowerCase); return 'INBOX' === \strtoupper($this->FullName) || \in_array('\\inbox', $this->aFlagsLowerCase);
} }
public function SetMetadata(string $sName, string $sData) : void public function SetMetadata(string $sName, string $sData) : void
@ -150,7 +141,7 @@ class Folder implements \JsonSerializable
if ($match) { if ($match) {
$role = \array_shift($match); $role = \array_shift($match);
} }
if (!$role && 'INBOX' === \strtoupper($this->FolderName)) { if (!$role && 'INBOX' === \strtoupper($this->FullName)) {
return 'inbox'; return 'inbox';
} }
} }
@ -179,17 +170,14 @@ class Folder implements \JsonSerializable
/* /*
if ($this->ImapClient->hasCapability('ACL') || $this->ImapClient->CapabilityValue('RIGHTS')) { if ($this->ImapClient->hasCapability('ACL') || $this->ImapClient->CapabilityValue('RIGHTS')) {
// MailSo\Imap\Responses\ACL // MailSo\Imap\Responses\ACL
$rights = $this->ImapClient->FolderMyRights($this->FolderName); $rights = $this->ImapClient->FolderMyRights($this->FullName);
} }
*/ */
return array( return array(
'@Object' => 'Object/Folder', '@Object' => 'Object/Folder',
'name' => $this->Name(), 'name' => $this->Name(),
'fullName' => $this->FolderName, 'fullName' => $this->FullName,
'delimiter' => (string) $this->sDelimiter, 'delimiter' => (string) $this->sDelimiter,
'isSubscribed' => $this->IsSubscribed(),
'exists' => $this->Exists(),
'selectable' => $this->Selectable(),
'flags' => $this->aFlagsLowerCase, 'flags' => $this->aFlagsLowerCase,
// 'extended' => $aExtended, // 'extended' => $aExtended,
// 'permanentFlags' => $this->PermanentFlags, // 'permanentFlags' => $this->PermanentFlags,

View file

@ -9,7 +9,7 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
namespace MailSo\Mail; namespace MailSo\Imap;
/** /**
* @category MailSo * @category MailSo
@ -21,22 +21,16 @@ class FolderCollection extends \MailSo\Base\Collection
public function append($oFolder, bool $bToTop = false) : void public function append($oFolder, bool $bToTop = false) : void
{ {
assert($oFolder instanceof \MailSo\Imap\Folder); assert($oFolder instanceof Folder);
parent::append($oFolder, $bToTop); parent::append($oFolder, $bToTop);
} }
public function FindDelimiter() : string
{
$oFolder = $this['INBOX'] ?? $this[0] ?? null;
return $oFolder ? $oFolder->Delimiter() : '/';
}
#[\ReturnTypeWillChange] #[\ReturnTypeWillChange]
public function jsonSerialize() public function jsonSerialize()
{ {
return array( return array(
'@Object' => 'Collection/FolderCollection', '@Object' => 'Collection/FolderCollection',
'@Collection' => $this->getArrayCopy(), '@Collection' => \array_values($this->getArrayCopy()),
// 'optimized' => $this->Optimized // 'optimized' => $this->Optimized
); );
} }

View file

@ -33,9 +33,9 @@ class FolderInformation implements \JsonSerializable
*/ */
public array $PermanentFlags = array(); public array $PermanentFlags = array();
function __construct(string $sFolderName, bool $bIsWritable) function __construct(string $sFullName, bool $bIsWritable)
{ {
$this->FolderName = $sFolderName; $this->FullName = $sFullName;
$this->IsWritable = $bIsWritable; $this->IsWritable = $bIsWritable;
} }
@ -56,7 +56,7 @@ class FolderInformation implements \JsonSerializable
{ {
$result = array( $result = array(
'id' => $this->MAILBOXID, 'id' => $this->MAILBOXID,
'name' => $this->FolderName, 'name' => $this->FullName,
'flags' => $this->Flags, 'flags' => $this->Flags,
'permanentFlags' => $this->PermanentFlags, 'permanentFlags' => $this->PermanentFlags,
'uidNext' => $this->UIDNEXT, 'uidNext' => $this->UIDNEXT,

View file

@ -22,7 +22,7 @@ namespace MailSo\Imap\Traits;
*/ */
trait Status trait Status
{ {
public string $FolderName; public string $FullName;
public bool $hasStatus = false; public bool $hasStatus = false;
@ -102,7 +102,7 @@ trait Status
return null; return null;
} }
return \md5('FolderHash/'. \implode('-', [ return \md5('FolderHash/'. \implode('-', [
$this->FolderName, $this->FullName,
$this->MESSAGES, $this->MESSAGES,
$this->UIDNEXT, $this->UIDNEXT,
$this->UIDVALIDITY, $this->UIDVALIDITY,

View file

@ -11,6 +11,7 @@
namespace MailSo\Mail; namespace MailSo\Mail;
use MailSo\Imap\FolderCollection;
use MailSo\Imap\FolderInformation; use MailSo\Imap\FolderInformation;
use MailSo\Imap\Enumerations\FetchType; use MailSo\Imap\Enumerations\FetchType;
use MailSo\Imap\Enumerations\MessageFlag; use MailSo\Imap\Enumerations\MessageFlag;
@ -794,45 +795,32 @@ class MailClient
public function Folders(string $sParent, string $sListPattern, bool $bUseListSubscribeStatus) : ?FolderCollection public function Folders(string $sParent, string $sListPattern, bool $bUseListSubscribeStatus) : ?FolderCollection
{ {
$aImapSubscribedFoldersHelper = null; // $this->oImapClient->Settings->disable_list_status
if ($this->oImapClient->hasCapability('LIST-EXTENDED')) { $oFolderCollection = $this->oImapClient->FolderStatusList($sParent, $sListPattern);
$bUseListSubscribeStatus = false; if (!$oFolderCollection->count()) {
} else if ($bUseListSubscribeStatus) { return null;
}
if ($bUseListSubscribeStatus && !$this->oImapClient->hasCapability('LIST-EXTENDED')) {
// $this->oLogger && $this->oLogger->Write('RFC5258 not supported, using LSUB'); // $this->oLogger && $this->oLogger->Write('RFC5258 not supported, using LSUB');
// \SnappyMail\Log::warning('IMAP', 'RFC5258 not supported, using LSUB'); // \SnappyMail\Log::warning('IMAP', 'RFC5258 not supported, using LSUB');
try try
{ {
$aSubscribedFolders = $this->oImapClient->FolderSubscribeList($sParent, $sListPattern); $oSubscribedFolders = $this->oImapClient->FolderSubscribeList($sParent, $sListPattern);
$aImapSubscribedFoldersHelper = array(); foreach ($oSubscribedFolders as /* @var $oImapFolder \MailSo\Imap\Folder */ $oImapFolder) {
foreach ($aSubscribedFolders as /* @var $oImapFolder \MailSo\Imap\Folder */ $oImapFolder) { isset($oFolderCollection[$oImapFolder->FullName])
$aImapSubscribedFoldersHelper[] = $oImapFolder->FullName(); && $oFolderCollection[$oImapFolder->FullName]->setSubscribed();
} }
} }
catch (\Throwable $oException) catch (\Throwable $oException)
{ {
\SnappyMail\Log::error('IMAP', 'FolderSubscribeList: ' . $oException->getMessage()); \SnappyMail\Log::error('IMAP', 'FolderSubscribeList: ' . $oException->getMessage());
} foreach ($oFolderCollection as /* @var $oImapFolder \MailSo\Imap\Folder */ $oImapFolder) {
}
// $this->oImapClient->Settings->disable_list_status
$aFolders = $this->oImapClient->FolderStatusList($sParent, $sListPattern);
if (!$aFolders) {
return null;
}
if ($bUseListSubscribeStatus) {
foreach ($aFolders as $sFullName => /* @var $oImapFolder \MailSo\Imap\Folder */ $oImapFolder) {
if (null === $aImapSubscribedFoldersHelper || \in_array($sFullName, $aImapSubscribedFoldersHelper)) {
$oImapFolder->setSubscribed(); $oImapFolder->setSubscribed();
} }
} }
} }
$oFolderCollection = new FolderCollection;
// $iOptimizationLimit = $this->oImapClient->Settings->folder_list_limit;
// $oFolderCollection->Optimized = 10 < $iOptimizationLimit && \count($aFolders) > $iOptimizationLimit;
$oFolderCollection->exchangeArray(\array_values($aFolders));
return $oFolderCollection; return $oFolderCollection;
} }
@ -930,23 +918,22 @@ class MailClient
throw new \InvalidArgumentException; throw new \InvalidArgumentException;
} }
$aSubscribeFolders = array(); $oSubscribedFolders = array();
if ($bSubscribe) { if ($bSubscribe) {
$aSubscribeFolders = $this->oImapClient->FolderSubscribeList($sPrevFolderFullName, '*'); $oSubscribedFolders = $this->oImapClient->FolderSubscribeList($sPrevFolderFullName, '*');
foreach ($aSubscribeFolders as /* @var $oFolder \MailSo\Imap\Folder */ $oFolder) { foreach ($oSubscribedFolders as /* @var $oFolder \MailSo\Imap\Folder */ $oFolder) {
$this->oImapClient->FolderUnsubscribe($oFolder->FullName()); $this->oImapClient->FolderUnsubscribe($oFolder->FullName);
} }
} }
$this->oImapClient->FolderRename($sPrevFolderFullName, $sNewFolderFullName); $this->oImapClient->FolderRename($sPrevFolderFullName, $sNewFolderFullName);
foreach ($aSubscribeFolders as /* @var $oFolder \MailSo\Imap\Folder */ $oFolder) { foreach ($oSubscribedFolders as /* @var $oFolder \MailSo\Imap\Folder */ $oFolder) {
$sFolderFullNameForResubscribe = $oFolder->FullName(); $sFolderFullNameForResubscribe = $oFolder->FullName;
if (\str_starts_with($sFolderFullNameForResubscribe, $sPrevFolderFullName)) { if (\str_starts_with($sFolderFullNameForResubscribe, $sPrevFolderFullName)) {
$sNewFolderFullNameForResubscribe = $sNewFolderFullName. $this->oImapClient->FolderSubscribe(
\substr($sFolderFullNameForResubscribe, \strlen($sPrevFolderFullName)); $sNewFolderFullName . \substr($sFolderFullNameForResubscribe, \strlen($sPrevFolderFullName))
);
$this->oImapClient->FolderSubscribe($sNewFolderFullNameForResubscribe);
} }
} }

View file

@ -204,7 +204,7 @@ trait Response
); );
$this->aCheckableFolder = \is_array($aCheckable) ? $aCheckable : array(); $this->aCheckableFolder = \is_array($aCheckable) ? $aCheckable : array();
} }
$aResult['checkable'] = \in_array($mResponse->FullName(), $this->aCheckableFolder); $aResult['checkable'] = \in_array($mResponse->FullName, $this->aCheckableFolder);
return $aResult; return $aResult;
} }
@ -216,7 +216,7 @@ trait Response
return \array_slice($mResult['@Collection'], 0, 100); return \array_slice($mResult['@Collection'], 0, 100);
} }
if ($mResponse instanceof \MailSo\Mail\AttachmentCollection if ($mResponse instanceof \MailSo\Mail\AttachmentCollection
|| $mResponse instanceof \MailSo\Mail\FolderCollection || $mResponse instanceof \MailSo\Imap\FolderCollection
|| $mResponse instanceof \MailSo\Mail\MessageCollection || $mResponse instanceof \MailSo\Mail\MessageCollection
) { ) {
return $mResult; return $mResult;