-
-
Notifications
You must be signed in to change notification settings - Fork 252
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Suggestion: tree structure of mailboxes #261
Comments
Hi, can you elaborate your idea with deeper details? |
IMAP servers are too heterogeneous to have a unique way for handling folders, and a lot of them support features that others don't. For this very reason the imap extension returns some flags to help identify them, and this library directly reports them without prior elaboration: without an accepted and used standard we can't do anything but return raw attributes. If you are able to code your idea and to test it a least against Dovecot and GMail, feel free to open a PR 👍 |
I totaly understand - I'll try to make my own class. If it's good I'll post it here |
I'm closing the issue for now; if you have any updates in the future, feel free to reopen in. |
I've created two classes. class MailboxesParser
{
/** @var MailboxInterface[] */
protected $mailboxes;
protected $folders;
protected $treeStructure;
const DRAFT = 'drafts';
const INBOX = 'inbox';
const SENT = 'sent';
const SPAM = 'spam';
const TRASH = 'trash';
const TEMPLATES = 'templates';
const ARCHIVES = 'archives';
protected $specialFoldersIds = [
self::INBOX => ['inbox',],
self::SENT => ['sent', 'sent messages', 'INBOX.Sent', 'INBOX.Wysłane', '[Gmail]/Sent Mail', 'elementy wysłane', 'wysłane'],
self::DRAFT => ['drafts', 'INBOX.Drafts', '[Gmail]/Drafts', 'szkice',],
self::SPAM => ['spam', 'INBOX.spam', '[Gmail]/spam'],
self::TRASH => ['trash', 'bin', 'INBOX.trash', '[Gmail]/trash', 'kosz'],
self::TEMPLATES => ['templates', 'szablony'],
self::ARCHIVES => ['archives', 'archiwum'],
];
protected $specialFoldersNames = [
self::DRAFT => 'Szkice',
self::INBOX => 'Odebrane',
self::SENT => 'Wysłane',
self::SPAM => 'Spam',
self::TRASH => 'Kosz',
self::TEMPLATES => 'Szablony',
self::ARCHIVES => 'Archiwum',
];
protected $specialFoldersOrder = [
self::INBOX => 1,
self::SENT => 2,
self::DRAFT => 3,
self::SPAM => 5000,
self::TRASH => 6000,
self::TEMPLATES => 4,
self::ARCHIVES => 4000,
];
/**
* MailboxesTree constructor.
*
* @param MailboxInterface[] $mailboxes
*/
public function __construct($mailboxes)
{
$this->mailboxes = $mailboxes;
}
protected function parse()
{
$this->folders = [];
foreach ($this->mailboxes AS $k => $mailbox) {
$mailboxName = $mailbox->getName();
$this->folders[$mailboxName] = [
'special' => $this->getSpecialFolder($mailboxName),
];
$this->folders[$mailboxName]['mailboxName'] = $mailboxName;
$this->folders[$mailboxName]['name'] = $this->getName($mailboxName, $mailbox->getDelimiter());
$this->folders[$mailboxName]['order'] = $this->getOrder($mailboxName, $mailbox->getDelimiter());
$this->folders[$mailboxName]['mailbox'] = $mailbox;
}
uasort($this->folders, [$this, "sortByOrder"]);
return $this->folders;
}
/**
* @return mixed
*/
public function getFolders()
{
if (!$this->folders) {
$this->parse();
}
return $this->folders;
}
/**
* @return mixed
*/
public function getTreeStructure()
{
if (!$this->treeStructure) {
$treeParser = new MailboxesTreeParser();
$this->treeStructure = $treeParser->parse($this->getFolders());
}
return $this->treeStructure;
}
protected function sortByOrder($a, $b)
{
return ($a['order'] < $b['order']) ? -1 : 1;
}
protected function getSpecialFolder($mailboxName)
{
foreach ($this->specialFoldersIds AS $specialFolderKind => $names) {
$lower = mb_strtolower($mailboxName);
foreach ($names AS $name) {
if ($lower === mb_strtolower($name)) {
return $specialFolderKind;
}
}
}
return null;
}
private function getName($mailboxName, $delimiter = '.')
{
if ($this->folders[$mailboxName]['special']) {
return $this->specialFoldersNames[$this->folders[$mailboxName]['special']];
} else {
$e = explode($delimiter, $mailboxName);
return $e[count($e) - 1];
}
}
private function getOrder($mailboxName, $delimiter)
{
if ($this->folders[$mailboxName]['special']) {
return $this->specialFoldersOrder[$this->folders[$mailboxName]['special']];
} else {
$e = explode($delimiter, $mailboxName);
return 100 * count($e);
}
}
} class MailboxesTreeParser
{
public function parse($input, $delimiter = '.')
{
$newKeys = array_map(function ($key) use ($delimiter) {
$k = explode($delimiter, $key);
$newkey = [];
foreach ($k as $segment) {
$newkey[] = $segment;
$newkey[] = "subfolders";
}
return implode(".", $newkey);
}, array_keys($input));
$arrayToParse = [];
foreach ($newKeys AS $key) {
$k = explode(".", $key);
$keyWithoutLast = implode('.', array_splice($k, 0, -1));
$arrayToParse[$key] = [];
$inputKey = str_replace('.', $delimiter, str_replace('.subfolders', '', $key));
if ($input[$inputKey]) {
$arrayToParse[$keyWithoutLast . '.mailboxName'] = $inputKey;
foreach ($input[$inputKey] AS $k => $w) {
$arrayToParse[$keyWithoutLast . '.' . $k] = $w;
}
}
}
$res = [];
array_walk($arrayToParse, function ($value, $key) use (&$res) {
$this->set($res, $key, $value);
});
return $res;
}
private function set(&$array, $key, $value)
{
if (is_null($key)) {
return $array = $value;
}
$keys = explode('.', $key);
while (count($keys) > 1) {
$key = array_shift($keys);
// If the key doesn't exist at this depth, we will just create an empty array
// to hold the next value, allowing us to create the arrays to hold final
// values at the correct depth. Then we'll keep digging into the array.
if (!isset($array[$key]) || !is_array($array[$key])) {
$array[$key] = [];
}
$array = &$array[$key];
}
$array[array_shift($keys)] = $value;
return $array;
}
} usage: $parser = new MailboxesParser($mailboxes);
$orderedFlatFolders = $parser->getFolders();
$treeStructure = $parser->getTreeStructure(); If You find those useful feel free to use them. |
And simple tests: class MailboxesParserTest extends TestCase
{
/** @var MailboxInterface[] */
protected $mailboxes;
protected function setUp()
{
parent::setUp();
$this->mailboxes = [
$this->createMailboxMock('INBOX.Drafts'),
$this->createMailboxMock('INBOX.normal'),
$this->createMailboxMock('INBOX'),
$this->createMailboxMock('INBOX.normal.sub'),
];
}
public function testParsera()
{
$parser = new MailboxesParser($this->mailboxes);
$foldery = $parser->getFolders();
$spodziewane = [
'INBOX' => [
'special' => 'inbox',
'name' => 'Odebrane',
],
];
$this->assertCount(4, $foldery);
$this->assertArrayHasKey('INBOX', $foldery);
$this->assertEquals('Odebrane', $foldery['INBOX']['name']);
$this->assertArrayHasKey('INBOX.Drafts', $foldery);
}
private function createMailboxMock($mailboxName)
{
$mailbox = $this->createMock(MailboxInterface::class);
$mailbox->method('getName')
->willReturn($mailboxName);
$mailbox->method('getDelimiter')
->willReturn('.');
return $mailbox;
}
} class MailboxesTreeParserTest extends TestCase
{
public function testParsowania()
{
$dane = [
'inbox' => ['name' => 'Inbox'],
'inbox.first' => ['name' => 'First'],
'inbox.second' => ['name' => 'Second'],
'inbox.second.other' => ['name' => 'Second Other'],
'inbox.third.another' => ['name' => 'Third Another'],
];
$parser = new MailboxesTreeParser();
$zwroc = $parser->parse($dane);
$spodziewane = [
'inbox' => [
'name' => 'Inbox',
'mailboxName' => 'inbox',
'subfolders' => [
'first' => [
'name' => 'First',
'mailboxName' => 'inbox.first',
'subfolders' => [],
],
'second' => [
'name' => 'Second',
'mailboxName' => 'inbox.second',
'subfolders' => [
'other' => [
'name' => 'Second Other',
'subfolders' => [],
'mailboxName' => 'inbox.second.other',
],
],
],
'third' => [
'subfolders' => [
'another' => [
'name' => 'Third Another',
'subfolders' => [],
'mailboxName' => 'inbox.third.another',
],
],
],
],
],
];
$this->assertEquals($spodziewane, $zwroc);
}
public function testParsowaniaKreska()
{
$dane = [
'inbox' => ['name' => 'Inbox'],
'inbox|first' => ['name' => 'First'],
'inbox|second' => ['name' => 'Second'],
];
$parser = new MailboxesTreeParser();
$zwroc = $parser->parse($dane, '|');
$spodziewane = [
'inbox' => [
'name' => 'Inbox',
'mailboxName' => 'inbox',
'subfolders' => [
'first' => [
'name' => 'First',
'mailboxName' => 'inbox|first',
'subfolders' => [],
],
'second' => [
'name' => 'Second',
'mailboxName' => 'inbox|second',
'subfolders' => [],
]
],
],
];
$this->assertEquals($spodziewane, $zwroc);
}
} Sorry for polish ;) |
@piernik could you create a PR? Your parser looks good. |
I don't know how - I'm not github fluent. |
@piernik Github is trivial, the requirement is knowing Git. If you know Git, then you can follow the Github helps, that sums up in:
😉 |
I've uploaded parser: #268 |
Closed in favour of #268 |
It's a suggestion. Creating tree structure of mailboxes.
In addition You could mark special folders ex. INBOX as inbox, INBOX.sent as sent and so on.
The text was updated successfully, but these errors were encountered: