diff --git a/dev/App/Abstract.js b/dev/App/Abstract.js index 594c45e7e..98c6a8a74 100644 --- a/dev/App/Abstract.js +++ b/dev/App/Abstract.js @@ -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(); diff --git a/dev/App/User.js b/dev/App/User.js index 337683bea..f544ae6be 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -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 { diff --git a/dev/Common/Cache.js b/dev/Common/Cache.js index bacc24b80..86dc950e9 100644 --- a/dev/Common/Cache.js +++ b/dev/Common/Cache.js @@ -204,7 +204,10 @@ */ CacheUserStorage.prototype.setFolderHash = function (sFolderFullNameRaw, sFolderHash) { - this.oFolderHashCache[sFolderFullNameRaw] = sFolderHash; + if ('' !== sFolderFullNameRaw) + { + this.oFolderHashCache[sFolderFullNameRaw] = sFolderHash; + } }; /** diff --git a/dev/Common/Enums.js b/dev/Common/Enums.js index 2028b7937..887240ba9 100644 --- a/dev/Common/Enums.js +++ b/dev/Common/Enums.js @@ -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', diff --git a/dev/Common/Globals.js b/dev/Common/Globals.js index 5391f5744..19a194552 100644 --- a/dev/Common/Globals.js +++ b/dev/Common/Globals.js @@ -208,6 +208,7 @@ }; Globals.leftPanelDisabled = ko.observable(false); + Globals.leftPanelType = ko.observable(''); // popups Globals.popupVisibilityNames = ko.observableArray([]); diff --git a/dev/Common/Links.js b/dev/Common/Links.js index 50ac867e6..450c9924d 100644 --- a/dev/Common/Links.js +++ b/dev/Common/Links.js @@ -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' : ''); }; /** diff --git a/dev/Common/Utils.js b/dev/Common/Utils.js index 7eb657b86..c42e635d8 100644 --- a/dev/Common/Utils.js +++ b/dev/Common/Utils.js @@ -150,6 +150,11 @@ { if (sMailToUrl && 'mailto:' === sMailToUrl.toString().substr(0, 7).toLowerCase()) { + if (!PopupComposeVoreModel) + { + return true; + } + sMailToUrl = sMailToUrl.toString().substr(7); var diff --git a/dev/Knoin/Knoin.js b/dev/Knoin/Knoin.js index 42744c49f..01808641c 100644 --- a/dev/Knoin/Knoin.js +++ b/dev/Knoin/Knoin.js @@ -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); diff --git a/dev/Screen/User/MailBox.js b/dev/Screen/User/MailBox.js index a08625507..8f79ed316 100644 --- a/dev/Screen/User/MailBox.js +++ b/dev/Screen/User/MailBox.js @@ -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(''); + } }; /** diff --git a/dev/Screen/User/Settings.js b/dev/Screen/User/Settings.js index 20575d599..8ac11868f 100644 --- a/dev/Screen/User/Settings.js +++ b/dev/Screen/User/Settings.js @@ -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 () diff --git a/dev/Settings/Admin/Branding.js b/dev/Settings/Admin/Branding.js index d83cd123f..b121ad261 100644 --- a/dev/Settings/Admin/Branding.js +++ b/dev/Settings/Admin/Branding.js @@ -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); diff --git a/dev/Styles/FolderList.less b/dev/Styles/FolderList.less index d11993121..a97ac85f9 100644 --- a/dev/Styles/FolderList.less +++ b/dev/Styles/FolderList.less @@ -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; diff --git a/dev/Styles/Layout.less b/dev/Styles/Layout.less index d183aded1..a7aca5690 100644 --- a/dev/Styles/Layout.less +++ b/dev/Styles/Layout.less @@ -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); diff --git a/dev/Styles/MessageView.less b/dev/Styles/MessageView.less index c72353fa1..215a8e4b8 100644 --- a/dev/Styles/MessageView.less +++ b/dev/Styles/MessageView.less @@ -53,6 +53,10 @@ html.rl-no-preview-pane { } } + .logoPlace { + text-align: center; + } + .b-message-view-desc { text-align: center; font-size: 24px; diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index c464e3162..f9bf54df5 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -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')) { diff --git a/dev/View/Popup/Contacts.js b/dev/View/Popup/Contacts.js index 0cf7c8b17..4ac9b6e7e 100644 --- a/dev/View/Popup/Contacts.js +++ b/dev/View/Popup/Contacts.js @@ -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')); + } } }; diff --git a/dev/View/User/AbstractSystemDropDown.js b/dev/View/User/AbstractSystemDropDown.js index c67261165..88dcf9474 100644 --- a/dev/View/User/AbstractSystemDropDown.js +++ b/dev/View/User/AbstractSystemDropDown.js @@ -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 () diff --git a/dev/View/User/Login.js b/dev/View/User/Login.js index 3fe1de3d8..f3359f4b5 100644 --- a/dev/View/User/Login.js +++ b/dev/View/User/Login.js @@ -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 () { diff --git a/dev/View/User/MailBox/FolderList.js b/dev/View/User/MailBox/FolderList.js index 7004b0fb3..d32f72a6a 100644 --- a/dev/View/User/MailBox/FolderList.js +++ b/dev/View/User/MailBox/FolderList.js @@ -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 () diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index 2f4955e5b..aae4ab7ba 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -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 () diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 01bc1f54c..fef533f33 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -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()]); } diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Base/Utils.php b/rainloop/v/0.0.0/app/libraries/MailSo/Base/Utils.php index ba2ace531..9d3b64cc0 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Base/Utils.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Base/Utils.php @@ -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; diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Config.php b/rainloop/v/0.0.0/app/libraries/MailSo/Config.php index 682b0439b..b40fdf518 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Config.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Config.php @@ -46,6 +46,11 @@ class Config */ public static $MessageListDateFilter = 0; + /** + * @var string + */ + public static $MessageListPermanentFilter = ''; + /** * @var int */ diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderResponseStatus.php b/rainloop/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderResponseStatus.php index f1f14fee8..588473edf 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderResponseStatus.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderResponseStatus.php @@ -22,5 +22,6 @@ class FolderResponseStatus const RECENT = 'RECENT'; const UNSEEN = 'UNSEEN'; const UIDNEXT = 'UIDNEXT'; + const HIGHESTMODSEQ = 'HIGHESTMODSEQ'; const UIDVALIDITY = 'UIDVALIDITY'; } diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderStatus.php b/rainloop/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderStatus.php index d958e4897..7b9fc4e70 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderStatus.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderStatus.php @@ -22,5 +22,6 @@ class FolderStatus const RECENT = 'RECENT'; const UNSEEN = 'UNSEEN'; const UIDNEXT = 'UIDNEXT'; + const HIGHESTMODSEQ = 'HIGHESTMODSEQ'; const UIDVALIDITY = 'UIDVALIDITY'; } diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Imap/FolderInformation.php b/rainloop/v/0.0.0/app/libraries/MailSo/Imap/FolderInformation.php index c5b437889..f6ac83e43 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Imap/FolderInformation.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Imap/FolderInformation.php @@ -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; } /** diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php b/rainloop/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php index 8abaea90a..45d365472 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php @@ -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 && diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php b/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php index d8ad6896e..5946ff6ea 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php @@ -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), diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php index 94b57e98b..c0beae908 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -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)) diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Api.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Api.php index 156b9f25c..7ac756b24 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Api.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Api.php @@ -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', ''); diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php index de8b0a6a9..05020a6a6 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php @@ -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), diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Enumerations/Capa.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Enumerations/Capa.php index b89535211..d7602cdee 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Enumerations/Capa.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Enumerations/Capa.php @@ -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'; } diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Model/Account.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Model/Account.php index a862dd272..31108e1f1 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Model/Account.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Model/Account.php @@ -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; } diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/ServiceActions.php b/rainloop/v/0.0.0/app/libraries/RainLoop/ServiceActions.php index 6a50121af..1f75a9bb2 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/ServiceActions.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/ServiceActions.php @@ -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); } /** diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Social.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Social.php index 6b363a0a3..0183b84f3 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Social.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Social.php @@ -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'], diff --git a/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsBranding.html b/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsBranding.html index 899ba9235..23d724312 100644 --- a/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsBranding.html +++ b/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsBranding.html @@ -155,6 +155,21 @@ }"> +
+ +
+
+
+
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/MailFolderList.html b/rainloop/v/0.0.0/app/templates/Views/User/MailFolderList.html index a135a530f..aaf438106 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/MailFolderList.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/MailFolderList.html @@ -2,14 +2,14 @@ 'single-root-inbox': foldersListWithSingleInboxRootFolder}"> -
+
@@ -19,7 +19,7 @@
-