diff --git a/dev/App/Abstract.js b/dev/App/Abstract.js index 19a88db9a..6b160d847 100644 --- a/dev/App/Abstract.js +++ b/dev/App/Abstract.js @@ -252,9 +252,7 @@ Utils.initNotificationLanguage(); }, null); - _.delay(function () { - Utils.windowResize(); - }, 1000); + _.delay(Utils.windowResizeCallback, 1000); ssm.addState({ 'id': 'mobile', diff --git a/dev/Common/Enums.js b/dev/Common/Enums.js index bfa211f8a..cbd7e496e 100644 --- a/dev/Common/Enums.js +++ b/dev/Common/Enums.js @@ -306,6 +306,7 @@ 'None': 'None', 'MoveTo': 'MoveTo', 'Discard': 'Discard', + 'Vacation': 'Vacation', 'Forward': 'Forward' }; diff --git a/dev/Common/Utils.js b/dev/Common/Utils.js index 692137456..c65f2da16 100644 --- a/dev/Common/Utils.js +++ b/dev/Common/Utils.js @@ -52,6 +52,10 @@ } }, 50); + Utils.windowResizeCallback = function () { + Utils.windowResize(); + }; + /** * @param {(string|number)} mValue * @param {boolean=} bIncludeZero diff --git a/dev/External/ko.js b/dev/External/ko.js index 39029351b..7d55dff68 100644 --- a/dev/External/ko.js +++ b/dev/External/ko.js @@ -266,9 +266,7 @@ 'keyboard': false, 'show': ko.unwrap(fValueAccessor()) }) - .on('shown.koModal', function () { - Utils.windowResize(); - }) + .on('shown.koModal', Utils.windowResizeCallback) .find('.close').on('click.koModal', function () { fValueAccessor()(false); }); diff --git a/dev/Model/Filter.js b/dev/Model/Filter.js index 9700e21da..664fc913d 100644 --- a/dev/Model/Filter.js +++ b/dev/Model/Filter.js @@ -9,6 +9,9 @@ Enums = require('Common/Enums'), Utils = require('Common/Utils'), + + Cache = require('Storage/User/Cache'), + FilterConditionModel = require('Model/FilterCondition'), AbstractModel = require('Knoin/AbstractModel') @@ -29,13 +32,15 @@ this.name.error = ko.observable(false); this.name.focused = ko.observable(false); - this.raw = ko.observable(''); - this.conditions = ko.observableArray([]); this.conditionsType = ko.observable(Enums.FilterRulesType.Any); // Actions this.actionValue = ko.observable(''); + this.actionValue.error = ko.observable(false); + + this.actionValueSecond = ko.observable(''); + this.actionMarkAsRead = ko.observable(false); this.actionSkipOthers = ko.observable(false); @@ -44,8 +49,43 @@ this.actionType = ko.observable(Enums.FiltersAction.MoveTo); - this.actionType.subscribe(function (sValue) { + this.actionType.subscribe(function () { this.actionValue(''); + this.actionValue.error(false); + this.actionValueSecond(''); + }, this); + + var fGetRealFolderName = function (sFolderFullNameRaw) { + var oFolder = Cache.getFolderFromCacheList(sFolderFullNameRaw); + return oFolder ? oFolder.fullName.replace( + '.' === oFolder.delimiter ? /\./ : /[\\\/]+/, ' / ') : sFolderFullNameRaw; + }; + + this.nameSub = ko.computed(function () { + + var + sResult = '', + sActionValue = this.actionValue() + ; + + switch (this.actionType()) + { + case Enums.FiltersAction.MoveTo: + sResult = 'MoveTo $i18n "' + fGetRealFolderName(sActionValue) + '"'; + break; + case Enums.FiltersAction.Forward: + sResult = 'Forward @i18n "' + sActionValue + '"'; + break; + case Enums.FiltersAction.Vacation: + sResult = 'Vacation message @i18n'; + break; + case Enums.FiltersAction.Discard: + sResult = 'Discard @i18n'; + break; + } + + return sResult ? '(' + sResult + ')' : ''; + }, this); this.actionTemplate = ko.computed(function () { @@ -55,17 +95,19 @@ { default: case Enums.FiltersAction.MoveTo: - sTemplate = 'SettingsFiltersActionValueAsFolders'; + sTemplate = 'SettingsFiltersActionMoveToFolder'; break; -// case Enums.FiltersAction.Forward: -// sTemplate = 'SettingsFiltersActionWithValue'; -// break; case Enums.FiltersAction.Forward: sTemplate = 'SettingsFiltersActionForward'; break; + case Enums.FiltersAction.Vacation: + sTemplate = 'SettingsFiltersActionVacation'; + break; case Enums.FiltersAction.None: + sTemplate = 'SettingsFiltersActionNone'; + break; case Enums.FiltersAction.Discard: - sTemplate = 'SettingsFiltersActionNoValue'; + sTemplate = 'SettingsFiltersActionDiscard'; break; } @@ -73,14 +115,16 @@ }, this); - this.regDisposables(this.conditions.subscribe(function () { - Utils.windowResize(); - })); + this.regDisposables(this.conditions.subscribe(Utils.windowResizeCallback)); this.regDisposables(this.name.subscribe(function (sValue) { this.name.error('' === sValue); }, this)); + this.regDisposables(this.actionValue.subscribe(function (sValue) { + this.actionValue.error('' === sValue); + }, this)); + this.regDisposables([this.actionTemplate]); this.deleteAccess = ko.observable(false); @@ -94,6 +138,40 @@ this.id = Utils.fakeMd5(); }; + FilterModel.prototype.verify = function () + { + if ('' === this.name()) + { + this.name.error(true); + return false; + } + + if ('' === this.actionValue()) + { + if (-1 < Utils.inArray(this.actionType(), [ + Enums.FiltersAction.MoveTo, + Enums.FiltersAction.Forward, + Enums.FiltersAction.Vacation + ])) + { + this.actionValue.error(true); + return false; + } + } + + if (Enums.FiltersAction.Forward === this.actionType() && + -1 === this.actionValue().indexOf('@')) + { + this.actionValue.error(true); + return false; + } + + this.name.error(false); + this.actionValue.error(false); + + return true; + }; + FilterModel.prototype.toJson = function () { return { @@ -106,10 +184,9 @@ }), 'ActionValue': this.actionValue(), + 'ActionValueSecond': this.actionValueSecond(), 'ActionType': this.actionType(), - 'Raw': this.raw(), - 'MarkAsRead': this.actionMarkAsRead() ? '1' : '0', 'KeepForward': this.keepForward() ? '1' : '0', 'SkipOthers': this.actionSkipOthers() ? '1' : '0' @@ -152,17 +229,18 @@ oClone.name(this.name()); oClone.name.error(this.name.error()); - oClone.raw(this.raw()); - oClone.conditionsType(this.conditionsType()); oClone.actionMarkAsRead(this.actionMarkAsRead()); oClone.actionSkipOthers(this.actionSkipOthers()); - oClone.actionValue(this.actionValue()); - oClone.actionType(this.actionType()); + oClone.actionValue(this.actionValue()); + oClone.actionValue.error(this.actionValue.error()); + + oClone.actionValueSecond(this.actionValueSecond()); + oClone.keepForward(this.keepForward()); oClone.conditions(_.map(this.conditions(), function (oCondition) { diff --git a/dev/Screen/User/MailBox.js b/dev/Screen/User/MailBox.js index aabbfa476..43c232475 100644 --- a/dev/Screen/User/MailBox.js +++ b/dev/Screen/User/MailBox.js @@ -99,15 +99,9 @@ MailBoxUserScreen.prototype.onStart = function () { - var - fResizeFunction = function () { - Utils.windowResize(); - } - ; - - Data.folderList.subscribe(fResizeFunction); - Data.messageList.subscribe(fResizeFunction); - Data.message.subscribe(fResizeFunction); + Data.folderList.subscribe(Utils.windowResizeCallback); + Data.messageList.subscribe(Utils.windowResizeCallback); + Data.message.subscribe(Utils.windowResizeCallback); Data.layout.subscribe(function (nValue) { diff --git a/dev/Settings/User/Filters.js b/dev/Settings/User/Filters.js index a90aecd63..4a4cfb1a2 100644 --- a/dev/Settings/User/Filters.js +++ b/dev/Settings/User/Filters.js @@ -28,9 +28,7 @@ this.filters.loading = ko.observable(false).extend({'throttle': 200}); this.filters.saving = ko.observable(false).extend({'throttle': 200}); - this.filters.subscribe(function () { - Utils.windowResize(); - }); + this.filters.subscribe(Utils.windowResizeCallback); this.processText = ko.computed(function () { return this.filters.loading() ? Utils.i18n('SETTINGS_FILTERS/LOADING_PROCESS') : ''; @@ -136,7 +134,7 @@ self.haveChanges(true); } - }, false]); + }, true]); }; FiltersUserSettings.prototype.onBuild = function (oDom) diff --git a/dev/Storage/User/Data.js b/dev/Storage/User/Data.js index 4f9afd0ae..5ddb17b5c 100644 --- a/dev/Storage/User/Data.js +++ b/dev/Storage/User/Data.js @@ -239,11 +239,11 @@ }, this); this.folderMenuForFilters = ko.computed(function () { - return Utils.folderListOptionsBuilder(this.folderListSystem(), this.folderList(), [ - 'INBOX' - ], null, null, null, null, function (oItem) { - return oItem ? oItem.localName() : ''; - }); + return Utils.folderListOptionsBuilder(this.folderListSystem(), this.folderList(), + ['INBOX'], [['', '']], null, null, null, function (oItem) { + return oItem ? oItem.localName() : ''; + } + ); }, this); // message list diff --git a/dev/Styles/SettingsFilters.less b/dev/Styles/SettingsFilters.less index 9bcf8e558..888da2b0e 100644 --- a/dev/Styles/SettingsFilters.less +++ b/dev/Styles/SettingsFilters.less @@ -21,13 +21,17 @@ margin-right: 5px; } - .filter-name { + .filter-name, .filter-sub-name { display: inline-block; word-break: break-all; box-sizing: border-box; line-height: 22px; cursor: pointer; } + + .filter-sub-name { + color: #aaa; + } } .filter-item { diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index 41317684c..404214c90 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -51,9 +51,6 @@ var self = this, - fResizeSub = function () { - Utils.windowResize(); - }, fCcAndBccCheckHelper = function (aValue) { if (false === self.showCcAndBcc() && 0 < aValue.length) { @@ -134,8 +131,8 @@ this.attachmentsPlace = ko.observable(false); - this.attachments.subscribe(fResizeSub); - this.attachmentsPlace.subscribe(fResizeSub); + this.attachments.subscribe(Utils.windowResizeCallback); + this.attachmentsPlace.subscribe(Utils.windowResizeCallback); this.attachmentsInErrorCount.subscribe(function (iN) { if (0 === iN) diff --git a/dev/View/Popup/Contacts.js b/dev/View/Popup/Contacts.js index 0fc80e4fa..a8e6f6b4f 100644 --- a/dev/View/Popup/Contacts.js +++ b/dev/View/Popup/Contacts.js @@ -178,13 +178,8 @@ this.reloadContactList(); }, this); - this.contacts.subscribe(function () { - Utils.windowResize(); - }, this); - - this.viewProperties.subscribe(function () { - Utils.windowResize(); - }, this); + this.contacts.subscribe(Utils.windowResizeCallback); + this.viewProperties.subscribe(Utils.windowResizeCallback); this.contactsChecked = ko.computed(function () { return _.filter(this.contacts(), function (oItem) { @@ -745,7 +740,7 @@ if (this.bBackToCompose) { this.bBackToCompose = false; - + kn.showScreenPopup(require('View/Popup/Compose')); } }; diff --git a/dev/View/Popup/Filter.js b/dev/View/Popup/Filter.js index 37e82d6ff..0f7f640d7 100644 --- a/dev/View/Popup/Filter.js +++ b/dev/View/Popup/Filter.js @@ -30,31 +30,33 @@ this.fTrueCallback = null; this.filter = ko.observable(null); - this.selectedFolderValue = ko.observable(Consts.Values.UnuseOptionValue); - this.folderSelectList = Data.folderMenuForFilters; this.defautOptionsAfterRender = Utils.defautOptionsAfterRender; + this.folderSelectList = Data.folderMenuForFilters; + this.selectedFolderValue = ko.observable(''); + + this.selectedFolderValue.subscribe(function() { + if (this.filter()) + { + this.filter().actionValue.error(false); + } + }, this); this.saveFilter = Utils.createCommand(this, function () { if (this.filter()) { - if ('' === this.filter().name()) + if (Enums.FiltersAction.MoveTo === this.filter().actionType()) + { + this.filter().actionValue(this.selectedFolderValue()); + } + + if (!this.filter().verify()) { - this.filter().name.error(true); return false; } if (this.fTrueCallback) { - if (Enums.FiltersAction.MoveTo === this.filter().actionType()) - { - this.filter().actionValue(this.selectedFolderValue()); - } - else if (Enums.FiltersAction.Forward !== this.filter().actionType()) - { - this.filter().actionValue(''); - } - this.fTrueCallback(this.filter()); } @@ -68,9 +70,10 @@ }); this.actionTypeOptions = [ - {'id': Enums.FiltersAction.None, 'name': 'None @i18n'}, +// {'id': Enums.FiltersAction.None, 'name': 'None @i18n'}, {'id': Enums.FiltersAction.MoveTo, 'name': ' Move to @i18n'}, {'id': Enums.FiltersAction.Forward, 'name': 'Forward to @i18n'}, + {'id': Enums.FiltersAction.Vacation, 'name': 'Vacation message @i18n'}, {'id': Enums.FiltersAction.Discard, 'name': 'Discard @i18n'} ]; diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 16f9177bd..9901fe0a6 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -292,12 +292,7 @@ Utils.windowResize(); }); - this.messageLoadingThrottle.subscribe(function (bV) { - if (bV) - { - Utils.windowResize(); - } - }); + this.messageLoadingThrottle.subscribe(Utils.windowResizeCallback); this.goUpCommand = Utils.createCommand(this, function () { Events.pub('mailbox.message-list.selector.go-up'); diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Classes/Filter.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Classes/Filter.php index 992eb1d48..0f95d8ec8 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Classes/Filter.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Classes/Filter.php @@ -39,6 +39,11 @@ class Filter */ private $sActionValue; + /** + * @var string + */ + private $sActionValueSecond; + /** * @var bool */ @@ -72,6 +77,7 @@ class Filter $this->sActionType = \RainLoop\Providers\Filters\Enumerations\ActionType::MOVE_TO; $this->sActionValue = ''; + $this->sActionValueSecond = ''; $this->bMarkAsRead = false; $this->bSkipOthers = false; @@ -134,6 +140,14 @@ class Filter return $this->sActionValue; } + /** + * @return string + */ + public function ActionValueSecond() + { + return $this->sActionValueSecond; + } + /** * @return bool */ @@ -201,6 +215,7 @@ class Filter \RainLoop\Providers\Filters\Enumerations\ActionType::MOVE_TO; $this->sActionValue = isset($aFilter['ActionValue']) ? $aFilter['ActionValue'] : ''; + $this->sActionValueSecond = isset($aFilter['ActionValueSecond']) ? $aFilter['ActionValueSecond'] : ''; $this->bMarkAsRead = isset($aFilter['MarkAsRead']) ? $aFilter['MarkAsRead'] : false; $this->bSkipOthers = isset($aFilter['SkipOthers']) ? $aFilter['SkipOthers'] : false; @@ -239,6 +254,7 @@ class Filter 'ConditionsType' => $this->ConditionsType(), 'ActionType' => $this->ActionType(), 'ActionValue' => $this->ActionValue(), + 'ActionValueSecond' => $this->ActionValueSecond(), 'MarkAsRead' => $this->MarkAsRead(), 'SkipOthers' => $this->SkipOthers() ); diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Enumerations/ActionType.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Enumerations/ActionType.php index f982c5b6b..9d7b7bf9a 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Enumerations/ActionType.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Enumerations/ActionType.php @@ -7,5 +7,6 @@ class ActionType const NONE = 'None'; const MOVE_TO = 'MoveTo'; const DISCARD = 'Discard'; + const VACATION = 'Vacation'; const FORWARD = 'Forward'; } diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/SieveStorage.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/SieveStorage.php index 0eb45b7e5..c2f4b5bfc 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/SieveStorage.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/SieveStorage.php @@ -195,6 +195,28 @@ class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface $aResult[] = $sTab.'discard;'; $aResult[] = $sTab.'stop;'; break; + case \RainLoop\Providers\Filters\Enumerations\ActionType::VACATION: + $sValue = \trim($oFilter->ActionValue()); + $sValueSecond = \trim($oFilter->ActionValueSecond()); + if (0 < \strlen($sValue)) + { + $aCapa['vacation'] = true; + + $sSubject = ''; + if (0 < \strlen($sValueSecond)) + { + $sSubject = ':subject "'.$this->quote( + \preg_replace('/[\s]+/u', ' ', $sValueSecond)).'" '; + } + + $aResult[] = $sTab.'vacation :days 1 '.$sSubject.'"'.$this->quote($sValue).'";'; + $aResult[] = $sTab.'stop;'; + } + else + { + $aResult[] = $sTab.'# @Error (vacation): empty action value'; + } + break; case \RainLoop\Providers\Filters\Enumerations\ActionType::FORWARD: $sValue = $oFilter->ActionValue(); if (0 < \strlen($sValue)) diff --git a/rainloop/v/0.0.0/app/templates/Views/User/PopupsFilter.html b/rainloop/v/0.0.0/app/templates/Views/User/PopupsFilter.html index 491a467cf..aac776f16 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/PopupsFilter.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/PopupsFilter.html @@ -14,7 +14,7 @@