mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-09 06:28:28 +03:00
Capability improvements
Additional branding options
This commit is contained in:
parent
7fce424758
commit
763fae345b
46 changed files with 880 additions and 556 deletions
|
|
@ -375,6 +375,11 @@
|
|||
Globals.$html.toggleClass('rl-left-panel-disabled', bValue);
|
||||
});
|
||||
|
||||
Globals.leftPanelType.subscribe(function (sValue) {
|
||||
Globals.$html.toggleClass('rl-left-panel-none', 'none' === sValue);
|
||||
Globals.$html.toggleClass('rl-left-panel-short', 'short' === sValue);
|
||||
});
|
||||
|
||||
ssm.ready();
|
||||
|
||||
require('Stores/Language').populate();
|
||||
|
|
|
|||
|
|
@ -1355,8 +1355,8 @@
|
|||
|
||||
kn.startScreens([
|
||||
require('Screen/User/MailBox'),
|
||||
require('Screen/User/Settings'),
|
||||
require('Screen/User/About')
|
||||
Settings.capa(Enums.Capa.Settings) ? require('Screen/User/Settings') : null,
|
||||
false ? require('Screen/User/About') : null
|
||||
]);
|
||||
|
||||
if (bGoogle || bFacebook || bTwitter)
|
||||
|
|
@ -1435,7 +1435,9 @@
|
|||
}, 1000);
|
||||
}
|
||||
|
||||
if (!!Settings.settingsGet('AccountSignMe') && window.navigator.registerProtocolHandler)
|
||||
if (!!Settings.settingsGet('AccountSignMe') &&
|
||||
window.navigator.registerProtocolHandler &&
|
||||
Settings.capa(Enums.Capa.Composer))
|
||||
{
|
||||
_.delay(function () {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -204,7 +204,10 @@
|
|||
*/
|
||||
CacheUserStorage.prototype.setFolderHash = function (sFolderFullNameRaw, sFolderHash)
|
||||
{
|
||||
this.oFolderHashCache[sFolderFullNameRaw] = sFolderHash;
|
||||
if ('' !== sFolderFullNameRaw)
|
||||
{
|
||||
this.oFolderHashCache[sFolderFullNameRaw] = sFolderHash;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -72,6 +72,18 @@
|
|||
'OpenPGP': 'OPEN_PGP',
|
||||
'Prefetch': 'PREFETCH',
|
||||
'Gravatar': 'GRAVATAR',
|
||||
'Folders': 'FOLDERS',
|
||||
'Composer': 'COMPOSER',
|
||||
'Contacts': 'CONTACTS',
|
||||
'Reload': 'RELOAD',
|
||||
'Search': 'SEARCH',
|
||||
'SearchAdv': 'SEARCH_ADV',
|
||||
'MessageActions': 'MESSAGE_ACTIONS',
|
||||
'MessageListActions': 'MESSAGELIST_ACTIONS',
|
||||
'AttachmentsActions': 'ATTACHMENTS_ACTIONS',
|
||||
'DangerousActions': 'DANGEROUS_ACTIONS',
|
||||
'Settings': 'SETTINGS',
|
||||
'Help': 'HELP',
|
||||
'Themes': 'THEMES',
|
||||
'UserBackground': 'USER_BACKGROUND',
|
||||
'Sieve': 'SIEVE',
|
||||
|
|
|
|||
|
|
@ -208,6 +208,7 @@
|
|||
};
|
||||
|
||||
Globals.leftPanelDisabled = ko.observable(false);
|
||||
Globals.leftPanelType = ko.observable('');
|
||||
|
||||
// popups
|
||||
Globals.popupVisibilityNames = ko.observableArray([]);
|
||||
|
|
|
|||
|
|
@ -394,11 +394,13 @@
|
|||
};
|
||||
|
||||
/**
|
||||
* @param {boolean} bXAuth = false
|
||||
* @return {string}
|
||||
*/
|
||||
Links.prototype.socialGoogle = function ()
|
||||
Links.prototype.socialGoogle = function (bXAuth)
|
||||
{
|
||||
return this.sServer + 'SocialGoogle' + ('' !== this.sAuthSuffix ? '/' + this.subQueryPrefix() + '/' + this.sAuthSuffix + '/' : '');
|
||||
return this.sServer + 'SocialGoogle' + ('' !== this.sAuthSuffix ? '/' + this.subQueryPrefix() + '/' + this.sAuthSuffix + '/' : '') +
|
||||
(bXAuth ? '&xauth=1' : '');
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -150,6 +150,11 @@
|
|||
{
|
||||
if (sMailToUrl && 'mailto:' === sMailToUrl.toString().substr(0, 7).toLowerCase())
|
||||
{
|
||||
if (!PopupComposeVoreModel)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
sMailToUrl = sMailToUrl.toString().substr(7);
|
||||
|
||||
var
|
||||
|
|
|
|||
|
|
@ -427,19 +427,22 @@
|
|||
|
||||
_.each(aScreensClasses, function (CScreen) {
|
||||
|
||||
var
|
||||
oScreen = new CScreen(),
|
||||
sScreenName = oScreen ? oScreen.screenName() : ''
|
||||
;
|
||||
|
||||
if (oScreen && '' !== sScreenName)
|
||||
if (CScreen)
|
||||
{
|
||||
if ('' === this.sDefaultScreenName)
|
||||
{
|
||||
this.sDefaultScreenName = sScreenName;
|
||||
}
|
||||
var
|
||||
oScreen = new CScreen(),
|
||||
sScreenName = oScreen ? oScreen.screenName() : ''
|
||||
;
|
||||
|
||||
this.oScreens[sScreenName] = oScreen;
|
||||
if (oScreen && '' !== sScreenName)
|
||||
{
|
||||
if ('' === this.sDefaultScreenName)
|
||||
{
|
||||
this.sDefaultScreenName = sScreenName;
|
||||
}
|
||||
|
||||
this.oScreens[sScreenName] = oScreen;
|
||||
}
|
||||
}
|
||||
|
||||
}, this);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
FolderStore = require('Stores/User/Folder'),
|
||||
MessageStore = require('Stores/User/Message'),
|
||||
|
||||
Settings = require('Storage/Settings'),
|
||||
|
||||
AbstractScreen = require('Knoin/AbstractScreen')
|
||||
;
|
||||
|
||||
|
|
@ -64,6 +66,16 @@
|
|||
|
||||
AppStore.focusedState(Enums.Focused.None);
|
||||
AppStore.focusedState(Enums.Focused.MessageList);
|
||||
|
||||
if (!Settings.capa(Enums.Capa.Folders))
|
||||
{
|
||||
Globals.leftPanelType(
|
||||
Settings.capa(Enums.Capa.Composer) || Settings.capa(Enums.Capa.Contacts) ? 'short' : 'none');
|
||||
}
|
||||
else
|
||||
{
|
||||
Globals.leftPanelType('');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -47,6 +47,16 @@
|
|||
*/
|
||||
SettingsUserScreen.prototype.setupSettings = function (fCallback)
|
||||
{
|
||||
if (!Settings.capa(Enums.Capa.Settings))
|
||||
{
|
||||
if (fCallback)
|
||||
{
|
||||
fCallback();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
kn.addSettingsViewModel(require('Settings/User/General'),
|
||||
'SettingsGeneral', 'SETTINGS_LABELS/LABEL_GENERAL_NAME', 'general', true);
|
||||
|
||||
|
|
@ -96,8 +106,11 @@
|
|||
'SettingsTemplates', 'SETTINGS_LABELS/LABEL_TEMPLATES_NAME', 'templates');
|
||||
}
|
||||
|
||||
kn.addSettingsViewModel(require('Settings/User/Folders'),
|
||||
'SettingsFolders', 'SETTINGS_LABELS/LABEL_FOLDERS_NAME', 'folders');
|
||||
if (Settings.capa(Enums.Capa.Folders))
|
||||
{
|
||||
kn.addSettingsViewModel(require('Settings/User/Folders'),
|
||||
'SettingsFolders', 'SETTINGS_LABELS/LABEL_FOLDERS_NAME', 'folders');
|
||||
}
|
||||
|
||||
if (Settings.capa(Enums.Capa.Themes))
|
||||
{
|
||||
|
|
@ -117,12 +130,15 @@
|
|||
{
|
||||
fCallback();
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
SettingsUserScreen.prototype.onShow = function ()
|
||||
{
|
||||
this.setSettingsTitle();
|
||||
Globals.keyScope(Enums.KeyState.Settings);
|
||||
Globals.leftPanelType('');
|
||||
};
|
||||
|
||||
SettingsUserScreen.prototype.setSettingsTitle = function ()
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@
|
|||
this.userLogo = ko.observable(Settings.settingsGet('UserLogo') || '');
|
||||
this.userLogo.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
|
||||
|
||||
this.userLogoMessage = ko.observable(Settings.settingsGet('UserLogoMessage') || '');
|
||||
this.userLogoMessage.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
|
||||
|
||||
this.userLogoTitle = ko.observable(Settings.settingsGet('UserLogoTitle') || '');
|
||||
this.userLogoTitle.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
|
||||
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@
|
|||
/**/
|
||||
}
|
||||
|
||||
html.rl-left-panel-disabled {
|
||||
html.rl-left-panel-disabled, html.rl-left-panel-short {
|
||||
.btn.buttonContacts {
|
||||
margin-top: 10px !important;
|
||||
margin-left: 0 !important;
|
||||
|
|
|
|||
|
|
@ -205,6 +205,28 @@ html.rl-left-panel-disabled {
|
|||
}
|
||||
}
|
||||
|
||||
html.rl-left-panel-short {
|
||||
|
||||
#rl-left {
|
||||
width: 60px !important;
|
||||
}
|
||||
|
||||
#rl-right {
|
||||
left: 60px !important;
|
||||
}
|
||||
}
|
||||
|
||||
html.rl-left-panel-none {
|
||||
|
||||
#rl-left {
|
||||
width: 10px !important;
|
||||
}
|
||||
|
||||
#rl-right {
|
||||
left: 10px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-resizable-helper-w {
|
||||
border-right: 5px solid #777;
|
||||
border-right-color: rgba(255,255,255,0.7);
|
||||
|
|
|
|||
|
|
@ -53,6 +53,10 @@ html.rl-no-preview-pane {
|
|||
}
|
||||
}
|
||||
|
||||
.logoPlace {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.b-message-view-desc {
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@
|
|||
this.resizerTrigger = _.bind(this.resizerTrigger, this);
|
||||
|
||||
this.allowContacts = !!AppStore.contactsIsAllowed();
|
||||
this.allowFolders = !!Settings.capa(Enums.Capa.Folders);
|
||||
|
||||
this.bSkipNextHide = false;
|
||||
this.composeInEdit = AppStore.composeInEdit;
|
||||
|
|
@ -321,7 +322,7 @@
|
|||
}
|
||||
}, this));
|
||||
|
||||
this.canBeSendedOrSaved = ko.computed(function () {
|
||||
this.canBeSentOrSaved = ko.computed(function () {
|
||||
return !this.sending() && !this.saving();
|
||||
}, this);
|
||||
|
||||
|
|
@ -375,6 +376,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
if (!this.allowFolders)
|
||||
{
|
||||
sSentFolder = Consts.Values.UnuseOptionValue;
|
||||
}
|
||||
|
||||
if ('' === sSentFolder)
|
||||
{
|
||||
kn.showScreenPopup(require('View/Popup/FolderSystem'), [Enums.SetSystemFoldersNotification.Sent]);
|
||||
|
|
@ -432,10 +438,16 @@
|
|||
);
|
||||
}
|
||||
}
|
||||
}, this.canBeSendedOrSaved);
|
||||
|
||||
}, this.canBeSentOrSaved);
|
||||
|
||||
this.saveCommand = Utils.createCommand(this, function () {
|
||||
|
||||
if (!this.allowFolders)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FolderStore.draftFolderNotEnabled())
|
||||
{
|
||||
kn.showScreenPopup(require('View/Popup/FolderSystem'), [Enums.SetSystemFoldersNotification.Draft]);
|
||||
|
|
@ -470,7 +482,7 @@
|
|||
);
|
||||
}
|
||||
|
||||
}, this.canBeSendedOrSaved);
|
||||
}, this.canBeSentOrSaved);
|
||||
|
||||
this.skipCommand = Utils.createCommand(this, function () {
|
||||
|
||||
|
|
@ -484,7 +496,7 @@
|
|||
|
||||
this.tryToClosePopup();
|
||||
|
||||
}, this.canBeSendedOrSaved);
|
||||
}, this.canBeSentOrSaved);
|
||||
|
||||
this.contactsCommand = Utils.createCommand(this, function () {
|
||||
|
||||
|
|
@ -635,7 +647,7 @@
|
|||
sDraftFolder = FolderStore.draftFolder()
|
||||
;
|
||||
|
||||
if ('' !== sDraftFolder)
|
||||
if ('' !== sDraftFolder && Consts.Values.UnuseOptionValue !== sDraftFolder)
|
||||
{
|
||||
Cache.setFolderHash(sDraftFolder, '');
|
||||
if (FolderStore.currentFolderFullNameRaw() === sDraftFolder)
|
||||
|
|
@ -1413,10 +1425,13 @@
|
|||
return false;
|
||||
});
|
||||
|
||||
key('ctrl+s, command+s', Enums.KeyState.Compose, function () {
|
||||
self.saveCommand();
|
||||
return false;
|
||||
});
|
||||
if (this.allowFolders)
|
||||
{
|
||||
key('ctrl+s, command+s', Enums.KeyState.Compose, function () {
|
||||
self.saveCommand();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
if (!!Settings.settingsGet('AllowCtrlEnterOnCompose'))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
SettingsStore = require('Stores/User/Settings'),
|
||||
ContactStore = require('Stores/User/Contact'),
|
||||
|
||||
Settings = require('Storage/Settings'),
|
||||
|
||||
Remote = require('Remote/User/Ajax'),
|
||||
|
||||
EmailModel = require('Model/Email'),
|
||||
|
|
@ -237,6 +239,12 @@
|
|||
});
|
||||
|
||||
this.newMessageCommand = Utils.createCommand(this, function () {
|
||||
|
||||
if (!Settings.capa(Enums.Capa.Composer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var
|
||||
aE = [],
|
||||
aC = this.contactsCheckedOrSelected(),
|
||||
|
|
@ -772,7 +780,10 @@
|
|||
{
|
||||
this.bBackToCompose = false;
|
||||
|
||||
kn.showScreenPopup(require('View/Popup/Compose'));
|
||||
if (Settings.capa(Enums.Capa.Composer))
|
||||
{
|
||||
kn.showScreenPopup(require('View/Popup/Compose'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@
|
|||
this.logoImg = Utils.trim(Settings.settingsGet('UserLogo'));
|
||||
this.logoTitle = Utils.trim(Settings.settingsGet('UserLogoTitle'));
|
||||
|
||||
this.allowSettings = !!Settings.capa(Enums.Capa.Settings);
|
||||
this.allowHelp = !!Settings.capa(Enums.Capa.Help);
|
||||
|
||||
this.currentAudio = AppStore.currentAudio;
|
||||
|
||||
this.accountEmail = AccountStore.email;
|
||||
|
|
@ -84,12 +87,18 @@
|
|||
|
||||
AbstractSystemDropDownUserView.prototype.settingsClick = function ()
|
||||
{
|
||||
require('Knoin/Knoin').setHash(Links.settings());
|
||||
if (Settings.capa(Enums.Capa.Settings))
|
||||
{
|
||||
require('Knoin/Knoin').setHash(Links.settings());
|
||||
}
|
||||
};
|
||||
|
||||
AbstractSystemDropDownUserView.prototype.settingsHelp = function ()
|
||||
{
|
||||
require('Knoin/Knoin').showScreenPopup(require('View/Popup/KeyboardShortcutsHelp'));
|
||||
if (Settings.capa(Enums.Capa.Help))
|
||||
{
|
||||
require('Knoin/Knoin').showScreenPopup(require('View/Popup/KeyboardShortcutsHelp'));
|
||||
}
|
||||
};
|
||||
|
||||
AbstractSystemDropDownUserView.prototype.addAccountClick = function ()
|
||||
|
|
|
|||
|
|
@ -306,6 +306,17 @@
|
|||
return !this.submitRequest() && this.googleLoginEnabled();
|
||||
});
|
||||
|
||||
this.googleXAuthCommand = Utils.createCommand(this, function () {
|
||||
|
||||
window.open(Links.socialGoogle(true), 'Google',
|
||||
'left=200,top=100,width=650,height=450,menubar=no,status=no,resizable=yes,scrollbars=yes');
|
||||
|
||||
return true;
|
||||
|
||||
}, function () {
|
||||
return !this.submitRequest() && this.googleLoginEnabled();
|
||||
});
|
||||
|
||||
this.twitterLoginEnabled = ko.observable(false);
|
||||
|
||||
this.twitterCommand = Utils.createCommand(this, function () {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
FolderStore = require('Stores/User/Folder'),
|
||||
MessageStore = require('Stores/User/Message'),
|
||||
|
||||
Settings = require('Storage/Settings'),
|
||||
|
||||
kn = require('Knoin/Knoin'),
|
||||
AbstractView = require('Knoin/AbstractView')
|
||||
;
|
||||
|
|
@ -50,7 +52,9 @@
|
|||
|
||||
this.iDropOverTimer = 0;
|
||||
|
||||
this.allowComposer = !!Settings.capa(Enums.Capa.Composer);
|
||||
this.allowContacts = !!AppStore.contactsIsAllowed();
|
||||
this.allowFolders = !!Settings.capa(Enums.Capa.Folders);
|
||||
|
||||
this.folderListFocused = ko.computed(function () {
|
||||
return Enums.Focused.FolderList === AppStore.focusedState();
|
||||
|
|
@ -259,7 +263,10 @@
|
|||
|
||||
FolderListMailBoxUserView.prototype.composeClick = function ()
|
||||
{
|
||||
kn.showScreenPopup(require('View/Popup/Compose'));
|
||||
if (Settings.capa(Enums.Capa.Composer))
|
||||
{
|
||||
kn.showScreenPopup(require('View/Popup/Compose'));
|
||||
}
|
||||
};
|
||||
|
||||
FolderListMailBoxUserView.prototype.createFolder = function ()
|
||||
|
|
|
|||
|
|
@ -48,7 +48,12 @@
|
|||
this.bPrefetch = false;
|
||||
this.emptySubjectValue = '';
|
||||
|
||||
this.hideDangerousActions = !!Settings.settingsGet('HideDangerousActions');
|
||||
this.allowReload = !!Settings.capa(Enums.Capa.Reload);
|
||||
this.allowSearch = !!Settings.capa(Enums.Capa.Search);
|
||||
this.allowSearchAdv = !!Settings.capa(Enums.Capa.SearchAdv);
|
||||
this.allowComposer = !!Settings.capa(Enums.Capa.Composer);
|
||||
this.allowMessageListActions = !!Settings.capa(Enums.Capa.MessageListActions);
|
||||
this.allowDangerousActions = !!Settings.capa(Enums.Capa.DangerousActions);
|
||||
|
||||
this.popupVisibility = Globals.popupVisibility;
|
||||
|
||||
|
|
@ -200,18 +205,27 @@
|
|||
this.canBeMoved = this.hasCheckedOrSelectedLines;
|
||||
|
||||
this.clearCommand = Utils.createCommand(this, function () {
|
||||
kn.showScreenPopup(require('View/Popup/FolderClear'), [FolderStore.currentFolder()]);
|
||||
if (Settings.capa(Enums.Capa.DangerousActions))
|
||||
{
|
||||
kn.showScreenPopup(require('View/Popup/FolderClear'), [FolderStore.currentFolder()]);
|
||||
}
|
||||
});
|
||||
|
||||
this.multyForwardCommand = Utils.createCommand(this, function () {
|
||||
kn.showScreenPopup(require('View/Popup/Compose'), [
|
||||
Enums.ComposeType.ForwardAsAttachment, MessageStore.messageListCheckedOrSelected()]);
|
||||
if (Settings.capa(Enums.Capa.Composer))
|
||||
{
|
||||
kn.showScreenPopup(require('View/Popup/Compose'), [
|
||||
Enums.ComposeType.ForwardAsAttachment, MessageStore.messageListCheckedOrSelected()]);
|
||||
}
|
||||
}, this.canBeMoved);
|
||||
|
||||
this.deleteWithoutMoveCommand = Utils.createCommand(this, function () {
|
||||
require('App/User').deleteMessagesFromFolder(Enums.FolderType.Trash,
|
||||
FolderStore.currentFolderFullNameRaw(),
|
||||
MessageStore.messageListCheckedOrSelectedUidsWithSubMails(), false);
|
||||
if (Settings.capa(Enums.Capa.DangerousActions))
|
||||
{
|
||||
require('App/User').deleteMessagesFromFolder(Enums.FolderType.Trash,
|
||||
FolderStore.currentFolderFullNameRaw(),
|
||||
MessageStore.messageListCheckedOrSelectedUidsWithSubMails(), false);
|
||||
}
|
||||
}, this.canBeMoved);
|
||||
|
||||
this.deleteCommand = Utils.createCommand(this, function () {
|
||||
|
|
@ -241,7 +255,7 @@
|
|||
this.moveCommand = Utils.createCommand(this, Utils.emptyFunction, this.canBeMoved);
|
||||
|
||||
this.reloadCommand = Utils.createCommand(this, function () {
|
||||
if (!MessageStore.messageListCompleteLoadingThrottleForAnimation())
|
||||
if (!MessageStore.messageListCompleteLoadingThrottleForAnimation() && this.allowReload)
|
||||
{
|
||||
require('App/User').reloadMessageList(false, true);
|
||||
}
|
||||
|
|
@ -718,37 +732,43 @@
|
|||
}
|
||||
});
|
||||
|
||||
// archive (zip)
|
||||
key('z', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
self.archiveCommand();
|
||||
return false;
|
||||
});
|
||||
|
||||
// delete
|
||||
key('delete, shift+delete, shift+3', Enums.KeyState.MessageList, function (event, handler) {
|
||||
if (event)
|
||||
{
|
||||
if (0 < MessageStore.messageListCheckedOrSelected().length)
|
||||
{
|
||||
if (handler && 'shift+delete' === handler.shortcut)
|
||||
{
|
||||
self.deleteWithoutMoveCommand();
|
||||
}
|
||||
else
|
||||
{
|
||||
self.deleteCommand();
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.capa(Enums.Capa.MessageListActions))
|
||||
{
|
||||
// archive (zip)
|
||||
key('z', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
self.archiveCommand();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// check mail
|
||||
key('ctrl+r, command+r', [Enums.KeyState.FolderList, Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
self.reloadCommand();
|
||||
return false;
|
||||
});
|
||||
// delete
|
||||
key('delete, shift+delete, shift+3', Enums.KeyState.MessageList, function (event, handler) {
|
||||
if (event)
|
||||
{
|
||||
if (0 < MessageStore.messageListCheckedOrSelected().length)
|
||||
{
|
||||
if (handler && 'shift+delete' === handler.shortcut)
|
||||
{
|
||||
self.deleteWithoutMoveCommand();
|
||||
}
|
||||
else
|
||||
{
|
||||
self.deleteCommand();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (Settings.capa(Enums.Capa.Reload))
|
||||
{
|
||||
// check mail
|
||||
key('ctrl+r, command+r', [Enums.KeyState.FolderList, Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
self.reloadCommand();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// check all
|
||||
key('ctrl+a, command+a', Enums.KeyState.MessageList, function () {
|
||||
|
|
@ -756,17 +776,23 @@
|
|||
return false;
|
||||
});
|
||||
|
||||
// write/compose (open compose popup)
|
||||
key('w,c', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
kn.showScreenPopup(require('View/Popup/Compose'));
|
||||
return false;
|
||||
});
|
||||
if (Settings.capa(Enums.Capa.Composer))
|
||||
{
|
||||
// write/compose (open compose popup)
|
||||
key('w,c', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
kn.showScreenPopup(require('View/Popup/Compose'));
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// important - star/flag messages
|
||||
key('i', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
self.flagMessagesFast();
|
||||
return false;
|
||||
});
|
||||
if (Settings.capa(Enums.Capa.MessageListActions))
|
||||
{
|
||||
// important - star/flag messages
|
||||
key('i', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
self.flagMessagesFast();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
key('t', [Enums.KeyState.MessageList], function () {
|
||||
|
||||
|
|
@ -784,34 +810,46 @@
|
|||
return false;
|
||||
});
|
||||
|
||||
// move
|
||||
key('m', Enums.KeyState.MessageList, function () {
|
||||
self.moveDropdownTrigger(true);
|
||||
return false;
|
||||
});
|
||||
if (Settings.capa(Enums.Capa.MessageListActions))
|
||||
{
|
||||
// move
|
||||
key('m', Enums.KeyState.MessageList, function () {
|
||||
self.moveDropdownTrigger(true);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// read
|
||||
key('q', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
self.seenMessagesFast(true);
|
||||
return false;
|
||||
});
|
||||
if (Settings.capa(Enums.Capa.MessageListActions))
|
||||
{
|
||||
// read
|
||||
key('q', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
self.seenMessagesFast(true);
|
||||
return false;
|
||||
});
|
||||
|
||||
// unread
|
||||
key('u', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
self.seenMessagesFast(false);
|
||||
return false;
|
||||
});
|
||||
// unread
|
||||
key('u', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
self.seenMessagesFast(false);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
key('shift+f', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
self.multyForwardCommand();
|
||||
return false;
|
||||
});
|
||||
if (Settings.capa(Enums.Capa.Composer))
|
||||
{
|
||||
key('shift+f', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
self.multyForwardCommand();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// search input focus
|
||||
key('/', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
self.inputMessageListSearchFocus(true);
|
||||
return false;
|
||||
});
|
||||
if (Settings.capa(Enums.Capa.Search))
|
||||
{
|
||||
// search input focus
|
||||
key('/', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
self.inputMessageListSearchFocus(true);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// cancel search
|
||||
key('esc', Enums.KeyState.MessageList, function () {
|
||||
|
|
@ -887,12 +925,18 @@
|
|||
|
||||
MessageListMailBoxUserView.prototype.composeClick = function ()
|
||||
{
|
||||
kn.showScreenPopup(require('View/Popup/Compose'));
|
||||
if (Settings.capa(Enums.Capa.Composer))
|
||||
{
|
||||
kn.showScreenPopup(require('View/Popup/Compose'));
|
||||
}
|
||||
};
|
||||
|
||||
MessageListMailBoxUserView.prototype.advancedSearchClick = function ()
|
||||
{
|
||||
kn.showScreenPopup(require('View/Popup/AdvancedSearch'));
|
||||
if (Settings.capa(Enums.Capa.SearchAdv))
|
||||
{
|
||||
kn.showScreenPopup(require('View/Popup/AdvancedSearch'));
|
||||
}
|
||||
};
|
||||
|
||||
MessageListMailBoxUserView.prototype.quotaTooltip = function ()
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
MessageStore = require('Stores/User/Message'),
|
||||
|
||||
Local = require('Storage/Client'),
|
||||
Settings = require('Storage/Settings'),
|
||||
Remote = require('Remote/User/Ajax'),
|
||||
|
||||
Promises = require('Promises/User/Ajax'),
|
||||
|
|
@ -67,6 +68,12 @@
|
|||
|
||||
this.pswp = null;
|
||||
|
||||
this.allowComposer = !!Settings.capa(Enums.Capa.Composer);
|
||||
this.allowMessageActions = !!Settings.capa(Enums.Capa.MessageActions);
|
||||
this.allowMessageListActions = !!Settings.capa(Enums.Capa.MessageListActions);
|
||||
|
||||
this.logoImg = Utils.trim(Settings.settingsGet('UserLogoMessage'));
|
||||
|
||||
this.attachmentsActions = AppStore.attachmentsActions;
|
||||
|
||||
this.message = MessageStore.message;
|
||||
|
|
@ -92,18 +99,21 @@
|
|||
this.showAttachmnetControls = ko.observable(false);
|
||||
|
||||
this.allowAttachmnetControls = ko.computed(function () {
|
||||
return 0 < this.attachmentsActions().length;
|
||||
return 0 < this.attachmentsActions().length &&
|
||||
Settings.capa(Enums.Capa.AttachmentsActions);
|
||||
}, this);
|
||||
|
||||
this.downloadAsZipAllowed = ko.computed(function () {
|
||||
return -1 < Utils.inArray('zip', this.attachmentsActions());
|
||||
return -1 < Utils.inArray('zip', this.attachmentsActions()) &&
|
||||
this.allowAttachmnetControls();
|
||||
}, this);
|
||||
|
||||
this.downloadAsZipLoading = ko.observable(false);
|
||||
this.downloadAsZipError = ko.observable(false).extend({'falseTimeout': 7000});
|
||||
|
||||
this.saveToOwnCloudAllowed = ko.computed(function () {
|
||||
return -1 < Utils.inArray('owncloud', this.attachmentsActions());
|
||||
return -1 < Utils.inArray('owncloud', this.attachmentsActions()) &&
|
||||
this.allowAttachmnetControls();
|
||||
}, this);
|
||||
|
||||
this.saveToOwnCloudLoading = ko.observable(false);
|
||||
|
|
@ -125,7 +135,8 @@
|
|||
}, this);
|
||||
|
||||
this.saveToDropboxAllowed = ko.computed(function () {
|
||||
return -1 < Utils.inArray('dropbox', this.attachmentsActions());
|
||||
return -1 < Utils.inArray('dropbox', this.attachmentsActions()) &&
|
||||
this.allowAttachmnetControls();
|
||||
}, this);
|
||||
|
||||
this.saveToDropboxLoading = ko.observable(false);
|
||||
|
|
@ -214,7 +225,7 @@
|
|||
|
||||
this.deleteCommand = Utils.createCommand(this, function () {
|
||||
var oMessage = this.message();
|
||||
if (oMessage)
|
||||
if (oMessage && this.allowMessageListActions)
|
||||
{
|
||||
this.message(null);
|
||||
require('App/User').deleteMessagesFromFolder(Enums.FolderType.Trash,
|
||||
|
|
@ -224,7 +235,7 @@
|
|||
|
||||
this.deleteWithoutMoveCommand = Utils.createCommand(this, function () {
|
||||
var oMessage = this.message();
|
||||
if (oMessage)
|
||||
if (oMessage && this.allowMessageListActions)
|
||||
{
|
||||
this.message(null);
|
||||
require('App/User').deleteMessagesFromFolder(Enums.FolderType.Trash,
|
||||
|
|
@ -234,7 +245,7 @@
|
|||
|
||||
this.archiveCommand = Utils.createCommand(this, function () {
|
||||
var oMessage = this.message();
|
||||
if (oMessage)
|
||||
if (oMessage && this.allowMessageListActions)
|
||||
{
|
||||
this.message(null);
|
||||
require('App/User').deleteMessagesFromFolder(Enums.FolderType.Archive,
|
||||
|
|
@ -244,7 +255,7 @@
|
|||
|
||||
this.spamCommand = Utils.createCommand(this, function () {
|
||||
var oMessage = this.message();
|
||||
if (oMessage)
|
||||
if (oMessage && this.allowMessageListActions)
|
||||
{
|
||||
this.message(null);
|
||||
require('App/User').deleteMessagesFromFolder(Enums.FolderType.Spam,
|
||||
|
|
@ -254,7 +265,7 @@
|
|||
|
||||
this.notSpamCommand = Utils.createCommand(this, function () {
|
||||
var oMessage = this.message();
|
||||
if (oMessage)
|
||||
if (oMessage && this.allowMessageListActions)
|
||||
{
|
||||
this.message(null);
|
||||
require('App/User').deleteMessagesFromFolder(Enums.FolderType.NotSpam,
|
||||
|
|
@ -521,7 +532,10 @@
|
|||
*/
|
||||
MessageViewMailBoxUserView.prototype.replyOrforward = function (sType)
|
||||
{
|
||||
kn.showScreenPopup(require('View/Popup/Compose'), [sType, MessageStore.message()]);
|
||||
if (Settings.capa(Enums.Capa.Composer))
|
||||
{
|
||||
kn.showScreenPopup(require('View/Popup/Compose'), [sType, MessageStore.message()]);
|
||||
}
|
||||
};
|
||||
|
||||
MessageViewMailBoxUserView.prototype.checkHeaderHeight = function ()
|
||||
|
|
@ -692,7 +706,8 @@
|
|||
oDom
|
||||
.on('click', 'a', function (oEvent) {
|
||||
// setup maito protocol
|
||||
return !(!!oEvent && 3 !== oEvent['which'] && Utils.mailToHelper($(this).attr('href'), require('View/Popup/Compose')));
|
||||
return !(!!oEvent && 3 !== oEvent['which'] && Utils.mailToHelper($(this).attr('href'),
|
||||
Settings.capa(Enums.Capa.Composer) ? require('View/Popup/Compose') : null));
|
||||
})
|
||||
// .on('mouseover', 'a', _.debounce(function (oEvent) {
|
||||
//
|
||||
|
|
@ -1013,12 +1028,15 @@
|
|||
|
||||
MessageViewMailBoxUserView.prototype.composeClick = function ()
|
||||
{
|
||||
kn.showScreenPopup(require('View/Popup/Compose'));
|
||||
if (Settings.capa(Enums.Capa.Composer))
|
||||
{
|
||||
kn.showScreenPopup(require('View/Popup/Compose'));
|
||||
}
|
||||
};
|
||||
|
||||
MessageViewMailBoxUserView.prototype.editMessage = function ()
|
||||
{
|
||||
if (MessageStore.message())
|
||||
if (Settings.capa(Enums.Capa.Composer) && MessageStore.message())
|
||||
{
|
||||
kn.showScreenPopup(require('View/Popup/Compose'), [Enums.ComposeType.Draft, MessageStore.message()]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -907,7 +907,7 @@ END;
|
|||
public static function GetClearDomainName($sDomain)
|
||||
{
|
||||
$sResultDomain = \preg_replace(
|
||||
'/^(webmail|email|mail|www|imap4|pop3|imap|pop|demo|client|ssl|secure)\./i',
|
||||
'/^(webmail|email|mail|www|imap4|pop3|imap|pop|demo|client|ssl|secure|test|cloud|box|m)\./i',
|
||||
'', $sDomain);
|
||||
|
||||
return false === \strpos($sResultDomain, '.') ? $sDomain : $sResultDomain;
|
||||
|
|
|
|||
|
|
@ -46,6 +46,11 @@ class Config
|
|||
*/
|
||||
public static $MessageListDateFilter = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static $MessageListPermanentFilter = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -22,5 +22,6 @@ class FolderResponseStatus
|
|||
const RECENT = 'RECENT';
|
||||
const UNSEEN = 'UNSEEN';
|
||||
const UIDNEXT = 'UIDNEXT';
|
||||
const HIGHESTMODSEQ = 'HIGHESTMODSEQ';
|
||||
const UIDVALIDITY = 'UIDVALIDITY';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,5 +22,6 @@ class FolderStatus
|
|||
const RECENT = 'RECENT';
|
||||
const UNSEEN = 'UNSEEN';
|
||||
const UIDNEXT = 'UIDNEXT';
|
||||
const HIGHESTMODSEQ = 'HIGHESTMODSEQ';
|
||||
const UIDVALIDITY = 'UIDVALIDITY';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,11 @@ class FolderInformation
|
|||
*/
|
||||
public $Uidnext;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $HighestModSeq;
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*
|
||||
|
|
@ -79,6 +84,7 @@ class FolderInformation
|
|||
|
||||
$this->Unread = null;
|
||||
$this->Uidnext = null;
|
||||
$this->HighestModSeq = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -326,7 +326,24 @@ class ImapClient extends \MailSo\Net\NetClient
|
|||
|
||||
try
|
||||
{
|
||||
$this->SendRequestWithCheck('AUTHENTICATE', array('XOAUTH2', \trim($sXOAuth2Token)));
|
||||
$this->SendRequest('AUTHENTICATE', array('XOAUTH2', \trim($sXOAuth2Token)));
|
||||
$aR = $this->parseResponseWithValidation();
|
||||
|
||||
if (\is_array($aR) && 0 < \count($aR) && isset($aR[\count($aR) - 1]))
|
||||
{
|
||||
$oR = $aR[\count($aR) - 1];
|
||||
if (\MailSo\Imap\Enumerations\ResponseType::CONTINUATION === $oR->ResponseType)
|
||||
{
|
||||
if (!empty($oR->ResponseList[1]) && preg_match('/^[a-zA-Z0-9=+\/]+$/', $oR->ResponseList[1]))
|
||||
{
|
||||
$this->Logger()->Write(\base64_decode($oR->ResponseList[1]),
|
||||
\MailSo\Log\Enumerations\Type::WARNING);
|
||||
}
|
||||
|
||||
$this->sendRaw('');
|
||||
$this->parseResponseWithValidation();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (\MailSo\Imap\Exceptions\NegativeResponseException $oException)
|
||||
{
|
||||
|
|
@ -681,6 +698,7 @@ class ImapClient extends \MailSo\Net\NetClient
|
|||
{
|
||||
$sName = null;
|
||||
$aStatus = array();
|
||||
|
||||
foreach ($oImapResponse->ResponseList[3] as $sArrayItem)
|
||||
{
|
||||
if (null === $sName)
|
||||
|
|
@ -734,17 +752,21 @@ class ImapClient extends \MailSo\Net\NetClient
|
|||
$this->EscapeString($sListPattern)
|
||||
);
|
||||
|
||||
if ($bUseListStatus && $this->IsSupported('LIST-STATUS'))
|
||||
if ($bUseListStatus && !$bIsSubscribeList && $this->IsSupported('LIST-STATUS'))
|
||||
{
|
||||
$aParameters[] = 'RETURN';
|
||||
$aParameters[] = array(
|
||||
'STATUS',
|
||||
array(
|
||||
\MailSo\Imap\Enumerations\FolderStatus::MESSAGES,
|
||||
\MailSo\Imap\Enumerations\FolderStatus::UNSEEN,
|
||||
\MailSo\Imap\Enumerations\FolderStatus::UIDNEXT
|
||||
)
|
||||
$aL = array(
|
||||
\MailSo\Imap\Enumerations\FolderStatus::MESSAGES,
|
||||
\MailSo\Imap\Enumerations\FolderStatus::UNSEEN,
|
||||
\MailSo\Imap\Enumerations\FolderStatus::UIDNEXT
|
||||
);
|
||||
|
||||
// if ($this->IsSupported('CONDSTORE'))
|
||||
// {
|
||||
// $aL[] = \MailSo\Imap\Enumerations\FolderStatus::HIGHESTMODSEQ;
|
||||
// }
|
||||
|
||||
$aParameters[] = 'RETURN';
|
||||
$aParameters[] = array('STATUS', $aL);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -846,6 +868,12 @@ class ImapClient extends \MailSo\Net\NetClient
|
|||
{
|
||||
$oResult->Uidnext = $oImapResponse->OptionalResponse[1];
|
||||
}
|
||||
else if ('HIGHESTMODSEQ' === $oImapResponse->OptionalResponse[0] &&
|
||||
isset($oImapResponse->OptionalResponse[1]) &&
|
||||
\is_numeric($oImapResponse->OptionalResponse[1]))
|
||||
{
|
||||
$oResult->HighestModSeq = \trim($oImapResponse->OptionalResponse[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (\count($oImapResponse->ResponseList) > 2 &&
|
||||
|
|
|
|||
|
|
@ -684,16 +684,25 @@ class MailClient
|
|||
* @param int $iCount
|
||||
* @param int $iUnseenCount
|
||||
* @param string $sUidNext
|
||||
* @param string $sHighestModSeq
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function initFolderValues($sFolderName, &$iCount, &$iUnseenCount, &$sUidNext)
|
||||
protected function initFolderValues($sFolderName, &$iCount, &$iUnseenCount,
|
||||
&$sUidNext, &$sHighestModSeq = '')
|
||||
{
|
||||
$aFolderStatus = $this->oImapClient->FolderStatus($sFolderName, array(
|
||||
$aTypes = array(
|
||||
\MailSo\Imap\Enumerations\FolderResponseStatus::MESSAGES,
|
||||
\MailSo\Imap\Enumerations\FolderResponseStatus::UNSEEN,
|
||||
\MailSo\Imap\Enumerations\FolderResponseStatus::UIDNEXT
|
||||
));
|
||||
);
|
||||
|
||||
if ($this->oImapClient->IsSupported('CONDSTORE'))
|
||||
{
|
||||
$aTypes[] = \MailSo\Imap\Enumerations\FolderResponseStatus::HIGHESTMODSEQ;
|
||||
}
|
||||
|
||||
$aFolderStatus = $this->oImapClient->FolderStatus($sFolderName, $aTypes);
|
||||
|
||||
$iCount = isset($aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::MESSAGES])
|
||||
? (int) $aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::MESSAGES] : 0;
|
||||
|
|
@ -704,6 +713,9 @@ class MailClient
|
|||
$sUidNext = isset($aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::UIDNEXT])
|
||||
? (string) $aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::UIDNEXT] : '0';
|
||||
|
||||
$sHighestModSeq = isset($aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::HIGHESTMODSEQ])
|
||||
? (string) $aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::HIGHESTMODSEQ] : '';
|
||||
|
||||
if ($this->IsGmail())
|
||||
{
|
||||
$oFolder = $this->oImapClient->FolderCurrentInformation();
|
||||
|
|
@ -735,14 +747,15 @@ class MailClient
|
|||
* @param int $iCount
|
||||
* @param int $iUnseenCount
|
||||
* @param string $sUidNext
|
||||
* @param string $sHighestModSeq = ''
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GenerateFolderHash($sFolder, $iCount, $iUnseenCount, $sUidNext)
|
||||
public function GenerateFolderHash($sFolder, $iCount, $iUnseenCount, $sUidNext, $sHighestModSeq = '')
|
||||
{
|
||||
$iUnseenCount = 0; // unneccessery
|
||||
return \md5('FolderHash/'.$sFolder.'-'.$iCount.'-'.$iUnseenCount.'-'.$sUidNext.'-'.
|
||||
$this->GenerateImapClientHash()
|
||||
$sHighestModSeq.'-'.$this->GenerateImapClientHash()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -866,16 +879,18 @@ class MailClient
|
|||
$iCount = 0;
|
||||
$iUnseenCount = 0;
|
||||
$sUidNext = '0';
|
||||
$sHighestModSeq = '';
|
||||
|
||||
$this->initFolderValues($sFolderName, $iCount, $iUnseenCount, $sUidNext);
|
||||
$this->initFolderValues($sFolderName, $iCount, $iUnseenCount, $sUidNext, $sHighestModSeq);
|
||||
|
||||
$aResult = array(
|
||||
'Folder' => $sFolderName,
|
||||
'Hash' => $this->GenerateFolderHash($sFolderName, $iCount, $iUnseenCount, $sUidNext),
|
||||
'Hash' => $this->GenerateFolderHash($sFolderName, $iCount, $iUnseenCount, $sUidNext, $sHighestModSeq),
|
||||
'MessageCount' => $iCount,
|
||||
'MessageUnseenCount' => $iUnseenCount,
|
||||
'UidNext' => $sUidNext,
|
||||
'Flags' => $aFlags,
|
||||
'HighestModSeq' => $sHighestModSeq,
|
||||
'NewMessages' => 'INBOX' === $sFolderName ?
|
||||
$this->getFolderNextMessageInformation($sFolderName, $sPrevUidNext, $sUidNext) : array()
|
||||
);
|
||||
|
|
@ -897,10 +912,11 @@ class MailClient
|
|||
$iCount = 0;
|
||||
$iUnseenCount = 0;
|
||||
$sUidNext = '0';
|
||||
$sHighestModSeq = '';
|
||||
|
||||
$this->initFolderValues($sFolderName, $iCount, $iUnseenCount, $sUidNext);
|
||||
$this->initFolderValues($sFolderName, $iCount, $iUnseenCount, $sUidNext, $sHighestModSeq);
|
||||
|
||||
return $this->GenerateFolderHash($sFolderName, $iCount, $iUnseenCount, $sUidNext);
|
||||
return $this->GenerateFolderHash($sFolderName, $iCount, $iUnseenCount, $sUidNext, $sHighestModSeq);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1124,12 +1140,13 @@ class MailClient
|
|||
|
||||
/**
|
||||
* @param string $sSearch
|
||||
* @param string $sFilter
|
||||
* @param int $iTimeZoneOffset = 0
|
||||
* @param bool $bUseCache = true
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getImapSearchCriterias($sSearch, $iTimeZoneOffset = 0, &$bUseCache = true)
|
||||
private function getImapSearchCriterias($sSearch, $sFilter, $iTimeZoneOffset = 0, &$bUseCache = true)
|
||||
{
|
||||
$bUseCache = true;
|
||||
$iTimeFilter = 0;
|
||||
|
|
@ -1370,14 +1387,25 @@ class MailClient
|
|||
$sCriteriasResult .= ' SINCE '.\gmdate('j-M-Y', $iTimeFilter);
|
||||
}
|
||||
|
||||
$sCriteriasResult = \trim($sCriteriasResult);
|
||||
$sCriteriasResult .= ' NOT DELETED';
|
||||
|
||||
$sFilter = \trim($sFilter);
|
||||
if ('' !== $sFilter)
|
||||
{
|
||||
$sCriteriasResult .= ' '.$sFilter;
|
||||
}
|
||||
|
||||
$sCriteriasResult = \trim($sCriteriasResult);
|
||||
if ('' !== \MailSo\Config::$MessageListPermanentFilter)
|
||||
{
|
||||
$sCriteriasResult .= ' '.\MailSo\Config::$MessageListPermanentFilter;
|
||||
}
|
||||
|
||||
$sCriteriasResult = \trim($sCriteriasResult);
|
||||
if ('' === $sCriteriasResult)
|
||||
{
|
||||
$sCriteriasResult = 'NOT DELETED'; // ALL
|
||||
}
|
||||
else
|
||||
{
|
||||
$sCriteriasResult .= ' NOT DELETED';
|
||||
$sCriteriasResult = 'ALL';
|
||||
}
|
||||
|
||||
return $sCriteriasResult;
|
||||
|
|
@ -1704,190 +1732,6 @@ class MailClient
|
|||
$this->oImapClient->IsSupported('THREAD=ORDEREDSUBJECT');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sSearch
|
||||
* @param string $sFolderName
|
||||
* @param string|bool $sFolderHash
|
||||
* @param bool $bUseSortIfSupported = true
|
||||
* @param bool $bUseESearchOrESortRequest = false
|
||||
* @param \MailSo\Cache\CacheClient|null $oCacher = null
|
||||
*
|
||||
* @return Array|bool
|
||||
*/
|
||||
private function getSearchUidsResult($sSearch, $sFolderName, $sFolderHash,
|
||||
$bUseSortIfSupported = true, $bUseESearchOrESortRequest = false, $oCacher = null)
|
||||
{
|
||||
$aResultUids = false;
|
||||
$bUidsFromCacher = false;
|
||||
$bUseCacheAfterSearch = true;
|
||||
$sSerializedHash = '';
|
||||
|
||||
$bESortSupported = $bUseSortIfSupported && $bUseESearchOrESortRequest ? $this->oImapClient->IsSupported('ESORT') : false;
|
||||
$bESearchSupported = $bUseESearchOrESortRequest ? $this->oImapClient->IsSupported('ESEARCH') : false;
|
||||
$bUseSortIfSupported = $bUseSortIfSupported ? $this->oImapClient->IsSupported('SORT') : false;
|
||||
|
||||
$sSearchCriterias = $this->getImapSearchCriterias($sSearch, 0, $bUseCacheAfterSearch);
|
||||
if ($bUseCacheAfterSearch && $oCacher && $oCacher->IsInited())
|
||||
{
|
||||
$sSerializedHash = 'SearchSortUids/'.
|
||||
($bUseSortIfSupported ? 'S': 'N').'/'.
|
||||
$this->GenerateImapClientHash().'/'.
|
||||
$sFolderName.'/'.$sSearchCriterias;
|
||||
|
||||
$sSerializedLog = '"'.$sFolderName.'" / '.$sSearchCriterias.'';
|
||||
|
||||
$sSerialized = $oCacher->Get($sSerializedHash);
|
||||
if (!empty($sSerialized))
|
||||
{
|
||||
$aSerialized = @\json_decode($sSerialized, true);
|
||||
if (\is_array($aSerialized) && isset($aSerialized['FolderHash'], $aSerialized['Uids']) &&
|
||||
$sFolderHash === $aSerialized['FolderHash'] &&
|
||||
\is_array($aSerialized['Uids'])
|
||||
)
|
||||
{
|
||||
if ($this->oLogger)
|
||||
{
|
||||
$this->oLogger->Write('Get Serialized UIDS from cache ('.$sSerializedLog.') [count:'.\count($aSerialized['Uids']).']');
|
||||
}
|
||||
|
||||
$aResultUids = $aSerialized['Uids'];
|
||||
$bUidsFromCacher = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!\is_array($aResultUids))
|
||||
{
|
||||
if ($bUseSortIfSupported)
|
||||
{
|
||||
if ($bESortSupported)
|
||||
{
|
||||
$aESorthData = $this->oImapClient->MessageSimpleESort(array('ARRIVAL'), $sSearchCriterias, array('ALL'), true, '');
|
||||
if (isset($aESorthData['ALL']))
|
||||
{
|
||||
$aResultUids = \MailSo\Base\Utils::ParseFetchSequence($aESorthData['ALL']);
|
||||
$aResultUids = \array_reverse($aResultUids);
|
||||
}
|
||||
|
||||
unset($aESorthData);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aResultUids = $this->oImapClient->MessageSimpleSort(array('REVERSE ARRIVAL'), $sSearchCriterias, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!\MailSo\Base\Utils::IsAscii($sSearch))
|
||||
{
|
||||
try
|
||||
{
|
||||
if ($bESearchSupported)
|
||||
{
|
||||
$aESearchData = $this->oImapClient->MessageSimpleESearch($sSearchCriterias, array('ALL'), true, '', 'UTF-8');
|
||||
if (isset($aESearchData['ALL']))
|
||||
{
|
||||
$aResultUids = \MailSo\Base\Utils::ParseFetchSequence($aESearchData['ALL']);
|
||||
$aResultUids = \array_reverse($aResultUids);
|
||||
}
|
||||
unset($aESearchData);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aResultUids = $this->oImapClient->MessageSimpleSearch($sSearchCriterias, true, 'UTF-8');
|
||||
}
|
||||
}
|
||||
catch (\MailSo\Imap\Exceptions\NegativeResponseException $oException)
|
||||
{
|
||||
$oException = null;
|
||||
$aResultUids = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (false === $aResultUids)
|
||||
{
|
||||
if ($bESearchSupported)
|
||||
{
|
||||
$aESearchData = $this->oImapClient->MessageSimpleESearch($sSearchCriterias, array('ALL'), true);
|
||||
if (isset($aESearchData['ALL']))
|
||||
{
|
||||
$aResultUids = \MailSo\Base\Utils::ParseFetchSequence($aESearchData['ALL']);
|
||||
$aResultUids = \array_reverse($aResultUids);
|
||||
}
|
||||
|
||||
unset($aESearchData);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aResultUids = $this->oImapClient->MessageSimpleSearch($sSearchCriterias, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bUidsFromCacher && $bUseCacheAfterSearch && \is_array($aResultUids) && $oCacher && $oCacher->IsInited() && 0 < \strlen($sSerializedHash))
|
||||
{
|
||||
$oCacher->Set($sSerializedHash, @\json_encode(array(
|
||||
'FolderHash' => $sFolderHash,
|
||||
'Uids' => $aResultUids
|
||||
)));
|
||||
|
||||
if ($this->oLogger)
|
||||
{
|
||||
$this->oLogger->Write('Save Serialized UIDS to cache ('.$sSerializedLog.') [count:'.\count($aResultUids).']');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aResultUids;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sFolderName
|
||||
* @param string $sFolderHash
|
||||
* @param string $sUid
|
||||
* @param \MailSo\Cache\CacheClient|null $oCacher = null
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function MessageThreadUidsFromCache($sFolderName, $sFolderHash, $sUid, $oCacher = null)
|
||||
{
|
||||
$aResult = array();
|
||||
if (0 < \strlen($sFolderName) && 0 < \strlen($sFolderHash) && 0 < \strlen($sUid) && $oCacher && $oCacher->IsInited())
|
||||
{
|
||||
$mThreads = $this->MessageListThreadsMap($sFolderName, $sFolderHash, array(), $oCacher, true);
|
||||
|
||||
if (\is_array($mThreads))
|
||||
{
|
||||
$iUid = (int) $sUid;
|
||||
foreach ($mThreads as $iSubUid => $aSubUids)
|
||||
{
|
||||
if ($iUid === $iSubUid)
|
||||
{
|
||||
$aResult = $aSubUids;
|
||||
if (!\is_array($aResult))
|
||||
{
|
||||
$aResult = array();
|
||||
}
|
||||
|
||||
\array_unshift($aResult, $iSubUid);
|
||||
break;
|
||||
}
|
||||
else if (\is_array($aSubUids))
|
||||
{
|
||||
if (\in_array($iUid, $aSubUids))
|
||||
{
|
||||
$aResult = $aSubUids;
|
||||
\array_unshift($aResult, $iSubUid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return \array_map('trim', $aResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sFolderName
|
||||
* @param array $aUids
|
||||
|
|
@ -1918,6 +1762,7 @@ class MailClient
|
|||
/**
|
||||
* @param \MailSo\Cache\CacheClient|null $oCacher
|
||||
* @param string $sSearch
|
||||
* @param string $sFilter
|
||||
* @param string $sFolderName
|
||||
* @param string $sFolderHash
|
||||
* @param bool $bUseSortIfSupported = false
|
||||
|
|
@ -1928,7 +1773,7 @@ class MailClient
|
|||
* @throws \MailSo\Net\Exceptions\Exception
|
||||
* @throws \MailSo\Imap\Exceptions\Exception
|
||||
*/
|
||||
public function GetUids($oCacher, $sSearch, $sFolderName, $sFolderHash, $bUseSortIfSupported = false)
|
||||
public function GetUids($oCacher, $sSearch, $sFilter, $sFolderName, $sFolderHash, $bUseSortIfSupported = false)
|
||||
{
|
||||
$aResultUids = false;
|
||||
$bUidsFromCacher = false;
|
||||
|
|
@ -1944,7 +1789,7 @@ class MailClient
|
|||
$bUseSortIfSupported = false;
|
||||
}
|
||||
|
||||
$sSearchCriterias = $this->getImapSearchCriterias($sSearch, 0, $bUseCacheAfterSearch);
|
||||
$sSearchCriterias = $this->getImapSearchCriterias($sSearch, $sFilter, 0, $bUseCacheAfterSearch);
|
||||
if ($bUseCacheAfterSearch && $oCacher && $oCacher->IsInited())
|
||||
{
|
||||
$sSerializedHash = 'GetUids/'.
|
||||
|
|
@ -2009,6 +1854,7 @@ class MailClient
|
|||
* @param bool $bUseThreadSortIfSupported = false
|
||||
* @param bool $bUseESearchOrESortRequest = false
|
||||
* @param string $sThreadUid = ''
|
||||
* @param string $sFilter = ''
|
||||
*
|
||||
* @return \MailSo\Mail\MessageCollection
|
||||
*
|
||||
|
|
@ -2017,8 +1863,9 @@ class MailClient
|
|||
* @throws \MailSo\Imap\Exceptions\Exception
|
||||
*/
|
||||
public function MessageList($sFolderName, $iOffset = 0, $iLimit = 10, $sSearch = '', $sPrevUidNext = '', $oCacher = null,
|
||||
$bUseSortIfSupported = false, $bUseThreadSortIfSupported = false, $sThreadUid = '')
|
||||
$bUseSortIfSupported = false, $bUseThreadSortIfSupported = false, $sThreadUid = '', $sFilter = '')
|
||||
{
|
||||
$sFilter = \trim($sFilter);
|
||||
$sSearch = \trim($sSearch);
|
||||
if (!\MailSo\Base\Validator::RangeInt($iOffset, 0) ||
|
||||
!\MailSo\Base\Validator::RangeInt($iLimit, 0, 999))
|
||||
|
|
@ -2026,6 +1873,8 @@ class MailClient
|
|||
throw new \MailSo\Base\Exceptions\InvalidArgumentException();
|
||||
}
|
||||
|
||||
$bUseFilter = '' !== $sFilter;
|
||||
|
||||
$this->oImapClient->FolderSelect($sFolderName);
|
||||
|
||||
$oMessageCollection = MessageCollection::NewInstance();
|
||||
|
|
@ -2044,6 +1893,7 @@ class MailClient
|
|||
$iMessageRealCount = 0;
|
||||
$iMessageUnseenCount = 0;
|
||||
$sUidNext = '0';
|
||||
$sHighestModSeq = '';
|
||||
|
||||
$bUseSortIfSupported = $bUseSortIfSupported ? $this->oImapClient->IsSupported('SORT') : false;
|
||||
|
||||
|
|
@ -2060,9 +1910,16 @@ class MailClient
|
|||
$oCacher = null;
|
||||
}
|
||||
|
||||
$this->initFolderValues($sFolderName, $iMessageRealCount, $iMessageUnseenCount, $sUidNext);
|
||||
$this->initFolderValues($sFolderName, $iMessageRealCount, $iMessageUnseenCount, $sUidNext, $sHighestModSeq);
|
||||
|
||||
if ($bUseFilter)
|
||||
{
|
||||
$iMessageUnseenCount = 0;
|
||||
}
|
||||
|
||||
$oMessageCollection->FolderHash = $this->GenerateFolderHash(
|
||||
$sFolderName, $iMessageRealCount, $iMessageUnseenCount, $sUidNext, $sHighestModSeq);
|
||||
|
||||
$oMessageCollection->FolderHash = $this->GenerateFolderHash($sFolderName, $iMessageRealCount, $iMessageUnseenCount, $sUidNext);
|
||||
$oMessageCollection->UidNext = $sUidNext;
|
||||
|
||||
if (empty($sThreadUid) && 0 < \strlen($sPrevUidNext) && 'INBOX' === $sFolderName)
|
||||
|
|
@ -2083,7 +1940,7 @@ class MailClient
|
|||
|
||||
if (0 < $iMessageRealCount)
|
||||
{
|
||||
$mAllSortedUids = $this->GetUids($oCacher, '',
|
||||
$mAllSortedUids = $this->GetUids($oCacher, '', $sFilter,
|
||||
$oMessageCollection->FolderName, $oMessageCollection->FolderHash, $bUseSortIfSupported);
|
||||
|
||||
$mAllThreads = $bUseThreadSortIfSupported ? $this->MessageListThreadsMap(
|
||||
|
|
@ -2132,7 +1989,7 @@ class MailClient
|
|||
|
||||
if (0 < \strlen($sSearch) && \is_array($aUids))
|
||||
{
|
||||
$aSearchedUids = $this->GetUids($oCacher, $sSearch, $oMessageCollection->FolderName, $oMessageCollection->FolderHash);
|
||||
$aSearchedUids = $this->GetUids($oCacher, $sSearch, $sFilter, $oMessageCollection->FolderName, $oMessageCollection->FolderHash);
|
||||
if (\is_array($aSearchedUids) && 0 < \count($aSearchedUids))
|
||||
{
|
||||
$aFlippedSearchedUids = \array_flip($aSearchedUids);
|
||||
|
|
@ -2678,7 +2535,7 @@ class MailClient
|
|||
$this->oImapClient->FolderSelect($sFolderFullNameRaw);
|
||||
|
||||
$oFolderInformation = $this->oImapClient->FolderCurrentInformation();
|
||||
if ($oFolderInformation && 0 < $oFolderInformation->Exists) // STATUS?
|
||||
if ($oFolderInformation && $oFolderInformation->Exists && 0 < $oFolderInformation->Exists) // STATUS?
|
||||
{
|
||||
$this->oImapClient->MessageStoreFlag('1:*', false,
|
||||
array(\MailSo\Imap\Enumerations\MessageFlag::DELETED),
|
||||
|
|
|
|||
|
|
@ -893,9 +893,16 @@ class Actions
|
|||
{
|
||||
if (null === $this->oAddressBookProvider)
|
||||
{
|
||||
$this->oAddressBookProvider = new \RainLoop\Providers\AddressBook(
|
||||
$this->Config()->Get('contacts', 'enable', false) || $bForceEnable ? $this->fabrica('address-book', $oAccount) : null);
|
||||
$oDriver = null;
|
||||
if ($this->GetCapa(false, \RainLoop\Enumerations\Capa::CONTACTS, $oAccount))
|
||||
{
|
||||
if ($this->Config()->Get('contacts', 'enable', false) || $bForceEnable)
|
||||
{
|
||||
$oDriver = $this->fabrica('address-book', $oAccount);
|
||||
}
|
||||
}
|
||||
|
||||
$this->oAddressBookProvider = new \RainLoop\Providers\AddressBook($oDriver);
|
||||
$this->oAddressBookProvider->SetLogger($this->Logger());
|
||||
}
|
||||
|
||||
|
|
@ -1078,7 +1085,7 @@ class Actions
|
|||
$this->oLoggerAuth->AddForbiddenType(\MailSo\Log\Enumerations\Type::TIME_DELTA);
|
||||
|
||||
$oDriver = \MailSo\Log\Drivers\File::NewInstance($sAuthLogFileFullPath);
|
||||
|
||||
|
||||
$oDriver->DisableTimePrefix();
|
||||
$oDriver->DisableGuidPrefix();
|
||||
$oDriver->DisableTypedPrefix();
|
||||
|
|
@ -1344,6 +1351,7 @@ class Actions
|
|||
'LoginCss' => '',
|
||||
'UserLogo' => '',
|
||||
'UserLogoTitle' => '',
|
||||
'UserLogoMessage' => '',
|
||||
'UserCss' => '',
|
||||
'WelcomePageUrl' => '',
|
||||
'WelcomePageDisplay' => 'none',
|
||||
|
|
@ -1357,7 +1365,6 @@ class Actions
|
|||
'AllowCtrlEnterOnCompose' => (bool) $oConfig->Get('labs', 'allow_ctrl_enter_on_compose', false),
|
||||
'UseRsaEncryption' => (bool) $oConfig->Get('security', 'use_rsa_encryption', false),
|
||||
'RsaPublicKey' => '',
|
||||
'HideDangerousActions' => $oConfig->Get('labs', 'hide_dangerous_actions', false),
|
||||
'CustomLoginLink' => $oConfig->Get('labs', 'custom_login_link', ''),
|
||||
'CustomLogoutLink' => $oConfig->Get('labs', 'custom_logout_link', ''),
|
||||
'LoginDefaultDomain' => $oConfig->Get('login', 'default_domain', ''),
|
||||
|
|
@ -1383,7 +1390,7 @@ class Actions
|
|||
'Plugins' => array()
|
||||
);
|
||||
|
||||
if ($oConfig->Get('capa', 'attachments_actions', false))
|
||||
if ($this->GetCapa(false, \RainLoop\Enumerations\Capa::ATTACHMENTS_ACTIONS))
|
||||
{
|
||||
if (!!\class_exists('ZipArchive'))
|
||||
{
|
||||
|
|
@ -1404,7 +1411,6 @@ class Actions
|
|||
$aResult['AllowDropboxSocial'] = (bool) $oConfig->Get('social', 'dropbox_enable', false);
|
||||
$aResult['DropboxApiKey'] = \trim($oConfig->Get('social', 'dropbox_api_key', ''));
|
||||
|
||||
|
||||
if ($aResult['UseRsaEncryption'] &&
|
||||
\file_exists(APP_PRIVATE_DATA.'rsa/public') && \file_exists(APP_PRIVATE_DATA.'rsa/private'))
|
||||
{
|
||||
|
|
@ -1417,7 +1423,7 @@ class Actions
|
|||
}
|
||||
}
|
||||
|
||||
if (0 === strlen($aResult['RsaPublicKey']))
|
||||
if (0 === \strlen($aResult['RsaPublicKey']))
|
||||
{
|
||||
$aResult['UseRsaEncryption'] = false;
|
||||
}
|
||||
|
|
@ -2749,7 +2755,7 @@ class Actions
|
|||
*/
|
||||
public function DoAttachmentsActions()
|
||||
{
|
||||
if (!$this->Config()->Get('capa', 'attachments_actions', false))
|
||||
if (!$this->GetCapa(false, \RainLoop\Enumerations\Capa::ATTACHMENTS_ACTIONS))
|
||||
{
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
|
@ -3491,7 +3497,7 @@ class Actions
|
|||
$this->setConfigFromParams($oConfig, $sParamName, 'webmail', 'allow_additional_identities', 'bool');
|
||||
break;
|
||||
case \RainLoop\Enumerations\Capa::TEMPLATES:
|
||||
$this->setConfigFromParams($oConfig, $sParamName, 'capa', 'templates', 'bool');
|
||||
$this->setConfigFromParams($oConfig, $sParamName, 'capa', 'x-templates', 'bool');
|
||||
break;
|
||||
case \RainLoop\Enumerations\Capa::TWO_FACTOR:
|
||||
$this->setConfigFromParams($oConfig, $sParamName, 'security', 'allow_two_factor_auth', 'bool');
|
||||
|
|
@ -5126,9 +5132,6 @@ class Actions
|
|||
*/
|
||||
public function DoFolders()
|
||||
{
|
||||
// \sleep(1);
|
||||
// throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::MailServerError);
|
||||
|
||||
$oAccount = $this->initMailClientConnection();
|
||||
|
||||
$oFolderCollection = null;
|
||||
|
|
@ -5136,7 +5139,8 @@ class Actions
|
|||
|
||||
if (null === $oFolderCollection)
|
||||
{
|
||||
$oFolderCollection = $this->MailClient()->Folders('', '*',
|
||||
$oFolderCollection = $this->MailClient()->Folders('',
|
||||
$this->GetCapa(false, \RainLoop\Enumerations\Capa::FOLDERS, $oAccount) ? '*' : 'INBOX',
|
||||
!!$this->Config()->Get('labs', 'use_imap_list_subscribe', true),
|
||||
(int) $this->Config()->Get('labs', 'imap_folder_list_limit', 200)
|
||||
);
|
||||
|
|
@ -5152,7 +5156,8 @@ class Actions
|
|||
$this->recFoldersTypes($oAccount, $oFolderCollection, $aSystemFolders);
|
||||
$oFolderCollection->SystemFolders = $aSystemFolders;
|
||||
|
||||
if ($this->Config()->Get('labs', 'autocreate_system_folders', true))
|
||||
if ($this->GetCapa(false, \RainLoop\Enumerations\Capa::FOLDERS, $oAccount) &&
|
||||
$this->Config()->Get('labs', 'autocreate_system_folders', true))
|
||||
{
|
||||
$bDoItAgain = false;
|
||||
|
||||
|
|
@ -5272,10 +5277,12 @@ class Actions
|
|||
*/
|
||||
public function DoFolderCreate()
|
||||
{
|
||||
// \sleep(1);
|
||||
// throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::CantCreateFolder);
|
||||
$oAccount = $this->initMailClientConnection();
|
||||
|
||||
$this->initMailClientConnection();
|
||||
if (!$this->GetCapa(false, \RainLoop\Enumerations\Capa::FOLDERS, $oAccount))
|
||||
{
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -5298,7 +5305,12 @@ class Actions
|
|||
*/
|
||||
public function DoFolderSubscribe()
|
||||
{
|
||||
$this->initMailClientConnection();
|
||||
$oAccount = $this->initMailClientConnection();
|
||||
|
||||
if (!$this->GetCapa(false, \RainLoop\Enumerations\Capa::FOLDERS, $oAccount))
|
||||
{
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
$sFolderFullNameRaw = $this->GetActionParam('Folder', '');
|
||||
$bSubscribe = '1' === (string) $this->GetActionParam('Subscribe', '0');
|
||||
|
|
@ -5329,6 +5341,11 @@ class Actions
|
|||
{
|
||||
$oAccount = $this->getAccountFromToken();
|
||||
|
||||
if (!$this->GetCapa(false, \RainLoop\Enumerations\Capa::FOLDERS, $oAccount))
|
||||
{
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
$sFolderFullNameRaw = $this->GetActionParam('Folder', '');
|
||||
$bCheckable = '1' === (string) $this->GetActionParam('Checkable', '0');
|
||||
|
||||
|
|
@ -5374,7 +5391,12 @@ class Actions
|
|||
*/
|
||||
public function DoFolderRename()
|
||||
{
|
||||
$this->initMailClientConnection();
|
||||
$oAccount = $this->initMailClientConnection();
|
||||
|
||||
if (!$this->GetCapa(false, \RainLoop\Enumerations\Capa::FOLDERS, $oAccount))
|
||||
{
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
$sPrevFolderFullNameRaw = $this->GetActionParam('Folder', '');
|
||||
$sNewTopFolderNameInUtf = $this->GetActionParam('NewFolderName', '');
|
||||
|
|
@ -5399,10 +5421,12 @@ class Actions
|
|||
*/
|
||||
public function DoFolderDelete()
|
||||
{
|
||||
// \sleep(1);
|
||||
// throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::CantRenameFolder);
|
||||
$oAccount = $this->initMailClientConnection();
|
||||
|
||||
$this->initMailClientConnection();
|
||||
if (!$this->GetCapa(false, \RainLoop\Enumerations\Capa::FOLDERS, $oAccount))
|
||||
{
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
$sFolderFullNameRaw = $this->GetActionParam('Folder', '');
|
||||
|
||||
|
|
@ -5619,7 +5643,8 @@ class Actions
|
|||
$this->cacherForUids(),
|
||||
!!$this->Config()->Get('labs', 'use_imap_sort', false),
|
||||
$bUseThreads,
|
||||
$sThreadUid
|
||||
$sThreadUid,
|
||||
''
|
||||
);
|
||||
}
|
||||
catch (\Exception $oException)
|
||||
|
|
@ -5886,6 +5911,11 @@ class Actions
|
|||
{
|
||||
$oAccount = $this->initMailClientConnection();
|
||||
|
||||
if (!$this->GetCapa(false, \RainLoop\Enumerations\Capa::COMPOSER, $oAccount))
|
||||
{
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
$sMessageFolder = $this->GetActionParam('MessageFolder', '');
|
||||
$sMessageUid = $this->GetActionParam('MessageUid', '');
|
||||
|
||||
|
|
@ -6098,6 +6128,11 @@ class Actions
|
|||
{
|
||||
$oAccount = $this->initMailClientConnection();
|
||||
|
||||
if (!$this->GetCapa(false, \RainLoop\Enumerations\Capa::COMPOSER, $oAccount))
|
||||
{
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
$oConfig = $this->Config();
|
||||
|
||||
$sDraftFolder = $this->GetActionParam('MessageFolder', '');
|
||||
|
|
@ -6283,6 +6318,11 @@ class Actions
|
|||
{
|
||||
$oAccount = $this->initMailClientConnection();
|
||||
|
||||
if (!$this->GetCapa(false, \RainLoop\Enumerations\Capa::COMPOSER, $oAccount))
|
||||
{
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
$oMessage = $this->buildReadReceiptMessage($oAccount);
|
||||
|
||||
$this->Plugins()->RunHook('filter.send-read-receipt-message', array(&$oMessage, $oAccount));
|
||||
|
|
@ -7875,10 +7915,10 @@ class Actions
|
|||
}
|
||||
}
|
||||
|
||||
// if ($oConfig->Get('capa', 'templates', true))
|
||||
// {
|
||||
// $aResult[] = \RainLoop\Enumerations\Capa::TEMPLATES;
|
||||
// }
|
||||
if ($oConfig->Get('capa', 'x-templates', true))
|
||||
{
|
||||
$aResult[] = \RainLoop\Enumerations\Capa::TEMPLATES;
|
||||
}
|
||||
|
||||
if ($oConfig->Get('webmail', 'allow_additional_accounts', false))
|
||||
{
|
||||
|
|
@ -7890,6 +7930,66 @@ class Actions
|
|||
$aResult[] = \RainLoop\Enumerations\Capa::IDENTITIES;
|
||||
}
|
||||
|
||||
if ($oConfig->Get('capa', 'folders', true))
|
||||
{
|
||||
$aResult[] = \RainLoop\Enumerations\Capa::FOLDERS;
|
||||
|
||||
if ($oConfig->Get('capa', 'messagelist_actions', true))
|
||||
{
|
||||
$aResult[] = \RainLoop\Enumerations\Capa::MESSAGELIST_ACTIONS;
|
||||
|
||||
if ($oConfig->Get('capa', 'dangerous_actions', true))
|
||||
{
|
||||
$aResult[] = \RainLoop\Enumerations\Capa::DANGEROUS_ACTIONS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($oConfig->Get('capa', 'reload', true))
|
||||
{
|
||||
$aResult[] = \RainLoop\Enumerations\Capa::RELOAD;
|
||||
}
|
||||
|
||||
if ($oConfig->Get('capa', 'settings', true))
|
||||
{
|
||||
$aResult[] = \RainLoop\Enumerations\Capa::SETTINGS;
|
||||
}
|
||||
|
||||
if ($oConfig->Get('capa', 'help', true))
|
||||
{
|
||||
$aResult[] = \RainLoop\Enumerations\Capa::HELP;
|
||||
}
|
||||
|
||||
if ($oConfig->Get('capa', 'attachments_actions', false))
|
||||
{
|
||||
$aResult[] = \RainLoop\Enumerations\Capa::ATTACHMENTS_ACTIONS;
|
||||
}
|
||||
|
||||
if ($oConfig->Get('capa', 'message_actions', true))
|
||||
{
|
||||
$aResult[] = \RainLoop\Enumerations\Capa::MESSAGE_ACTIONS;
|
||||
}
|
||||
|
||||
if ($oConfig->Get('capa', 'composer', true))
|
||||
{
|
||||
$aResult[] = \RainLoop\Enumerations\Capa::COMPOSER;
|
||||
|
||||
if ($oConfig->Get('capa', 'contacts', true))
|
||||
{
|
||||
$aResult[] = \RainLoop\Enumerations\Capa::CONTACTS;
|
||||
}
|
||||
}
|
||||
|
||||
if ($oConfig->Get('capa', 'search', true))
|
||||
{
|
||||
$aResult[] = \RainLoop\Enumerations\Capa::SEARCH;
|
||||
|
||||
if ($oConfig->Get('capa', 'search_adv', true))
|
||||
{
|
||||
$aResult[] = \RainLoop\Enumerations\Capa::SEARCH_ADV;
|
||||
}
|
||||
}
|
||||
|
||||
if ($oConfig->Get('security', 'allow_two_factor_auth', false) &&
|
||||
($bAdmin || ($oAccount && !$oAccount->IsAdditionalAccount())))
|
||||
{
|
||||
|
|
@ -9219,7 +9319,7 @@ class Actions
|
|||
}
|
||||
else if ('MailSo\Mail\Message' === $sClassName)
|
||||
{
|
||||
$oAccount = call_user_func($fGetAccount);
|
||||
$oAccount = \call_user_func($fGetAccount);
|
||||
|
||||
$mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array(
|
||||
'Folder' => $mResponse->Folder(),
|
||||
|
|
@ -9275,6 +9375,11 @@ class Actions
|
|||
$mResult['IsForwarded'] = 0 < \strlen($sForwardedFlag) && \in_array(\strtolower($sForwardedFlag), $aFlags);
|
||||
$mResult['IsReadReceipt'] = 0 < \strlen($sReadReceiptFlag) && \in_array(\strtolower($sReadReceiptFlag), $aFlags);
|
||||
|
||||
if (!$this->GetCapa(false, \RainLoop\Enumerations\Capa::COMPOSER, $oAccount))
|
||||
{
|
||||
$mResult['IsReadReceipt'] = true;
|
||||
}
|
||||
|
||||
$mResult['TextPartIsTrimmed'] = false;
|
||||
|
||||
if ('Message' === $sParent)
|
||||
|
|
@ -9483,17 +9588,19 @@ class Actions
|
|||
else if ('MailSo\Mail\Folder' === $sClassName)
|
||||
{
|
||||
$aExtended = null;
|
||||
$mStatus = $mResponse->Status();
|
||||
if (\is_array($mStatus) && isset($mStatus['MESSAGES'], $mStatus['UNSEEN'], $mStatus['UIDNEXT']))
|
||||
{
|
||||
$aExtended = array(
|
||||
'MessageCount' => (int) $mStatus['MESSAGES'],
|
||||
'MessageUnseenCount' => (int) $mStatus['UNSEEN'],
|
||||
'UidNext' => (string) $mStatus['UIDNEXT'],
|
||||
'Hash' => $this->MailClient()->GenerateFolderHash(
|
||||
$mResponse->FullNameRaw(), $mStatus['MESSAGES'], $mStatus['UNSEEN'], $mStatus['UIDNEXT'])
|
||||
);
|
||||
}
|
||||
|
||||
// $mStatus = $mResponse->Status();
|
||||
// if (\is_array($mStatus) && isset($mStatus['MESSAGES'], $mStatus['UNSEEN'], $mStatus['UIDNEXT']))
|
||||
// {
|
||||
// $aExtended = array(
|
||||
// 'MessageCount' => (int) $mStatus['MESSAGES'],
|
||||
// 'MessageUnseenCount' => (int) $mStatus['UNSEEN'],
|
||||
// 'UidNext' => (string) $mStatus['UIDNEXT'],
|
||||
// 'Hash' => $this->MailClient()->GenerateFolderHash(
|
||||
// $mResponse->FullNameRaw(), $mStatus['MESSAGES'], $mStatus['UNSEEN'], $mStatus['UIDNEXT'],
|
||||
// empty($mStatus['HIGHESTMODSEQ']) ? '' : $mStatus['HIGHESTMODSEQ'])
|
||||
// );
|
||||
// }
|
||||
|
||||
$aCheckableFolder = \call_user_func($fGetCheckableFolder);
|
||||
if (!\is_array($aCheckableFolder))
|
||||
|
|
|
|||
|
|
@ -91,9 +91,13 @@ class Api
|
|||
\MailSo\Config::$MessageListDateFilter =
|
||||
(int) \RainLoop\Api::Config()->Get('labs', 'imap_message_list_date_filter', 0);
|
||||
|
||||
\MailSo\Config::$MessageListPermanentFilter =
|
||||
\trim(\RainLoop\Api::Config()->Get('labs', 'imap_message_list_permanent_filter', ''));
|
||||
|
||||
\MailSo\Config::$LargeThreadLimit =
|
||||
(int) \RainLoop\Api::Config()->Get('labs', 'imap_large_thread_limit', 50);
|
||||
|
||||
|
||||
\MailSo\Config::$SystemLogger = \RainLoop\Api::Logger();
|
||||
|
||||
$sSslCafile = \RainLoop\Api::Config()->Get('ssl', 'cafile', '');
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ class Application extends \RainLoop\Config\AbstractConfig
|
|||
'login_powered' => array(true),
|
||||
'user_logo' => array(''),
|
||||
'user_logo_title' => array(''),
|
||||
'user_logo_message' => array(''),
|
||||
'user_css' => array(''),
|
||||
'welcome_page_url' => array(''),
|
||||
'welcome_page_display' => array('none')
|
||||
|
|
@ -134,8 +135,19 @@ class Application extends \RainLoop\Config\AbstractConfig
|
|||
),
|
||||
|
||||
'capa' => array(
|
||||
'folders' => array(true),
|
||||
'composer' => array(true),
|
||||
'contacts' => array(true),
|
||||
'settings' => array(true),
|
||||
'help' => array(true),
|
||||
'reload' => array(true),
|
||||
'search' => array(true),
|
||||
'search_adv' => array(true),
|
||||
'filters' => array(true),
|
||||
'templates' => array(true),
|
||||
'x-templates' => array(false),
|
||||
'dangerous_actions' => array(true),
|
||||
'message_actions' => array(true),
|
||||
'messagelist_actions' => array(true),
|
||||
'attachments_actions' => array(true)
|
||||
),
|
||||
|
||||
|
|
@ -299,6 +311,7 @@ Enables caching in the system'),
|
|||
'imap_message_list_fast_simple_search' => array(true),
|
||||
'imap_message_list_count_limit_trigger' => array(0),
|
||||
'imap_message_list_date_filter' => array(0),
|
||||
'imap_message_list_permanent_filter' => array(''),
|
||||
'imap_large_thread_limit' => array(50),
|
||||
'imap_folder_list_limit' => array(200),
|
||||
'imap_show_login_alert' => array(true),
|
||||
|
|
|
|||
|
|
@ -17,6 +17,18 @@ class Capa
|
|||
const ATTACHMENT_THUMBNAILS = 'ATTACHMENT_THUMBNAILS';
|
||||
const ADDITIONAL_ACCOUNTS = 'ADDITIONAL_ACCOUNTS';
|
||||
const IDENTITIES = 'IDENTITIES';
|
||||
const FOLDERS = 'FOLDERS';
|
||||
const COMPOSER = 'COMPOSER';
|
||||
const CONTACTS = 'CONTACTS';
|
||||
const RELOAD = 'RELOAD';
|
||||
const SEARCH = 'SEARCH';
|
||||
const SEARCH_ADV = 'SEARCH_ADV';
|
||||
const SETTINGS = 'SETTINGS';
|
||||
const HELP = 'HELP';
|
||||
const TEMPLATES = 'TEMPLATES';
|
||||
const MESSAGE_ACTIONS = 'MESSAGE_ACTIONS';
|
||||
const MESSAGELIST_ACTIONS = 'MESSAGELIST_ACTIONS';
|
||||
const ATTACHMENTS_ACTIONS = 'ATTACHMENTS_ACTIONS';
|
||||
const DANGEROUS_ACTIONS = 'DANGEROUS_ACTIONS';
|
||||
const AUTOLOGOUT = 'AUTOLOGOUT';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -437,8 +437,18 @@ class Account extends \RainLoop\Account // for backward compatibility
|
|||
}
|
||||
else
|
||||
{
|
||||
$oMailClient->Login($aImapCredentials['Login'], $aImapCredentials['Password'], '',
|
||||
$aImapCredentials['UseAuthPlainIfSupported']);
|
||||
$iGatLen = \strlen(APP_GOOGLE_ACCESS_TOKEN_PREFIX);
|
||||
$sPassword = $aImapCredentials['Password'];
|
||||
if (APP_GOOGLE_ACCESS_TOKEN_PREFIX === \substr($sPassword, 0, $iGatLen))
|
||||
{
|
||||
$oMailClient->LoginWithXOauth2(
|
||||
\base64_encode('user='.$aImapCredentials['Login']."\1".'auth=Bearer '.\substr($sPassword, $iGatLen)."\1\1"));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oMailClient->Login($aImapCredentials['Login'], $aImapCredentials['Password'], '',
|
||||
$aImapCredentials['UseAuthPlainIfSupported']);
|
||||
}
|
||||
}
|
||||
|
||||
$bLogin = true;
|
||||
|
|
@ -495,7 +505,17 @@ class Account extends \RainLoop\Account // for backward compatibility
|
|||
|
||||
if ($aSmtpCredentials['UseAuth'] && !$aSmtpCredentials['UsePhpMail'] && $oSmtpClient)
|
||||
{
|
||||
$oSmtpClient->Login($aSmtpCredentials['Login'], $aSmtpCredentials['Password']);
|
||||
$iGatLen = \strlen(APP_GOOGLE_ACCESS_TOKEN_PREFIX);
|
||||
$sPassword = $aSmtpCredentials['Password'];
|
||||
if (APP_GOOGLE_ACCESS_TOKEN_PREFIX === \substr($sPassword, 0, $iGatLen))
|
||||
{
|
||||
$oSmtpClient->LoginWithXOauth2(
|
||||
\base64_encode('user='.$aSmtpCredentials['Login']."\1".'auth=Bearer '.\substr($sPassword, $iGatLen)."\1\1"));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oSmtpClient->Login($aSmtpCredentials['Login'], $aSmtpCredentials['Password']);
|
||||
}
|
||||
|
||||
$bLogin = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -766,7 +766,8 @@ class ServiceActions
|
|||
*/
|
||||
public function ServiceSocialGoogle()
|
||||
{
|
||||
return $this->oActions->Social()->GooglePopupService();
|
||||
$bXAuth = '1' === (string) $this->oHttp->GetQuery('xauth', '0');
|
||||
return $this->oActions->Social()->GooglePopupService($bXAuth);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ class Social
|
|||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GooglePopupService()
|
||||
public function GooglePopupService($bGmail = false)
|
||||
{
|
||||
$sResult = '';
|
||||
$sLoginUrl = '';
|
||||
|
|
@ -152,23 +152,36 @@ class Social
|
|||
$sState = $this->oHttp->GetQuery('state');
|
||||
if (!empty($sState))
|
||||
{
|
||||
$aParts = explode('|', $sState, 2);
|
||||
$sCheckToken = !empty($aParts[0]) ? $aParts[0] : '';
|
||||
$sCheckAuth = !empty($aParts[1]) ? $aParts[1] : '';
|
||||
$aParts = explode('|', $sState, 3);
|
||||
|
||||
if (!$bGmail)
|
||||
{
|
||||
$bGmail = !empty($aParts[0]) ? '1' === (string) $aParts[0] : false;
|
||||
}
|
||||
|
||||
$sCheckToken = !empty($aParts[1]) ? $aParts[1] : '';
|
||||
$sCheckAuth = !empty($aParts[2]) ? $aParts[2] : '';
|
||||
}
|
||||
|
||||
$sRedirectUrl = $this->oHttp->GetFullUrl().'?SocialGoogle';
|
||||
if (!$this->oHttp->HasQuery('code'))
|
||||
{
|
||||
// https://www.google.com/m8/feeds/
|
||||
$aParams = array(
|
||||
'scope' => 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile',
|
||||
'state' => \RainLoop\Utils::GetConnectionToken().'|'.$this->oActions->GetSpecAuthToken(),
|
||||
// 'access_type' => 'offline',
|
||||
// 'approval_prompt' => 'force',
|
||||
'scope' => \trim(\implode(' ', array(
|
||||
'https://www.googleapis.com/auth/userinfo.email',
|
||||
'https://www.googleapis.com/auth/userinfo.profile',
|
||||
$bGmail ? 'https://mail.google.com/' : ''
|
||||
))),
|
||||
'state' => ($bGmail ? '1' : '0').'|'.\RainLoop\Utils::GetConnectionToken().'|'.$this->oActions->GetSpecAuthToken(),
|
||||
'response_type' => 'code'
|
||||
);
|
||||
|
||||
// if ($bGmail)
|
||||
// {
|
||||
// $aParams['access_type'] = 'offline';
|
||||
// $aParams['approval_prompt'] = 'force';
|
||||
// }
|
||||
|
||||
$sLoginUrl = $oGoogle->getAuthenticationUrl('https://accounts.google.com/o/oauth2/auth', $sRedirectUrl, $aParams);
|
||||
}
|
||||
else if (!empty($sState) && $sCheckToken === \RainLoop\Utils::GetConnectionToken())
|
||||
|
|
@ -192,12 +205,26 @@ class Social
|
|||
{
|
||||
if ($bLogin)
|
||||
{
|
||||
$sUserData = $this->oActions->StorageProvider()->Get(null,
|
||||
\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY,
|
||||
$this->GoogleUserLoginStorageKey($oGoogle, $aUserInfoResponse['result']['id'])
|
||||
);
|
||||
$aUserData = null;
|
||||
if ($bGmail)
|
||||
{
|
||||
if (!empty($aUserInfoResponse['result']['email']))
|
||||
{
|
||||
$aUserData = array(
|
||||
'Email' => $aUserInfoResponse['result']['email'],
|
||||
'Password' => APP_GOOGLE_ACCESS_TOKEN_PREFIX.$aAuthorizationResponse['result']['access_token']
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sUserData = $this->oActions->StorageProvider()->Get(null,
|
||||
\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY,
|
||||
$this->GoogleUserLoginStorageKey($oGoogle, $aUserInfoResponse['result']['id'])
|
||||
);
|
||||
|
||||
$aUserData = \RainLoop\Utils::DecodeKeyValues($sUserData);
|
||||
$aUserData = \RainLoop\Utils::DecodeKeyValues($sUserData);
|
||||
}
|
||||
|
||||
if ($aUserData && \is_array($aUserData) &&
|
||||
!empty($aUserData['Email']) && isset($aUserData['Password']))
|
||||
|
|
@ -210,7 +237,7 @@ class Social
|
|||
}
|
||||
}
|
||||
|
||||
if ($oAccount)
|
||||
if ($oAccount && !$bGmail)
|
||||
{
|
||||
$aUserData = array(
|
||||
'ID' => $aUserInfoResponse['result']['id'],
|
||||
|
|
|
|||
|
|
@ -155,6 +155,21 @@
|
|||
}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" data-i18n="TAB_BRANDING/LABEL_USER_LOGO_MESSAGE"></label>
|
||||
<div class="controls">
|
||||
<div data-bind="component: {
|
||||
name: 'Input',
|
||||
params: {
|
||||
value: userLogoMessage,
|
||||
trigger: userLogoMessage.trigger,
|
||||
placeholder: 'https://',
|
||||
size: 5,
|
||||
enable: capa
|
||||
}
|
||||
}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" data-i18n="TAB_BRANDING/LABEL_USER_CUSTOM_CSS"></label>
|
||||
<div class="controls">
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@
|
|||
'single-root-inbox': foldersListWithSingleInboxRootFolder}">
|
||||
<div class="b-toolbar btn-toolbar">
|
||||
<a class="btn buttonCompose pull-left" data-tooltip-join="top"
|
||||
data-bind="click: composeClick, tooltip: 'FOLDER_LIST/BUTTON_COMPOSE', css: {'btn-warning': composeInEdit}">
|
||||
data-bind="visible: allowComposer, click: composeClick, tooltip: 'FOLDER_LIST/BUTTON_COMPOSE', css: {'btn-warning': composeInEdit}">
|
||||
<i class="icon-paper-plane"></i>
|
||||
</a>
|
||||
<a class="btn buttonContacts pull-left" data-tooltip-join="top" data-bind="visible: allowContacts, click: contactsClick, tooltip: 'FOLDER_LIST/BUTTON_CONTACTS'">
|
||||
<i class="icon-address-book"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="b-content opacity-on-panel-disabled" data-bind="nano: true">
|
||||
<div class="b-content opacity-on-panel-disabled" data-bind="visible: allowFolders, nano: true">
|
||||
<div class="content g-scrollbox">
|
||||
<div class="content-wrapper">
|
||||
<div class="b-folders-system" data-bind="template: { name: 'MailFolderListSystemItem', foreach: folderListSystem }"></div>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="b-content show-on-panel-disabled" data-bind="click: function () { leftPanelDisabled(false); }"></div>
|
||||
<div class="b-footer">
|
||||
<div class="b-footer" data-bind="visible: allowFolders">
|
||||
<nobr>
|
||||
<div class="btn-toolbar">
|
||||
<div class="btn-group hide-on-panel-disabled">
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
data-bind="css: {'message-selected': isMessageSelected, 'message-focused': !messageListFocused(), 'loading': messageListCompleteLoadingThrottle, 'hideMessageListCheckbox': !useCheckboxesInList() }">
|
||||
<div class="toolbar">
|
||||
<div class="btn-toolbar">
|
||||
<div class="btn-group">
|
||||
<div class="btn-group" data-bind="visible: allowReload">
|
||||
<a class="btn single btn-dark-disabled-border buttonReload" data-tooltip-join="top" data-bind="command: reloadCommand, tooltip: 'MESSAGE_LIST/BUTTON_RELOAD'">
|
||||
<i class="icon-spinner" data-bind="css: {'animated': messageListCompleteLoadingThrottleForAnimation}"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="btn-group"> </div>
|
||||
<div class="btn-group dropdown colored-toggle" data-bind="registrateBootstrapDropdown: true, openDropdownTrigger: moveDropdownTrigger">
|
||||
<div class="btn-group" data-bind="visible: allowReload"> </div>
|
||||
<div class="btn-group dropdown colored-toggle" data-bind="visible: allowMessageListActions, registrateBootstrapDropdown: true, openDropdownTrigger: moveDropdownTrigger">
|
||||
<a id="move-dropdown-id" href="#" tabindex="-1" class="btn single btn-dark-disabled-border dropdown-toggle buttonMove" data-toggle="dropdown" data-tooltip-join="top" data-bind="command: moveCommand, tooltip: 'MESSAGE_LIST/BUTTON_MOVE_TO'">
|
||||
<i class="icon-copy visible-on-ctrl-btn"></i>
|
||||
<i class="icon-folder hidden-on-ctrl-btn"></i>
|
||||
|
|
@ -27,8 +27,8 @@
|
|||
<!-- /ko -->
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group"> </div>
|
||||
<div class="btn-group">
|
||||
<div class="btn-group" data-bind="visible: allowMessageListActions"> </div>
|
||||
<div class="btn-group" data-bind="visible: allowMessageListActions">
|
||||
<a class="btn first btn-dark-disabled-border button-archive" data-tooltip-join="top"
|
||||
data-bind="visible: isArchiveVisible, command: archiveCommand, tooltip: 'MESSAGE_LIST/BUTTON_ARCHIVE'">
|
||||
<i class="icon-archive"></i>
|
||||
|
|
@ -50,64 +50,66 @@
|
|||
<!--<span data-bind="text: printableMessageCountForDeletion()"></span>-->
|
||||
</a>
|
||||
</div>
|
||||
<div class="btn-group"> </div>
|
||||
<div class="btn-group dropdown colored-toggle" data-bind="registrateBootstrapDropdown: true, openDropdownTrigger: moreDropdownTrigger">
|
||||
<div class="btn-group" data-bind="visible: allowMessageListActions"> </div>
|
||||
<div class="btn-group dropdown colored-toggle" data-bind="visible: allowComposer || allowMessageListActions || allowDangerousActions, registrateBootstrapDropdown: true, openDropdownTrigger: moreDropdownTrigger">
|
||||
<a id="more-list-dropdown-id" class="btn single btn-dark-disabled-border dropdown-toggle buttonMore" href="#" tabindex="-1" data-toggle="dropdown" data-tooltip-join="top" data-bind="tooltip: 'MESSAGE_LIST/BUTTON_MORE'">
|
||||
<i class="icon-list animate-this-icon-on-open"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu g-ui-menu" role="menu" aria-labelledby="more-list-dropdown-id">
|
||||
<li class="e-item" role="presentation" data-bind="click: listUnsetSeen, css: {'disabled': !hasCheckedOrSelectedLines()}">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||
<i class="icon-none"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE_LIST/MENU_UNSET_SEEN"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="click: listSetSeen, css: {'disabled': !hasCheckedOrSelectedLines()}">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||
<i class="icon-none"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE_LIST/MENU_SET_SEEN"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="click: listSetFlags, css: {'disabled': !hasCheckedOrSelectedLines()}">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||
<i class="icon-none"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE_LIST/MENU_SET_FLAG"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="click: listUnsetFlags, css: {'disabled': !hasCheckedOrSelectedLines()}">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||
<i class="icon-none"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE_LIST/MENU_UNSET_FLAG"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="click: listSetAllSeen, css: {'disabled': !hasMessages()}">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||
<i class="icon-none"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE_LIST/MENU_SET_ALL_SEEN"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider" role="presentation"></li>
|
||||
<li class="e-item" role="presentation" data-bind="command: multyForwardCommand">
|
||||
<div data-bind="visible: allowMessageListActions">
|
||||
<li class="e-item" role="presentation" data-bind="click: listUnsetSeen, css: {'disabled': !hasCheckedOrSelectedLines()}">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||
<i class="icon-none"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE_LIST/MENU_UNSET_SEEN"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="click: listSetSeen, css: {'disabled': !hasCheckedOrSelectedLines()}">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||
<i class="icon-none"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE_LIST/MENU_SET_SEEN"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="click: listSetFlags, css: {'disabled': !hasCheckedOrSelectedLines()}">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||
<i class="icon-none"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE_LIST/MENU_SET_FLAG"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="click: listUnsetFlags, css: {'disabled': !hasCheckedOrSelectedLines()}">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||
<i class="icon-none"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE_LIST/MENU_UNSET_FLAG"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="click: listSetAllSeen, css: {'disabled': !hasMessages()}">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||
<i class="icon-none"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE_LIST/MENU_SET_ALL_SEEN"></span>
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
<li class="divider" role="presentation" data-bind="visible: allowComposer && allowMessageListActions"></li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: allowComposer, command: multyForwardCommand">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||
<i class="icon-reply-all"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE_LIST/BUTTON_MULTY_FORWARD"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider" role="presentation" data-bind="visible: !hideDangerousActions"></li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: !hideDangerousActions, command: deleteWithoutMoveCommand">
|
||||
<li class="divider" role="presentation" data-bind="visible: allowDangerousActions"></li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: allowDangerousActions, command: deleteWithoutMoveCommand">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||
<i class="icon-trash"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE_LIST/BUTTON_DELETE_WITHOUT_MOVE"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: !hideDangerousActions, command: clearCommand">
|
||||
<li class="e-item" role="presentation" data-bind="visible: allowDangerousActions, command: clearCommand">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||
<i class="icon-fire"></i>
|
||||
|
||||
|
|
@ -121,11 +123,11 @@
|
|||
<div class="b-message-list-wrapper">
|
||||
<div class="second-toolbar thm-message-list-top-toolbar">
|
||||
<div class="form-inline">
|
||||
<div class="input-append pull-right">
|
||||
<div class="input-append pull-right" data-bind="visible: allowSearch">
|
||||
<input type="search" class="i18n span4 inputSearch" tabindex="-1"
|
||||
placeholder="Search" autocorrect="off" autocapitalize="off"
|
||||
data-i18n="[placeholder]SEARCH/MAIN_INPUT_PLACEHOLDER" data-bind="value: inputProxyMessageListSearch, onEnter: searchEnterAction, hasfocus: inputMessageListSearchFocus" />
|
||||
<a class="btn buttonMoreSearch" data-bind="click: advancedSearchClick">
|
||||
<a class="btn buttonMoreSearch" data-bind="visible: allowSearchAdv, click: advancedSearchClick">
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -50,7 +50,10 @@
|
|||
</div>
|
||||
|
||||
<div class="b-message-view-desc" data-bind="visible: !message() && '' === messageError() && !hasCheckedMessages()">
|
||||
<span class="i18n" data-i18n="MESSAGE/MESSAGE_VIEW_DESC"></span>
|
||||
<span class="i18n" data-bind="visible: !logoImg" data-i18n="MESSAGE/MESSAGE_VIEW_DESC"></span>
|
||||
<div class="logoPlace" data-bind="if: logoImg">
|
||||
<img style="height: 99%" data-bind="attr: { 'src': logoImg }" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="b-message-view-desc error" data-bind="visible: !message() && '' !== messageError()">
|
||||
|
|
@ -60,124 +63,136 @@
|
|||
<div class="message-fixed-button-toolbar clearfix" data-bind="visible: message">
|
||||
<nobr>
|
||||
<div class="btn-group pull-right">
|
||||
<div class="btn-group pull-right" data-bind="registrateBootstrapDropdown: true">
|
||||
<div class="btn-group pull-right" data-bind="registrateBootstrapDropdown: true,
|
||||
visible: allowComposer || allowMessageListActions || allowMessageActions">
|
||||
|
||||
<a class="btn btn-thin last btn-dark-disabled-border dropdown-toggle buttonMore"
|
||||
id="more-view-dropdown-id" href="#" tabindex="-1"
|
||||
data-toggle="dropdown" data-tooltip-join="bottom"
|
||||
style="margin-left: -1px;"
|
||||
data-bind="command: messageVisibilityCommand, tooltip: 'MESSAGE/BUTTON_MORE'">
|
||||
<span class="caret"></span>
|
||||
data-bind="command: messageVisibilityCommand, tooltip: 'MESSAGE/BUTTON_MORE',
|
||||
css: {'first': !allowComposer}">
|
||||
|
||||
<span data-bind="visible: !allowComposer" class="icon-ellipsis-alt"></span>
|
||||
<span data-bind="visible: allowComposer" class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu g-ui-menu" role="menu" aria-labelledby="more-view-dropdown-id">
|
||||
<li class="e-item" role="presentation"
|
||||
data-bind="visible: 'reply' !== lastReplyAction() && !isDraftFolder()">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="command: replyCommand">
|
||||
<i class="icon-reply"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_REPLY"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation"
|
||||
data-bind="visible: 'replyall' !== lastReplyAction() && !isDraftFolder()">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="command: replyAllCommand">
|
||||
<i class="icon-reply-all"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_REPLY_ALL"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation"
|
||||
data-bind="visible: 'forward' !== lastReplyAction() && !isDraftFolder()">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="command: forwardCommand">
|
||||
<i class="icon-forward"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_FORWARD"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: !isDraftFolder()">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="command: editAsNewCommand">
|
||||
<i class="icon-pencil"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_EDIT_AS_NEW"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: !isDraftFolder()">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="command: forwardAsAttachmentCommand">
|
||||
<i class="icon-forward"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_FORWARD_AS_ATTACHMENT"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider" role="presentation" data-bind="visible: !isDraftFolder()"></li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: usePreviewPane() && !isDraftFolder() && !isArchiveFolder() && !isArchiveDisabled()">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="command: archiveCommand">
|
||||
<i class="icon-archive"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_ARCHIVE"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: usePreviewPane() && !isDraftFolder() && !isSentFolder() && !isSpamFolder() && !isSpamDisabled()">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="command: spamCommand">
|
||||
<i class="icon-angry-smiley"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_SPAM"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: usePreviewPane() && !isDraftFolder() && !isSentFolder() && isSpamFolder() && !isSpamDisabled()">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="command: notSpamCommand">
|
||||
<i class="icon-happy-smiley"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_NOT_SPAM"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: usePreviewPane()">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="command: deleteCommand">
|
||||
<i class="icon-trash"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_DELETE"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider" role="presentation" data-bind="visible: usePreviewPane()"></li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="click: function () { if (message()) { message().printMessage(); }} ">
|
||||
<i class="icon-print"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/MENU_PRINT"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="click: function () { if (message()) { message().viewPopupMessage(); }}">
|
||||
<i class="icon-popup"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_IN_NEW_WINDOW"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider" role="presentation"></li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="link: viewViewLink()">
|
||||
<i class="icon-file-code"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/MENU_VIEW_ORIGINAL"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="link: viewDownloadLink()">
|
||||
<i class="icon-download"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/MENU_DOWNLOAD_ORIGINAL"></span>
|
||||
</a>
|
||||
</li>
|
||||
<div data-bind="visible: allowComposer && !isDraftFolder()">
|
||||
<li class="e-item" role="presentation"
|
||||
data-bind="visible: 'reply' !== lastReplyAction()">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="command: replyCommand">
|
||||
<i class="icon-reply"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_REPLY"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation"
|
||||
data-bind="visible: 'replyall' !== lastReplyAction()">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="command: replyAllCommand">
|
||||
<i class="icon-reply-all"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_REPLY_ALL"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation"
|
||||
data-bind="visible: 'forward' !== lastReplyAction()">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="command: forwardCommand">
|
||||
<i class="icon-forward"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_FORWARD"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="command: editAsNewCommand">
|
||||
<i class="icon-pencil"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_EDIT_AS_NEW"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="command: forwardAsAttachmentCommand">
|
||||
<i class="icon-forward"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_FORWARD_AS_ATTACHMENT"></span>
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
<div data-bind="visible: allowMessageListActions && usePreviewPane()">
|
||||
<li class="divider" role="presentation" data-bind="visible: allowComposer && !isDraftFolder()"></li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: !isDraftFolder() && !isArchiveFolder() && !isArchiveDisabled()">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="command: archiveCommand">
|
||||
<i class="icon-archive"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_ARCHIVE"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: !isDraftFolder() && !isSentFolder() && !isSpamFolder() && !isSpamDisabled()">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="command: spamCommand">
|
||||
<i class="icon-angry-smiley"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_SPAM"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: !isDraftFolder() && !isSentFolder() && isSpamFolder() && !isSpamDisabled()">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="command: notSpamCommand">
|
||||
<i class="icon-happy-smiley"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_NOT_SPAM"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="command: deleteCommand">
|
||||
<i class="icon-trash"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_DELETE"></span>
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
<div data-bind="visible: allowMessageActions">
|
||||
<li class="divider" role="presentation"
|
||||
data-bind="visible: (allowComposer && !isDraftFolder()) || (allowMessageListActions && usePreviewPane())"></li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="click: function () { if (message()) { message().printMessage(); }} ">
|
||||
<i class="icon-print"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/MENU_PRINT"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="click: function () { if (message()) { message().viewPopupMessage(); }}">
|
||||
<i class="icon-popup"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/BUTTON_IN_NEW_WINDOW"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider" role="presentation"></li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="link: viewViewLink()">
|
||||
<i class="icon-file-code"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/MENU_VIEW_ORIGINAL"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="link: viewDownloadLink()">
|
||||
<i class="icon-download"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE/MENU_DOWNLOAD_ORIGINAL"></span>
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
<a class="btn first btn-dark-disabled-border buttonReply pull-right" data-tooltip-join="bottom"
|
||||
data-bind="visible: 'reply' === lastReplyAction(), command: replyCommand, tooltip: 'MESSAGE/BUTTON_REPLY'">
|
||||
data-bind="visible: 'reply' === lastReplyAction() && allowComposer, command: replyCommand, tooltip: 'MESSAGE/BUTTON_REPLY'">
|
||||
<i class="icon-reply"></i>
|
||||
</a>
|
||||
<a class="btn first btn-dark-disabled-border buttonReplyAll pull-right" data-tooltip-join="bottom"
|
||||
data-bind="visible: 'replyall' === lastReplyAction(), command: replyAllCommand, tooltip: 'MESSAGE/BUTTON_REPLY_ALL'">
|
||||
data-bind="visible: 'replyall' === lastReplyAction() && allowComposer, command: replyAllCommand, tooltip: 'MESSAGE/BUTTON_REPLY_ALL'">
|
||||
<i class="icon-reply-all"></i>
|
||||
</a>
|
||||
<a class="btn first btn-dark-disabled-border buttonForward pull-right" data-tooltip-join="bottom"
|
||||
data-bind="visible: 'forward' === lastReplyAction(), command: forwardCommand, tooltip: 'MESSAGE/BUTTON_FORWARD'">
|
||||
data-bind="visible: 'forward' === lastReplyAction() && allowComposer, command: forwardCommand, tooltip: 'MESSAGE/BUTTON_FORWARD'">
|
||||
<i class="icon-forward"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
<span class="i18n" data-i18n="COMPOSE/BUTTON_SEND"></span>
|
||||
</a>
|
||||
<a class="btn button-save" data-bind="command: saveCommand, tooltipErrorTip: savedErrorDesc, css: {'btn-danger': savedError }">
|
||||
<a class="btn button-save" data-bind="visible: allowFolders, command: saveCommand, tooltipErrorTip: savedErrorDesc, css: {'btn-danger': savedError }">
|
||||
<i data-bind="css: {'icon-floppy': !saving(), 'icon-spinner animated': saving(), 'icon-white': savedError()}"></i>
|
||||
|
||||
<span class="i18n" data-i18n="COMPOSE/BUTTON_SAVE"></span>
|
||||
|
|
|
|||
|
|
@ -43,21 +43,21 @@
|
|||
</a>
|
||||
</li>
|
||||
<!-- /ko -->
|
||||
<li class="e-item" role="presentation">
|
||||
<li class="e-item" role="presentation" data-bind="visible: allowSettings">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="click: settingsClick">
|
||||
<i class="icon-cog"></i>
|
||||
|
||||
<span class="i18n" data-i18n="TOP_TOOLBAR/BUTTON_SETTINGS"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation">
|
||||
<li class="e-item" role="presentation" data-bind="visible: allowHelp">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="click: settingsHelp">
|
||||
<i class="icon-help"></i>
|
||||
|
||||
<span class="i18n" data-i18n="TOP_TOOLBAR/BUTTON_HELP"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider" role="presentation"></li>
|
||||
<li class="divider" role="presentation" data-bind="visible: allowSettings || allowHelp"></li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="click: logoutClick">
|
||||
<i class="icon-power"></i>
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ Options -Indexes
|
|||
define('APP_MULTIPLY', 0 < strlen($sPrivateDataFolderInternalName) && APP_DEFAULT_PRIVATE_DATA_NAME !== APP_PRIVATE_DATA_NAME);
|
||||
|
||||
define('APP_DUMMY', '********');
|
||||
define('APP_GOOGLE_ACCESS_TOKEN_PREFIX', ':GAT:');
|
||||
define('APP_DEV_VERSION', '0.0.0');
|
||||
define('APP_API_PATH', 'http://api.rainloop.net/');
|
||||
define('APP_REP_PATH', 'http://repository.rainloop.net/v1/');
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ LABEL_LOGIN_SHOW_POWERED_LINK = "\"Powered by RainLoop\"-Link anzeigen"
|
|||
LEGEND_USER = "Benutzer"
|
||||
LABEL_USER_LOGO = "Logo"
|
||||
LABEL_USER_LOGO_TITLE = "Logo Title"
|
||||
LABEL_USER_LOGO_MESSAGE = "Logo (Message View)"
|
||||
LABEL_USER_CUSTOM_CSS = "Benutzerdefiniertes CSS"
|
||||
LEGEND_WELCOME_PAGE = "Willkommensseite"
|
||||
LABEL_WELCOME_PAGE_TITLE = "Titel"
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ LABEL_LOGIN_SHOW_POWERED_LINK = "Show \"Powered by RainLoop\" link"
|
|||
LEGEND_USER = "User"
|
||||
LABEL_USER_LOGO = "Logo"
|
||||
LABEL_USER_LOGO_TITLE = "Logo Title"
|
||||
LABEL_USER_LOGO_MESSAGE = "Logo (Message View)"
|
||||
LABEL_USER_CUSTOM_CSS = "Custom CSS"
|
||||
LEGEND_WELCOME_PAGE = "Welcome page"
|
||||
LABEL_WELCOME_PAGE_TITLE = "Title"
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ LABEL_LOGIN_SHOW_POWERED_LINK = "Mostrar link \"Powered by RainLoop\""
|
|||
LEGEND_USER = "Internas"
|
||||
LABEL_USER_LOGO = "Logo"
|
||||
LABEL_USER_LOGO_TITLE = "Logo Title"
|
||||
LABEL_USER_LOGO_MESSAGE = "Logo (Message View)"
|
||||
LABEL_USER_CUSTOM_CSS = "CSS personalizado"
|
||||
LEGEND_WELCOME_PAGE = "Página de boas-vindas"
|
||||
LABEL_WELCOME_PAGE_TITLE = "Título"
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ LABEL_LOGIN_SHOW_POWERED_LINK = "Показывать ссылку \"Powered by
|
|||
LEGEND_USER = "Экран пользователя"
|
||||
LABEL_USER_LOGO = "Логотип"
|
||||
LABEL_USER_LOGO_TITLE = "Название логотипа"
|
||||
LABEL_USER_LOGO_MESSAGE = "Логотип (Message View)"
|
||||
LABEL_USER_CUSTOM_CSS = "Кастомный CSS"
|
||||
LEGEND_WELCOME_PAGE = "Приветствие"
|
||||
LABEL_WELCOME_PAGE_TITLE = "Название"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue