mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
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:
parent
8799cd3e8d
commit
54896bf044
18 changed files with 419 additions and 468 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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: () => {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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}(),
|
||||||
|
|
|
||||||
219
plugins/mailbox-detect/index.php
Normal file
219
plugins/mailbox-detect/index.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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,9 +134,8 @@ 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'
|
||||||
|
|
@ -148,32 +146,15 @@ class Folder implements \JsonSerializable
|
||||||
'\\junk', // '\\spam'
|
'\\junk', // '\\spam'
|
||||||
'\\sent', // '\\sentmail'
|
'\\sent', // '\\sentmail'
|
||||||
'\\trash', // '\\bin'
|
'\\trash', // '\\bin'
|
||||||
], $aFlags);
|
], $this->aFlagsLowerCase);
|
||||||
if ($match) {
|
if ($match) {
|
||||||
return \substr(\array_shift($match), 1);
|
$role = \array_shift($match);
|
||||||
}
|
}
|
||||||
|
if (!$role && 'INBOX' === \strtoupper($this->FolderName)) {
|
||||||
if ('INBOX' === \strtoupper($this->FolderName)) {
|
|
||||||
return 'inbox';
|
return 'inbox';
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
// Kolab
|
|
||||||
$type = $this->GetMetadata(MetadataKeys::KOLAB_CTYPE) ?: $this->GetMetadata(MetadataKeys::KOLAB_CTYPE_SHARED);
|
|
||||||
switch ($type) {
|
|
||||||
case 'mail.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()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -933,29 +933,11 @@ class MailClient
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($bUseListSubscribeStatus) {
|
||||||
foreach ($aFolders as $sFullName => /* @var $oImapFolder \MailSo\Imap\Folder */ $oImapFolder) {
|
foreach ($aFolders as $sFullName => /* @var $oImapFolder \MailSo\Imap\Folder */ $oImapFolder) {
|
||||||
if (($bUseListSubscribeStatus && (null === $aImapSubscribedFoldersHelper || \in_array($sFullName, $aImapSubscribedFoldersHelper))) || $oImapFolder->IsInbox()) {
|
if (null === $aImapSubscribedFoldersHelper || \in_array($sFullName, $aImapSubscribedFoldersHelper)) {
|
||||||
$oImapFolder->setSubscribed();
|
$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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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']);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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,98 +51,9 @@ 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();
|
|
||||||
|
|
||||||
$this->Plugins()->RunHook('filter.folders-post', array($oAccount, $oFolderCollection));
|
|
||||||
|
|
||||||
$aSystemFolders = array();
|
|
||||||
$this->recFoldersTypes($oAccount, $oFolderCollection, $aSystemFolders);
|
|
||||||
|
|
||||||
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) {
|
|
||||||
$this->Plugins()->RunHook('filter.folders-complete', array($oAccount, $oFolderCollection));
|
|
||||||
|
|
||||||
$aQuota = null;
|
$aQuota = null;
|
||||||
if ($this->GetCapa(Capa::QUOTA)) {
|
if ($this->GetCapa(Capa::QUOTA)) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -158,24 +64,20 @@ trait Folders
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$aCapabilities = \array_filter($this->MailClient()->Capability(), function ($item) {
|
$aCapabilities = \array_values(\array_filter($this->MailClient()->Capability(), function ($item) {
|
||||||
return !\preg_match('/^(IMAP|AUTH|LOGIN|SASL)/', $item);
|
return !\preg_match('/^(IMAP|AUTH|LOGIN|SASL)/', $item);
|
||||||
});
|
}));
|
||||||
|
|
||||||
$oFolderCollection = \array_merge(
|
$oFolderCollection = \array_merge(
|
||||||
$oFolderCollection->jsonSerialize(),
|
$oFolderCollection->jsonSerialize(),
|
||||||
array(
|
array(
|
||||||
'quotaUsage' => $aQuota ? $aQuota[0] * 1024 : null,
|
'quotaUsage' => $aQuota ? $aQuota[0] * 1024 : null,
|
||||||
'quotaLimit' => $aQuota ? $aQuota[1] * 1024 : null,
|
'quotaLimit' => $aQuota ? $aQuota[1] * 1024 : null,
|
||||||
'Namespace' => $sNamespace,
|
'namespace' => $this->MailClient()->GetPersonalNamespace(),
|
||||||
'Optimized' => $oFolderCollection->Optimized,
|
'capabilities' => $aCapabilities
|
||||||
'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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue