Remove most DoFolders loops as they are not used.

And move system folder detection/autocreate to MailboxDetectPlugin because it is hardly used.
And by using mailbox.role the default system folder detection is only using (IMAP and JMAP) RFC standards.
This commit is contained in:
the-djmaze 2022-12-15 13:49:39 +01:00
parent 8799cd3e8d
commit 54896bf044
18 changed files with 419 additions and 468 deletions

View file

@ -6,7 +6,6 @@ import { mailToHelper, setLayoutResizer, dropdownsDetectVisibility } from 'Commo
import { import {
FolderType, FolderType,
SetSystemFoldersNotification,
ClientSideKeyNameFolderListSize ClientSideKeyNameFolderListSize
} from 'Common/EnumsUser'; } from 'Common/EnumsUser';
@ -101,27 +100,27 @@ export class AppUser extends AbstractApp {
*/ */
moveMessagesToFolderType(iFolderType, sFromFolderFullName, oUids, bDelete) { moveMessagesToFolderType(iFolderType, sFromFolderFullName, oUids, bDelete) {
let oMoveFolder = null, let oMoveFolder = null,
nSetSystemFoldersNotification = null; nSetSystemFoldersNotification = 0;
switch (iFolderType) { switch (iFolderType) {
case FolderType.Spam: case FolderType.Junk:
oMoveFolder = getFolderFromCacheList(FolderUserStore.spamFolder()); oMoveFolder = getFolderFromCacheList(FolderUserStore.spamFolder());
nSetSystemFoldersNotification = SetSystemFoldersNotification.Spam; nSetSystemFoldersNotification = iFolderType;
bDelete = bDelete || UNUSED_OPTION_VALUE === FolderUserStore.spamFolder(); bDelete = bDelete || UNUSED_OPTION_VALUE === FolderUserStore.spamFolder();
break; break;
case FolderType.NotSpam: case FolderType.Inbox:
oMoveFolder = getFolderFromCacheList(getFolderInboxName()); oMoveFolder = getFolderFromCacheList(getFolderInboxName());
break; break;
case FolderType.Trash: case FolderType.Trash:
oMoveFolder = getFolderFromCacheList(FolderUserStore.trashFolder()); oMoveFolder = getFolderFromCacheList(FolderUserStore.trashFolder());
nSetSystemFoldersNotification = SetSystemFoldersNotification.Trash; nSetSystemFoldersNotification = iFolderType;
bDelete = bDelete || UNUSED_OPTION_VALUE === FolderUserStore.trashFolder() bDelete = bDelete || UNUSED_OPTION_VALUE === FolderUserStore.trashFolder()
|| sFromFolderFullName === FolderUserStore.spamFolder() || sFromFolderFullName === FolderUserStore.spamFolder()
|| sFromFolderFullName === FolderUserStore.trashFolder(); || sFromFolderFullName === FolderUserStore.trashFolder();
break; break;
case FolderType.Archive: case FolderType.Archive:
oMoveFolder = getFolderFromCacheList(FolderUserStore.archiveFolder()); oMoveFolder = getFolderFromCacheList(FolderUserStore.archiveFolder());
nSetSystemFoldersNotification = SetSystemFoldersNotification.Archive; nSetSystemFoldersNotification = iFolderType;
bDelete = bDelete || UNUSED_OPTION_VALUE === FolderUserStore.archiveFolder(); bDelete = bDelete || UNUSED_OPTION_VALUE === FolderUserStore.archiveFolder();
break; break;
// no default // no default

View file

@ -4,14 +4,27 @@
* @enum {number} * @enum {number}
*/ */
export const FolderType = { export const FolderType = {
User: 0,
Inbox: 1, Inbox: 1,
Sent: 2, Sent: 2,
Drafts: 3, Drafts: 3,
Spam: 4, // JUNK Junk: 4, // Spam
Trash: 5, Trash: 5,
Archive: 6, Archive: 6
NotSpam: 80 /*
IMPORTANT : 10;
FLAGGED : 11;
ALL : 13;
// TODO: SnappyMail
TEMPLATES : 19;
// Kolab
CONFIGURATION : 20;
CALENDAR : 21;
CONTACTS : 22;
TASKS : 23;
NOTES : 24;
FILES : 25;
JOURNAL : 26;
*/
}; };
/** /**
@ -41,18 +54,6 @@ export const ComposeType = {
EditAsNew: 6 EditAsNew: 6
}; };
/**
* @enum {number}
*/
export const SetSystemFoldersNotification = {
None: 0,
Sent: 1,
Draft: 2,
Spam: 3,
Trash: 4,
Archive: 5
};
/** /**
* @enum {number} * @enum {number}
*/ */

View file

@ -37,7 +37,7 @@ const
Inbox: 0, Inbox: 0,
Sent: 0, Sent: 0,
Drafts: 0, Drafts: 0,
Spam: 0, Junk: 0, // Spam
Trash: 0, Trash: 0,
Archive: 0 Archive: 0
}, },
@ -62,7 +62,7 @@ const
case FolderType.Trash: case FolderType.Trash:
case FolderType.Archive: case FolderType.Archive:
return i18n('FOLDER_LIST/' + getKeyByValue(FolderType, type).toUpperCase() + '_NAME'); return i18n('FOLDER_LIST/' + getKeyByValue(FolderType, type).toUpperCase() + '_NAME');
case FolderType.Spam: case FolderType.Junk:
return i18n('GLOBAL/SPAM'); return i18n('GLOBAL/SPAM');
// no default // no default
} }
@ -104,11 +104,11 @@ export class FolderCollectionModel extends AbstractCollectionModel
/* /*
constructor() { constructor() {
super(); super();
this.CountRec this.quotaUsage;
this.Namespace; this.quotaLimit;
this.Optimized this.namespace;
this.SystemFolders this.optimized
this.Capabilities this.capabilities
} }
*/ */
@ -118,16 +118,13 @@ export class FolderCollectionModel extends AbstractCollectionModel
*/ */
static reviveFromJson(object) { static reviveFromJson(object) {
const expandedFolders = Local.get(ClientSideKeyNameExpandedFolders); const expandedFolders = Local.get(ClientSideKeyNameExpandedFolders);
if (object?.SystemFolders) {
forEachObjectEntry(SystemFolders, key => forEachObjectEntry(SystemFolders, (key, value) =>
SystemFolders[key] = SettingsGet(key+'Folder') || object.SystemFolders[FolderType[key]] value || (SystemFolders[key] = SettingsGet(key+'Folder'))
); );
}
const result = super.reviveFromJson(object, oFolder => { const result = super.reviveFromJson(object, oFolder => {
let oCacheFolder = getFolderFromCacheList(oFolder.FullName), let oCacheFolder = getFolderFromCacheList(oFolder.FullName);
type = FolderType[getKeyByValue(SystemFolders, oFolder.FullName)];
if (oCacheFolder) { if (oCacheFolder) {
// oCacheFolder.revivePropertiesFromJson(oFolder); // oCacheFolder.revivePropertiesFromJson(oFolder);
if (oFolder.Hash) { if (oFolder.Hash) {
@ -143,17 +140,59 @@ export class FolderCollectionModel extends AbstractCollectionModel
oCacheFolder = FolderModel.reviveFromJson(oFolder); oCacheFolder = FolderModel.reviveFromJson(oFolder);
if (!oCacheFolder) if (!oCacheFolder)
return null; return null;
if (1 == type) {
oCacheFolder.type(type);
setFolderInboxName(oFolder.FullName);
}
setFolder(oCacheFolder); setFolder(oCacheFolder);
} }
if (1 < type) { // JMAP RFC 8621
oCacheFolder.type(type); let role = oFolder.role;
/*
if (!role) {
// Kolab
let type = oFolder.metadata[FolderMetadataKeys.KolabFolderType]
|| oFolder.metadata[FolderMetadataKeys.KolabFolderTypeShared];
switch (type) {
case 'mail.inbox':
case 'mail.drafts':
role = type.replace('mail.', '');
break;
// case 'mail.outbox':
case 'mail.sentitems':
role = 'sent';
break;
case 'mail.junkemail':
role = 'spam';
break;
case 'mail.wastebasket':
role = 'trash';
break;
}
// Flags
if (oFolder.Flags.includes('\\sentmail')) {
role = 'sent';
}
if (oFolder.Flags.includes('\\spam')) {
role = 'junk';
}
if (oFolder.Flags.includes('\\bin')) {
role = 'trash';
}
if (oFolder.Flags.includes('\\important')) {
role = 'important';
}
if (oFolder.Flags.includes('\\starred')) {
role = 'flagged';
}
if (oFolder.Flags.includes('\\all') || oFolder.Flags.includes('\\allmail')) {
role = 'all';
}
} }
*/
if (role) {
role = role[0].toUpperCase() + role.slice(1);
SystemFolders[role] || (SystemFolders[role] = oFolder.FullName);
}
oCacheFolder.type(FolderType[getKeyByValue(SystemFolders, oFolder.FullName)] || 0);
oCacheFolder.collapsed(!expandedFolders oCacheFolder.collapsed(!expandedFolders
|| !isArray(expandedFolders) || !isArray(expandedFolders)
@ -162,12 +201,43 @@ export class FolderCollectionModel extends AbstractCollectionModel
return oCacheFolder; return oCacheFolder;
}); });
result.CountRec = result.length;
setFolderInboxName(SystemFolders.Inbox);
let i = result.length; let i = result.length;
if (i) { if (i) {
sortFolders(result); sortFolders(result);
try { try {
while (i--) { while (i--) {
let folder = result[i], parent = getFolderFromCacheList(folder.parentName); let folder = result[i], parent = getFolderFromCacheList(folder.parentName);
if (!parent) {
// Create NonExistent parent folders
let delimiter = folder.delimiter;
if (delimiter) {
let parents = folder.fullName.split(delimiter);
parents.pop();
while (parents.length) {
let parentName = parents.join(delimiter),
name = parents.pop(),
pfolder = getFolderFromCacheList(parentName);
if (!pfolder) {
pfolder = FolderModel.reviveFromJson({
'@Object': 'Object/Folder',
Name: name,
FullName: parentName,
Delimiter: delimiter,
Exists: false,
isSubscribed: false,
Flags: ['\\nonexistent']
});
setFolder(pfolder);
result.splice(i, 0, pfolder);
++i;
}
}
parent = getFolderFromCacheList(folder.parentName);
}
}
if (parent) { if (parent) {
parent.subFolders.unshift(folder); parent.subFolders.unshift(folder);
result.splice(i,1); result.splice(i,1);
@ -187,7 +257,7 @@ export class FolderCollectionModel extends AbstractCollectionModel
if (!( if (!(
SettingsGet('SentFolder') + SettingsGet('SentFolder') +
SettingsGet('DraftsFolder') + SettingsGet('DraftsFolder') +
SettingsGet('SpamFolder') + SettingsGet('JunkFolder') +
SettingsGet('TrashFolder') + SettingsGet('TrashFolder') +
SettingsGet('ArchiveFolder') SettingsGet('ArchiveFolder')
) )
@ -197,21 +267,21 @@ export class FolderCollectionModel extends AbstractCollectionModel
FolderUserStore.folderList(this); FolderUserStore.folderList(this);
FolderUserStore.namespace = this.Namespace; FolderUserStore.namespace = this.namespace;
// 'THREAD=REFS', 'THREAD=REFERENCES', 'THREAD=ORDEREDSUBJECT' // 'THREAD=REFS', 'THREAD=REFERENCES', 'THREAD=ORDEREDSUBJECT'
AppUserStore.threadsAllowed(!!( AppUserStore.threadsAllowed(!!(
Settings.app('useImapThread') && this.Capabilities.some(capa => capa.startsWith('THREAD=')) Settings.app('useImapThread') && this.capabilities.some(capa => capa.startsWith('THREAD='))
)); ));
FolderUserStore.folderListOptimized(!!this.Optimized); // FolderUserStore.folderListOptimized(!!this.optimized);
FolderUserStore.quotaUsage(this.quotaUsage); FolderUserStore.quotaUsage(this.quotaUsage);
FolderUserStore.quotaLimit(this.quotaLimit); FolderUserStore.quotaLimit(this.quotaLimit);
FolderUserStore.capabilities(this.Capabilities); FolderUserStore.capabilities(this.capabilities);
FolderUserStore.sentFolder(normalizeFolder(SystemFolders.Sent)); FolderUserStore.sentFolder(normalizeFolder(SystemFolders.Sent));
FolderUserStore.draftsFolder(normalizeFolder(SystemFolders.Drafts)); FolderUserStore.draftsFolder(normalizeFolder(SystemFolders.Drafts));
FolderUserStore.spamFolder(normalizeFolder(SystemFolders.Spam)); FolderUserStore.spamFolder(normalizeFolder(SystemFolders.Junk));
FolderUserStore.trashFolder(normalizeFolder(SystemFolders.Trash)); FolderUserStore.trashFolder(normalizeFolder(SystemFolders.Trash));
FolderUserStore.archiveFolder(normalizeFolder(SystemFolders.Archive)); FolderUserStore.archiveFolder(normalizeFolder(SystemFolders.Archive));
@ -238,7 +308,8 @@ export class FolderModel extends AbstractModel {
addObservablesTo(this, { addObservablesTo(this, {
name: '', name: '',
type: FolderType.User, type: 0,
role: null,
selectable: false, selectable: false,
focused: false, focused: false,
@ -345,9 +416,9 @@ export class FolderModel extends AbstractModel {
} }
), ),
canBeEdited: () => FolderType.User === folder.type() && folder.exists/* && folder.selectable()*/, canBeEdited: () => !folder.type() && folder.exists/* && folder.selectable()*/,
isSystemFolder: () => FolderType.User !== folder.type() isSystemFolder: () => folder.type()
| (FolderUserStore.allowKolab() && !!folder.kolabType() & !SettingsUserStore.unhideKolabFolders()), | (FolderUserStore.allowKolab() && !!folder.kolabType() & !SettingsUserStore.unhideKolabFolders()),
canBeSelected: () => folder.selectable() && !folder.isSystemFolder(), canBeSelected: () => folder.selectable() && !folder.isSystemFolder(),
@ -363,7 +434,7 @@ export class FolderModel extends AbstractModel {
* Or when all below conditions are true: * Or when all below conditions are true:
* - selectable() * - selectable()
* - isSubscribed() OR hideUnsubscribed = false * - isSubscribed() OR hideUnsubscribed = false
* - FolderType.User * - 0 == type()
* - not kolabType() * - not kolabType()
*/ */
visible: () => { visible: () => {

View file

@ -117,7 +117,7 @@ FolderUserStore = new class {
const const
subscribeRemoveSystemFolder = observable => { subscribeRemoveSystemFolder = observable => {
observable.subscribe(() => getFolderFromCacheList(observable())?.type(FolderType.User), self, 'beforeChange'); observable.subscribe(() => getFolderFromCacheList(observable())?.type(0), self, 'beforeChange');
}, },
fSetSystemFolderType = type => value => getFolderFromCacheList(value)?.type(type); fSetSystemFolderType = type => value => getFolderFromCacheList(value)?.type(type);
@ -130,7 +130,7 @@ FolderUserStore = new class {
addSubscribablesTo(self, { addSubscribablesTo(self, {
sentFolder: fSetSystemFolderType(FolderType.Sent), sentFolder: fSetSystemFolderType(FolderType.Sent),
draftsFolder: fSetSystemFolderType(FolderType.Drafts), draftsFolder: fSetSystemFolderType(FolderType.Drafts),
spamFolder: fSetSystemFolderType(FolderType.Spam), spamFolder: fSetSystemFolderType(FolderType.Junk),
trashFolder: fSetSystemFolderType(FolderType.Trash), trashFolder: fSetSystemFolderType(FolderType.Trash),
archiveFolder: fSetSystemFolderType(FolderType.Archive) archiveFolder: fSetSystemFolderType(FolderType.Archive)
}); });

View file

@ -8,7 +8,7 @@ import {
import { import {
ComposeType, ComposeType,
EditorDefaultType, EditorDefaultType,
SetSystemFoldersNotification FolderType
} from 'Common/EnumsUser'; } from 'Common/EnumsUser';
import { pInt, isArray, arrayLength } from 'Common/Utils'; import { pInt, isArray, arrayLength } from 'Common/Utils';
@ -444,7 +444,7 @@ export class ComposePopupView extends AbstractViewPopup {
} }
if (!sSentFolder) { if (!sSentFolder) {
showScreenPopup(FolderSystemPopupView, [SetSystemFoldersNotification.Sent]); showScreenPopup(FolderSystemPopupView, [FolderType.Sent]);
} else try { } else try {
this.sendError(false); this.sendError(false);
this.sending(true); this.sending(true);
@ -501,7 +501,7 @@ export class ComposePopupView extends AbstractViewPopup {
saveCommand() { saveCommand() {
if (FolderUserStore.draftsFolderNotEnabled()) { if (FolderUserStore.draftsFolderNotEnabled()) {
showScreenPopup(FolderSystemPopupView, [SetSystemFoldersNotification.Draft]); showScreenPopup(FolderSystemPopupView, [FolderType.Drafts]);
} else { } else {
this.savedError(false); this.savedError(false);
this.saving(true); this.saving(true);

View file

@ -1,7 +1,7 @@
import ko from 'ko'; import ko from 'ko';
import { koComputable, addSubscribablesTo } from 'External/ko'; import { koComputable, addSubscribablesTo } from 'External/ko';
import { SetSystemFoldersNotification } from 'Common/EnumsUser'; import { FolderType } from 'Common/EnumsUser';
import { UNUSED_OPTION_VALUE } from 'Common/Consts'; import { UNUSED_OPTION_VALUE } from 'Common/Consts';
import { defaultOptionsAfterRender } from 'Common/Utils'; import { defaultOptionsAfterRender } from 'Common/Utils';
import { folderListOptionsBuilder } from 'Common/Folders'; import { folderListOptionsBuilder } from 'Common/Folders';
@ -47,24 +47,24 @@ export class FolderSystemPopupView extends AbstractViewPopup {
} }
/** /**
* @param {number=} notificationType = SetSystemFoldersNotification.None * @param {number=} notificationType = 0
*/ */
onShow(notificationType = SetSystemFoldersNotification.None) { onShow(notificationType = 0) {
let notification = '', prefix = 'POPUPS_SYSTEM_FOLDERS/NOTIFICATION_'; let notification = '', prefix = 'POPUPS_SYSTEM_FOLDERS/NOTIFICATION_';
switch (notificationType) { switch (notificationType) {
case SetSystemFoldersNotification.Sent: case FolderType.Sent:
notification = i18n(prefix + 'SENT'); notification = i18n(prefix + 'SENT');
break; break;
case SetSystemFoldersNotification.Draft: case FolderType.Drafts:
notification = i18n(prefix + 'DRAFTS'); notification = i18n(prefix + 'DRAFTS');
break; break;
case SetSystemFoldersNotification.Spam: case FolderType.Junk:
notification = i18n(prefix + 'SPAM'); notification = i18n(prefix + 'SPAM');
break; break;
case SetSystemFoldersNotification.Trash: case FolderType.Trash:
notification = i18n(prefix + 'TRASH'); notification = i18n(prefix + 'TRASH');
break; break;
case SetSystemFoldersNotification.Archive: case FolderType.Archive:
notification = i18n(prefix + 'ARCHIVE'); notification = i18n(prefix + 'ARCHIVE');
break; break;
// no default // no default

View file

@ -364,11 +364,11 @@ export class MailMessageList extends AbstractViewRight {
} }
spamCommand() { spamCommand() {
moveMessagesToFolderType(FolderType.Spam); moveMessagesToFolderType(FolderType.Junk);
} }
notSpamCommand() { notSpamCommand() {
moveMessagesToFolderType(FolderType.NotSpam); moveMessagesToFolderType(FolderType.Inbox);
} }
moveCommand(vm, event) { moveCommand(vm, event) {

View file

@ -220,8 +220,8 @@ export class MailMessageView extends AbstractViewRight {
this.deleteCommand = createCommandActionHelper(FolderType.Trash); this.deleteCommand = createCommandActionHelper(FolderType.Trash);
this.deleteWithoutMoveCommand = createCommandActionHelper(FolderType.Trash, true); this.deleteWithoutMoveCommand = createCommandActionHelper(FolderType.Trash, true);
this.archiveCommand = createCommandActionHelper(FolderType.Archive); this.archiveCommand = createCommandActionHelper(FolderType.Archive);
this.spamCommand = createCommandActionHelper(FolderType.Spam); this.spamCommand = createCommandActionHelper(FolderType.Junk);
this.notSpamCommand = createCommandActionHelper(FolderType.NotSpam); this.notSpamCommand = createCommandActionHelper(FolderType.Inbox);
decorateKoCommands(this, { decorateKoCommands(this, {
editCommand: self => self.messageVisibility(), editCommand: self => self.messageVisibility(),

View file

@ -99,7 +99,7 @@ class Provider implements IProvider
} }
$search = \rawurlencode($oParams->sSearch); $search = \rawurlencode($oParams->sSearch);
// $MessageCollection->MessageResultCount; // $MessageCollection->totalEmails;
foreach ($MessageCollection as $Message) { foreach ($MessageCollection as $Message) {
// $Message instanceof \MailSo\Mail\Message // $Message instanceof \MailSo\Mail\Message
$result[] = new SearchResultEntry( $result[] = new SearchResultEntry(

View file

@ -218,28 +218,6 @@ $Plugin->addHook('hook.name', 'functionName');
bool $bSuccess bool $bSuccess
\MailSo\Smtp\Settings $oSettings \MailSo\Smtp\Settings $oSettings
## Folders
### filter.folders-post
params:
\RainLoop\Model\Account $oAccount
\MailSo\Mail\FolderCollection $oFolderCollection
### filter.folders-complete
params:
\RainLoop\Model\Account $oAccount
\MailSo\Mail\FolderCollection $oFolderCollection
### filter.folders-system-types
params:
\RainLoop\Model\Account $oAccount
array &$aList
### filter.system-folders-names
params:
\RainLoop\Model\Account $oAccount
array &$aCache
## Json service actions ## Json service actions
Called by RainLoop\ServiceActions::ServiceJson() Called by RainLoop\ServiceActions::ServiceJson()
{actionname} is one of the RainLoop\Actions::Do{ActionName}(), {actionname} is one of the RainLoop\Actions::Do{ActionName}(),

View file

@ -0,0 +1,219 @@
<?php
use MailSo\Imap\Enumerations\FolderType;
use MailSo\Imap\Enumerations\MetadataKeys;
class MailboxDetectPlugin extends \RainLoop\Plugins\AbstractPlugin
{
const
NAME = 'MailboxDetect',
AUTHOR = 'SnappyMail',
URL = 'https://snappymail.eu/',
VERSION = '2.1',
RELEASE = '2022-12-15',
REQUIRED = '2.23.1',
CATEGORY = 'General',
LICENSE = 'MIT',
DESCRIPTION = 'Autodetect system folders and/or create them when needed';
public function Init() : void
{
$this->addHook('json.after-folders', 'AfterFolders');
}
protected function configMapping() : array
{
return array(
\RainLoop\Plugins\Property::NewInstance('autocreate_system_folders')->SetLabel('Autocreate system folders')
->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL)
->SetDefaultValue(false),
);
}
public function AfterFolders(array &$aResponse)
{
if (!empty($aResponse['Result']['@Collection'])) {
$oActions = \RainLoop\Api::Actions();
$oAccount = $oActions->getAccountFromToken();
if (!$oAccount) {
\error_log('No Account');
return;
}
$oSettingsLocal = $oActions->SettingsProvider(true)->Load($oAccount);
$roles = [
'inbox' => false,
'sent' => !!$oSettingsLocal->GetConf('SentFolder', ''),
'drafts' => !!$oSettingsLocal->GetConf('DraftFolder', ''),
'junk' => !!$oSettingsLocal->GetConf('SpamFolder', ''),
'trash' => !!$oSettingsLocal->GetConf('TrashFolder', ''),
'archive' => !!$oSettingsLocal->GetConf('ArchiveFolder', '')
];
$types = [
FolderType::SENT => 'sent',
FolderType::DRAFTS => 'drafts',
FolderType::JUNK => 'junk',
FolderType::TRASH => 'trash',
FolderType::ARCHIVE => 'archive'
];
$found = [
'inbox' => [],
'sent' => [],
'drafts' => [],
'junk' => [],
'trash' => [],
'archive' => []
];
$aMap = $this->systemFoldersNames($oAccount);
$sDelimiter = '';
foreach ($aResponse['Result']['@Collection'] as $i => $folder) {
$sDelimiter || ($sDelimiter = $folder['Delimiter']);
if ($folder['role']) {
$roles[$folder['role']] = true;
} else if (\in_array('\\sentmail', $folder['Flags'])) {
$found['sent'][] = $i;
} else if (\in_array('\\spam', $folder['Flags'])) {
$found['junk'][] = $i;
} else if (\in_array('\\bin', $folder['Flags'])) {
$found['trash'][] = $i;
} else if (\in_array('\\starred', $folder['Flags'])) {
$found['flagged'][] = $i;
} else {
// Kolab
$kolab = $folder['Metadata'][MetadataKeys::KOLAB_CTYPE]
?? $folder['Metadata'][MetadataKeys::KOLAB_CTYPE_SHARED]
?? '';
if ('mail.inbox' === $kolab) {
$found['inbox'][] = $i;
} else if ('mail.sentitems' === $kolab /*|| 'mail.outbox' === $kolab*/) {
$found['sent'][] = $i;
} else if ('mail.drafts' === $kolab) {
$found['drafts'][] = $i;
} else if ('mail.junkemail' === $kolab) {
$found['junk'][] = $i;
} else if ('mail.wastebasket' === $kolab) {
$found['trash'][] = $i;
} else {
$iFolderType = 0;
if (isset($aMap[$folder['FullName']])) {
$iFolderType = $aMap[$folder['FullName']];
} else if (isset($aMap[$folder['name']]) || isset($aMap["INBOX{$folder['Delimiter']}{$folder['name']}"])) {
$iFolderType = $aMap[$folder['name']];
}
if ($iFolderType && isset($types[$iFolderType])) {
$found[$types[$iFolderType]][] = $i;
}
}
}
}
foreach ($roles as $role => $hasRole) {
if ($hasRole) {
unset($found[$role]);
}
}
if ($found) {
foreach ($found as $role => $folders) {
if (isset($folders[0])) {
// Set the first as default
// \error_log("Set role {$role}");
$aResponse['Result']['@Collection'][$folders[0]]['role'] = $role;
} else if ($this->Config()->Get('plugin', 'autocreate_system_folders', false)) {
try
{
$sParent = \substr($aResponse['Result']['namespace'], 0, -1);
$sFolderNameToCreate = \ucfirst($role);
/*
$this->Manager()->RunHook('filter.folders-system-types', array($oAccount, &$aList));
$iPos = \strrpos($sFolderNameToCreate, $sDelimiter);
if (false !== $iPos) {
$mNewParent = \substr($sFolderNameToCreate, 0, $iPos);
$mNewFolderNameToCreate = \substr($sFolderNameToCreate, $iPos + 1);
if (\strlen($mNewFolderNameToCreate)) {
$sFolderNameToCreate = $mNewFolderNameToCreate;
}
if (\strlen($mNewParent)) {
$sParent = \strlen($sParent) ? $sParent.$sDelimiter.$mNewParent : $mNewParent;
}
}
*/
// \error_log("Create mailbox {$sFolderNameToCreate}");
$oFolder = $oActions->MailClient()->FolderCreate(
$sFolderNameToCreate,
$sParent,
true,
$sDelimiter
);
$aResponse['Result']['@Collection'][] = \json_encode($oFolder);
}
catch (\Throwable $oException)
{
$this->Logger()->WriteException($oException);
}
}
}
}
}
}
/**
* @staticvar array $aCache
*/
private function systemFoldersNames(\RainLoop\Model\Account $oAccount) : array
{
static $aCache = null;
if (null === $aCache) {
$aCache = array(
'Sent' => FolderType::SENT,
'Send' => FolderType::SENT,
'Outbox' => FolderType::SENT,
'Out box' => FolderType::SENT,
'Sent Item' => FolderType::SENT,
'Sent Items' => FolderType::SENT,
'Send Item' => FolderType::SENT,
'Send Items' => FolderType::SENT,
'Sent Mail' => FolderType::SENT,
'Sent Mails' => FolderType::SENT,
'Send Mail' => FolderType::SENT,
'Send Mails' => FolderType::SENT,
'Drafts' => FolderType::DRAFTS,
'Draft' => FolderType::DRAFTS,
'Draft Mail' => FolderType::DRAFTS,
'Draft Mails' => FolderType::DRAFTS,
'Drafts Mail' => FolderType::DRAFTS,
'Drafts Mails' => FolderType::DRAFTS,
'Junk' => FolderType::JUNK,
'Junk E-mail' => FolderType::JUNK,
'Spam' => FolderType::JUNK,
'Spams' => FolderType::JUNK,
'Bulk Mail' => FolderType::JUNK,
'Bulk Mails' => FolderType::JUNK,
'Trash' => FolderType::TRASH,
'Deleted' => FolderType::TRASH,
'Deleted Items' => FolderType::TRASH,
'Bin' => FolderType::TRASH,
'Archive' => FolderType::ARCHIVE,
'Archives' => FolderType::ARCHIVE,
'All' => FolderType::ALL,
'All Mail' => FolderType::ALL,
'All Mails' => FolderType::ALL,
);
$aNewCache = array();
foreach ($aCache as $sKey => $iType) {
$aNewCache[$sKey] = $iType;
$aNewCache[\str_replace(' ', '', $sKey)] = $iType;
}
$aCache = $aNewCache;
$this->Manager()->RunHook('filter.system-folders-names', array($oAccount, &$aCache));
}
return $aCache;
}
}

View file

@ -11,7 +11,6 @@
namespace MailSo\Imap; namespace MailSo\Imap;
use MailSo\Imap\Enumerations\FolderType;
use MailSo\Imap\Enumerations\MetadataKeys; use MailSo\Imap\Enumerations\MetadataKeys;
/** /**
@ -135,45 +134,27 @@ class Folder implements \JsonSerializable
// JMAP RFC 8621 // JMAP RFC 8621
public function Role() : ?string public function Role() : ?string
{ {
$aFlags = $this->aFlagsLowerCase; $role = \strtolower($this->GetMetadata(MetadataKeys::SPECIALUSE) ?: '');
$aFlags[] = \strtolower($this->GetMetadata(MetadataKeys::SPECIALUSE)); if (!$role) {
$match = \array_intersect([
$match = \array_intersect([ '\\inbox',
'\\inbox', '\\all', // '\\allmail'
'\\all', // '\\allmail' '\\archive',
'\\archive', '\\drafts',
'\\drafts', '\\flagged', // '\\starred'
'\\flagged', // '\\starred' '\\important',
'\\important', '\\junk', // '\\spam'
'\\junk', // '\\spam' '\\sent', // '\\sentmail'
'\\sent', // '\\sentmail' '\\trash', // '\\bin'
'\\trash', // '\\bin' ], $this->aFlagsLowerCase);
], $aFlags); if ($match) {
if ($match) { $role = \array_shift($match);
return \substr(\array_shift($match), 1); }
} if (!$role && 'INBOX' === \strtoupper($this->FolderName)) {
if ('INBOX' === \strtoupper($this->FolderName)) {
return 'inbox';
}
/*
// Kolab
$type = $this->GetMetadata(MetadataKeys::KOLAB_CTYPE) ?: $this->GetMetadata(MetadataKeys::KOLAB_CTYPE_SHARED);
switch ($type) {
case 'mail.inbox':
return 'inbox'; return 'inbox';
// case 'mail.outbox': }
case 'mail.sentitems':
return 'sent';
case 'mail.drafts':
return 'drafts';
case 'mail.junkemail':
return 'junk';
case 'mail.wastebasket':
return 'trash';
} }
*/ return $role ? \ltrim($role, '\\') : null;
return null;
} }
public function Hash(string $sClientHash) : ?string public function Hash(string $sClientHash) : ?string
@ -181,94 +162,6 @@ class Folder implements \JsonSerializable
return $this->getHash($sClientHash); return $this->getHash($sClientHash);
} }
public function GetType() : int
{
$aFlags = $this->aFlagsLowerCase;
// RFC 6154
// $aFlags[] = \strtolower($this->GetMetadata(MetadataKeys::SPECIALUSE));
switch (true)
{
case $this->IsInbox():
return FolderType::INBOX;
case \in_array('\\sent', $this->aFlagsLowerCase):
case \in_array('\\sentmail', $this->aFlagsLowerCase):
return FolderType::SENT;
case \in_array('\\drafts', $this->aFlagsLowerCase):
return FolderType::DRAFTS;
case \in_array('\\junk', $this->aFlagsLowerCase):
case \in_array('\\spam', $this->aFlagsLowerCase):
return FolderType::JUNK;
case \in_array('\\trash', $this->aFlagsLowerCase):
case \in_array('\\bin', $this->aFlagsLowerCase):
return FolderType::TRASH;
case \in_array('\\important', $this->aFlagsLowerCase):
return FolderType::IMPORTANT;
case \in_array('\\flagged', $this->aFlagsLowerCase):
case \in_array('\\starred', $this->aFlagsLowerCase):
return FolderType::FLAGGED;
case \in_array('\\archive', $this->aFlagsLowerCase):
return FolderType::ARCHIVE;
case \in_array('\\all', $this->aFlagsLowerCase):
case \in_array('\\allmail', $this->aFlagsLowerCase):
return FolderType::ALL;
// TODO
// case 'Templates' === $this->FullName():
// return FolderType::TEMPLATES;
}
// Kolab
$type = $this->GetMetadata(MetadataKeys::KOLAB_CTYPE) ?: $this->GetMetadata(MetadataKeys::KOLAB_CTYPE_SHARED);
switch ($type)
{
/*
// TODO: Kolab
case 'event':
case 'event.default':
return FolderType::CALENDAR;
case 'contact':
case 'contact.default':
return FolderType::CONTACTS;
case 'task':
case 'task.default':
return FolderType::TASKS;
case 'note':
case 'note.default':
return FolderType::NOTES;
case 'file':
case 'file.default':
return FolderType::FILES;
case 'configuration':
return FolderType::CONFIGURATION;
case 'journal':
case 'journal.default':
return FolderType::JOURNAL;
*/
case 'mail.inbox':
return FolderType::INBOX;
// case 'mail.outbox':
case 'mail.sentitems':
return FolderType::SENT;
case 'mail.drafts':
return FolderType::DRAFTS;
case 'mail.junkemail':
return FolderType::JUNK;
case 'mail.wastebasket':
return FolderType::TRASH;
}
return FolderType::USER;
}
#[\ReturnTypeWillChange] #[\ReturnTypeWillChange]
public function jsonSerialize() public function jsonSerialize()
{ {

View file

@ -17,7 +17,7 @@ namespace MailSo\Mail;
*/ */
class FolderCollection extends \MailSo\Base\Collection class FolderCollection extends \MailSo\Base\Collection
{ {
public bool $Optimized = false; // public bool $Optimized = false;
public function append($oFolder, bool $bToTop = false) : void public function append($oFolder, bool $bToTop = false) : void
{ {
@ -30,4 +30,14 @@ class FolderCollection extends \MailSo\Base\Collection
$oFolder = $this['INBOX'] ?? $this[0] ?? null; $oFolder = $this['INBOX'] ?? $this[0] ?? null;
return $oFolder ? $oFolder->Delimiter() : '/'; return $oFolder ? $oFolder->Delimiter() : '/';
} }
#[\ReturnTypeWillChange]
public function jsonSerialize()
{
return array(
'@Object' => 'Collection/FolderCollection',
'@Collection' => $this->getArrayCopy(),
// 'optimized' => $this->Optimized
);
}
} }

View file

@ -933,29 +933,11 @@ class MailClient
return null; return null;
} }
foreach ($aFolders as $sFullName => /* @var $oImapFolder \MailSo\Imap\Folder */ $oImapFolder) { if ($bUseListSubscribeStatus) {
if (($bUseListSubscribeStatus && (null === $aImapSubscribedFoldersHelper || \in_array($sFullName, $aImapSubscribedFoldersHelper))) || $oImapFolder->IsInbox()) { foreach ($aFolders as $sFullName => /* @var $oImapFolder \MailSo\Imap\Folder */ $oImapFolder) {
$oImapFolder->setSubscribed(); if (null === $aImapSubscribedFoldersHelper || \in_array($sFullName, $aImapSubscribedFoldersHelper)) {
} $oImapFolder->setSubscribed();
// Add NonExistent folders
$sDelimiter = $oImapFolder->Delimiter();
$aFolderExplode = \explode($sDelimiter, $sFullName);
\array_pop($aFolderExplode);
while ($aFolderExplode) {
$sFullName = \implode($sDelimiter, $aFolderExplode);
if (!isset($aFolders[$sFullName])) {
try
{
$aFolders[$sFullName] =
new \MailSo\Imap\Folder($sFullName, $sDelimiter, ['\\Noselect', '\\NonExistent']);
}
catch (\Throwable $oExc)
{
unset($oExc);
}
} }
\array_pop($aFolderExplode);
} }
} }

View file

@ -759,7 +759,7 @@ class Actions
if ($oSettingsLocal instanceof Settings) { if ($oSettingsLocal instanceof Settings) {
$aResult['SentFolder'] = (string)$oSettingsLocal->GetConf('SentFolder', ''); $aResult['SentFolder'] = (string)$oSettingsLocal->GetConf('SentFolder', '');
$aResult['DraftsFolder'] = (string)$oSettingsLocal->GetConf('DraftFolder', ''); $aResult['DraftsFolder'] = (string)$oSettingsLocal->GetConf('DraftFolder', '');
$aResult['SpamFolder'] = (string)$oSettingsLocal->GetConf('SpamFolder', ''); $aResult['JunkFolder'] = (string)$oSettingsLocal->GetConf('SpamFolder', '');
$aResult['TrashFolder'] = (string)$oSettingsLocal->GetConf('TrashFolder', ''); $aResult['TrashFolder'] = (string)$oSettingsLocal->GetConf('TrashFolder', '');
$aResult['ArchiveFolder'] = (string)$oSettingsLocal->GetConf('ArchiveFolder', ''); $aResult['ArchiveFolder'] = (string)$oSettingsLocal->GetConf('ArchiveFolder', '');
$aResult['HideUnsubscribed'] = (bool)$oSettingsLocal->GetConf('HideUnsubscribed', $aResult['HideUnsubscribed']); $aResult['HideUnsubscribed'] = (bool)$oSettingsLocal->GetConf('HideUnsubscribed', $aResult['HideUnsubscribed']);

View file

@ -216,7 +216,7 @@ trait Accounts
$oConfig = $this->Config(); $oConfig = $this->Config();
$aResult['SentFolder'] = (string) $oSettingsLocal->GetConf('SentFolder', ''); $aResult['SentFolder'] = (string) $oSettingsLocal->GetConf('SentFolder', '');
$aResult['DraftsFolder'] = (string) $oSettingsLocal->GetConf('DraftFolder', ''); $aResult['DraftsFolder'] = (string) $oSettingsLocal->GetConf('DraftFolder', '');
$aResult['SpamFolder'] = (string) $oSettingsLocal->GetConf('SpamFolder', ''); $aResult['JunkFolder'] = (string) $oSettingsLocal->GetConf('SpamFolder', '');
$aResult['TrashFolder'] = (string) $oSettingsLocal->GetConf('TrashFolder', ''); $aResult['TrashFolder'] = (string) $oSettingsLocal->GetConf('TrashFolder', '');
$aResult['ArchiveFolder'] = (string) $oSettingsLocal->GetConf('ArchiveFolder', ''); $aResult['ArchiveFolder'] = (string) $oSettingsLocal->GetConf('ArchiveFolder', '');
$aResult['HideUnsubscribed'] = (bool) $oSettingsLocal->GetConf('HideUnsubscribed', false); $aResult['HideUnsubscribed'] = (bool) $oSettingsLocal->GetConf('HideUnsubscribed', false);

View file

@ -10,11 +10,6 @@ use MailSo\Imap\Enumerations\FolderType;
trait Folders trait Folders
{ {
private function getFolderCollection(bool $HideUnsubscribed) : ?\MailSo\Mail\FolderCollection
{
return $this->MailClient()->Folders('', '*', $HideUnsubscribed);
}
/** /**
* Appends uploaded rfc822 message to mailbox * Appends uploaded rfc822 message to mailbox
* @throws \MailSo\RuntimeException * @throws \MailSo\RuntimeException
@ -56,125 +51,32 @@ trait Folders
$HideUnsubscribed = (bool) $oSettingsLocal->GetConf('HideUnsubscribed', $HideUnsubscribed); $HideUnsubscribed = (bool) $oSettingsLocal->GetConf('HideUnsubscribed', $HideUnsubscribed);
} }
$oFolderCollection = $this->getFolderCollection($HideUnsubscribed); $oFolderCollection = $this->MailClient()->Folders('', '*', $HideUnsubscribed);
if ($oFolderCollection) { if ($oFolderCollection) {
$sNamespace = $this->MailClient()->GetPersonalNamespace(); $aQuota = null;
if ($this->GetCapa(Capa::QUOTA)) {
$this->Plugins()->RunHook('filter.folders-post', array($oAccount, $oFolderCollection)); try {
// $aQuota = $this->MailClient()->Quota();
$aSystemFolders = array(); $aQuota = $this->MailClient()->QuotaRoot();
$this->recFoldersTypes($oAccount, $oFolderCollection, $aSystemFolders); } catch (\Throwable $oException) {
// ignore
if ($this->Config()->Get('labs', 'autocreate_system_folders', false)) {
$bDoItAgain = false;
$sParent = \substr($sNamespace, 0, -1);
$sDelimiter = $oFolderCollection->FindDelimiter();
$aList = array();
$aMap = $this->systemFoldersNames($oAccount);
if ('' === $oSettingsLocal->GetConf('SentFolder', '')) {
$aList[] = FolderType::SENT;
}
if ('' === $oSettingsLocal->GetConf('DraftFolder', '')) {
$aList[] = FolderType::DRAFTS;
}
if ('' === $oSettingsLocal->GetConf('SpamFolder', '')) {
$aList[] = FolderType::JUNK;
}
if ('' === $oSettingsLocal->GetConf('TrashFolder', '')) {
$aList[] = FolderType::TRASH;
}
if ('' === $oSettingsLocal->GetConf('ArchiveFolder', '')) {
$aList[] = FolderType::ARCHIVE;
}
$this->Plugins()->RunHook('filter.folders-system-types', array($oAccount, &$aList));
foreach ($aList as $iType) {
if (!isset($aSystemFolders[$iType])) {
$mFolderNameToCreate = \array_search($iType, $aMap);
if (!empty($mFolderNameToCreate)) {
$iPos = \strrpos($mFolderNameToCreate, $sDelimiter);
if (false !== $iPos) {
$mNewParent = \substr($mFolderNameToCreate, 0, $iPos);
$mNewFolderNameToCreate = \substr($mFolderNameToCreate, $iPos + 1);
if (\strlen($mNewFolderNameToCreate)) {
$mFolderNameToCreate = $mNewFolderNameToCreate;
}
if (\strlen($mNewParent)) {
$sParent = \strlen($sParent) ? $sParent.$sDelimiter.$mNewParent : $mNewParent;
}
}
$sFullNameToCheck = $mFolderNameToCreate;
if (\strlen($sParent)) {
$sFullNameToCheck = $sParent.$sDelimiter.$sFullNameToCheck;
}
if (!isset($oFolderCollection[$sFullNameToCheck])) {
try
{
$this->MailClient()->FolderCreate($mFolderNameToCreate, $sParent, true, $sDelimiter);
$bDoItAgain = true;
}
catch (\Throwable $oException)
{
$this->Logger()->WriteException($oException);
}
}
}
}
}
if ($bDoItAgain) {
$oFolderCollection = $this->getFolderCollection($HideUnsubscribed);
if ($oFolderCollection) {
$aSystemFolders = array();
$this->recFoldersTypes($oAccount, $oFolderCollection, $aSystemFolders);
}
} }
} }
if ($oFolderCollection) { $aCapabilities = \array_values(\array_filter($this->MailClient()->Capability(), function ($item) {
$this->Plugins()->RunHook('filter.folders-complete', array($oAccount, $oFolderCollection)); return !\preg_match('/^(IMAP|AUTH|LOGIN|SASL)/', $item);
}));
$aQuota = null; $oFolderCollection = \array_merge(
if ($this->GetCapa(Capa::QUOTA)) { $oFolderCollection->jsonSerialize(),
try { array(
// $aQuota = $this->MailClient()->Quota(); 'quotaUsage' => $aQuota ? $aQuota[0] * 1024 : null,
$aQuota = $this->MailClient()->QuotaRoot(); 'quotaLimit' => $aQuota ? $aQuota[1] * 1024 : null,
} catch (\Throwable $oException) { 'namespace' => $this->MailClient()->GetPersonalNamespace(),
// ignore 'capabilities' => $aCapabilities
} )
} );
$aCapabilities = \array_filter($this->MailClient()->Capability(), function ($item) {
return !\preg_match('/^(IMAP|AUTH|LOGIN|SASL)/', $item);
});
$oFolderCollection = \array_merge(
$oFolderCollection->jsonSerialize(),
array(
'quotaUsage' => $aQuota ? $aQuota[0] * 1024 : null,
'quotaLimit' => $aQuota ? $aQuota[1] * 1024 : null,
'Namespace' => $sNamespace,
'Optimized' => $oFolderCollection->Optimized,
'CountRec' => $oFolderCollection->count(),
'SystemFolders' => empty($aSystemFolders) ? null : $aSystemFolders,
'Capabilities' => \array_values($aCapabilities)
)
);
}
} }
return $this->DefaultResponse(__FUNCTION__, $oFolderCollection); return $this->DefaultResponse(__FUNCTION__, $oFolderCollection);
@ -439,107 +341,4 @@ trait Folders
return $this->DefaultResponse(__FUNCTION__, return $this->DefaultResponse(__FUNCTION__,
$this->SettingsProvider(true)->Save($oAccount, $oSettingsLocal)); $this->SettingsProvider(true)->Save($oAccount, $oSettingsLocal));
} }
private function recFoldersTypes(\RainLoop\Model\Account $oAccount, \MailSo\Mail\FolderCollection $oFolders, array &$aResult, bool $bListFolderTypes = true) : void
{
if ($oFolders->count()) {
if ($bListFolderTypes) {
$types = array(
FolderType::INBOX,
FolderType::SENT,
FolderType::DRAFTS,
FolderType::JUNK,
FolderType::TRASH,
FolderType::ARCHIVE
);
foreach ($oFolders as $oFolder) {
$iFolderType = $oFolder->GetType();
if (!isset($aResult[$iFolderType]) && \in_array($iFolderType, $types)) {
$aResult[$iFolderType] = $oFolder->FullName();
}
}
}
$aMap = $this->systemFoldersNames($oAccount);
foreach ($oFolders as $oFolder) {
$sName = $oFolder->Name();
$sFullName = $oFolder->FullName();
if (isset($aMap[$sName]) || isset($aMap[$sFullName])) {
$iFolderType = isset($aMap[$sName]) ? $aMap[$sName] : $aMap[$sFullName];
if ((!isset($aResult[$iFolderType]) || $sName === $sFullName || "INBOX{$oFolder->Delimiter()}{$sName}" === $sFullName) && \in_array($iFolderType, $types)) {
$aResult[$iFolderType] = $oFolder->FullName();
}
}
}
}
}
/**
* @staticvar array $aCache
*/
private function systemFoldersNames(\RainLoop\Model\Account $oAccount) : array
{
static $aCache = null;
if (null === $aCache) {
$aCache = array(
'Sent' => FolderType::SENT,
'Send' => FolderType::SENT,
'Outbox' => FolderType::SENT,
'Out box' => FolderType::SENT,
'Sent Item' => FolderType::SENT,
'Sent Items' => FolderType::SENT,
'Send Item' => FolderType::SENT,
'Send Items' => FolderType::SENT,
'Sent Mail' => FolderType::SENT,
'Sent Mails' => FolderType::SENT,
'Send Mail' => FolderType::SENT,
'Send Mails' => FolderType::SENT,
'Drafts' => FolderType::DRAFTS,
'Draft' => FolderType::DRAFTS,
'Draft Mail' => FolderType::DRAFTS,
'Draft Mails' => FolderType::DRAFTS,
'Drafts Mail' => FolderType::DRAFTS,
'Drafts Mails' => FolderType::DRAFTS,
'Junk E-mail' => FolderType::JUNK,
'Spam' => FolderType::JUNK,
'Spams' => FolderType::JUNK,
'Junk' => FolderType::JUNK,
'Bulk Mail' => FolderType::JUNK,
'Bulk Mails' => FolderType::JUNK,
'Deleted Items' => FolderType::TRASH,
'Trash' => FolderType::TRASH,
'Deleted' => FolderType::TRASH,
'Bin' => FolderType::TRASH,
'Archive' => FolderType::ARCHIVE,
'Archives' => FolderType::ARCHIVE,
'All' => FolderType::ALL,
'All Mail' => FolderType::ALL,
'All Mails' => FolderType::ALL,
);
$aNewCache = array();
foreach ($aCache as $sKey => $iType) {
$aNewCache[$sKey] = $iType;
$aNewCache[\str_replace(' ', '', $sKey)] = $iType;
}
$aCache = $aNewCache;
$this->Plugins()->RunHook('filter.system-folders-names', array($oAccount, &$aCache));
}
return $aCache;
}
} }

View file

@ -370,7 +370,6 @@ Enables caching in the system'),
'labs' => array( 'labs' => array(
'cache_system_data' => array(true), 'cache_system_data' => array(true),
'date_from_headers' => array(true), 'date_from_headers' => array(true),
'autocreate_system_folders' => array(false),
'allow_message_append' => array(false), 'allow_message_append' => array(false),
'login_fault_delay' => array(1), 'login_fault_delay' => array(1),
'log_ajax_response_write_limit' => array(300), 'log_ajax_response_write_limit' => array(300),