From e4b286e257591d9d6bef5bcefa26f5367e82e7d0 Mon Sep 17 00:00:00 2001 From: RainLoop Team Date: Thu, 18 Dec 2014 00:53:46 +0400 Subject: [PATCH] Merged from sub repository (filters - step 4) --- .gitignore | 2 + dev/App/User.js | 68 +++- dev/Common/Utils.js | 6 +- dev/External/ko.js | 24 ++ dev/Model/Account.js | 5 +- dev/Model/Filter.js | 85 +++-- dev/Model/FilterCondition.js | 38 +- dev/Settings/User/Filters.js | 126 ++++++- dev/Settings/User/Identities.js | 10 +- dev/Storage/User/Data.js | 9 +- dev/Storage/User/Remote.js | 29 ++ dev/Styles/SettingsFilters.less | 6 + dev/Styles/SystemDropDown.less | 92 +++-- dev/View/Popup/Compose.js | 46 +-- dev/View/Popup/Filter.js | 81 +++- dev/View/User/MailBox/MessageList.js | 2 +- gulpfile.js | 3 +- package.json | 2 +- rainloop/v/0.0.0/app/src/RainLoop/Actions.php | 51 +++ .../Views/Admin/AdminSettingsLicensing.html | 4 +- .../templates/Views/Admin/PopupsDomain.html | 4 +- .../templates/Views/User/PopupsFilter.html | 44 +-- .../templates/Views/User/SettingsFilters.html | 60 ++- .../User/SettingsFiltersActionNoValue.html | 2 +- .../SettingsFiltersActionValueAsFolders.html | 2 +- .../User/SettingsFiltersActionWithValue.html | 2 +- .../User/SettingsFiltersConditionDefault.html | 6 +- .../templates/Views/User/SystemDropDown.html | 29 +- rainloop/v/0.0.0/langs/bg.ini | 6 - rainloop/v/0.0.0/langs/de.ini | 1 + rainloop/v/0.0.0/langs/en.ini | 8 +- rainloop/v/0.0.0/langs/es.ini | 1 + rainloop/v/0.0.0/langs/fr.ini | 1 + rainloop/v/0.0.0/langs/hu.ini | 1 + rainloop/v/0.0.0/langs/is.ini | 1 + rainloop/v/0.0.0/langs/it.ini | 1 + rainloop/v/0.0.0/langs/ja-jp.ini | 1 + rainloop/v/0.0.0/langs/ko-kr.ini | 1 + rainloop/v/0.0.0/langs/lt.ini | 6 - rainloop/v/0.0.0/langs/lv.ini | 1 + rainloop/v/0.0.0/langs/nl.ini | 1 + rainloop/v/0.0.0/langs/no.ini | 1 + rainloop/v/0.0.0/langs/pl.ini | 6 - rainloop/v/0.0.0/langs/pt-br.ini | 6 - rainloop/v/0.0.0/langs/pt-pt.ini | 1 + rainloop/v/0.0.0/langs/ro.ini | 1 + rainloop/v/0.0.0/langs/ru.ini | 1 + rainloop/v/0.0.0/langs/sk.ini | 1 + rainloop/v/0.0.0/langs/sv.ini | 6 - rainloop/v/0.0.0/langs/tr.ini | 6 - rainloop/v/0.0.0/langs/ua.ini | 1 + rainloop/v/0.0.0/langs/zh-cn.ini | 1 + rainloop/v/0.0.0/langs/zh-tw.ini | 6 - .../jquery.ui.touch-punch.min.js | 11 + vendors/knockout-sortable/README.md | 129 +++++++ .../knockout-sortable/knockout-sortable.js | 352 ++++++++++++++++++ .../knockout-sortable.min.js | 2 + vendors/knockout-sortable/package.json | 13 + 58 files changed, 1176 insertions(+), 236 deletions(-) create mode 100644 vendors/jquery-ui.touch-punch/jquery.ui.touch-punch.min.js create mode 100644 vendors/knockout-sortable/README.md create mode 100644 vendors/knockout-sortable/knockout-sortable.js create mode 100644 vendors/knockout-sortable/knockout-sortable.min.js create mode 100644 vendors/knockout-sortable/package.json diff --git a/.gitignore b/.gitignore index dac9fcceb..2e09257cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ /.idea /nbproject /npm-debug.log +/rainloop.sublime-project +/rainloop.sublime-workspace /rainloop/v/0.0.0/static/css/*.css /rainloop/v/0.0.0/static/js/*.js /rainloop/v/0.0.0/static/js/**/*.js diff --git a/dev/App/User.js b/dev/App/User.js index 154819268..2509c1d8d 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -75,6 +75,20 @@ Events.pub('interval.10m'); }, 60000 * 10); + window.setInterval(function () { + Events.pub('interval.15m'); + }, 60000 * 15); + + window.setInterval(function () { + Events.pub('interval.20m'); + }, 60000 * 15); + + window.setTimeout(function () { + window.setInterval(function () { + Events.pub('interval.5m-after5m'); + }, 60000 * 5); + }, 60000 * 5); + window.setTimeout(function () { window.setInterval(function () { Events.pub('interval.10m-after5m'); @@ -476,8 +490,32 @@ } }; - AppUser.prototype.accountsAndIdentities = function () + AppUser.prototype.accountsCounts = function () { + Remote.accountsCounts(function (sResult, oData) { + if (Enums.StorageResultType.Success === sResult && oData.Result && oData.Result['Counts']) + { + var aAcounts = Data.accounts(); + + _.each(oData.Result['Counts'], function (oItem) { + + var oAccount = _.find(aAcounts, function (oAccount) { + return oAccount && oItem[0] === oAccount.email; + }); + + if (oAccount) + { + oAccount.count(Utils.pInt(oItem[1])); + } + }); + } + }); + }; + + AppUser.prototype.accountsAndIdentities = function (bBoot) + { + var self = this; + Data.accountsLoading(true); Data.identitiesLoading(true); @@ -489,6 +527,7 @@ if (Enums.StorageResultType.Success === sResult && oData.Result) { var + aCounts = {}, sParentEmail = Settings.settingsGet('ParentEmail'), sAccountEmail = Data.accountEmail() ; @@ -497,12 +536,22 @@ if (Utils.isArray(oData.Result['Accounts'])) { + _.each(Data.accounts(), function (oAccount) { + aCounts[oAccount.email] = oAccount.count(); + }); + Utils.delegateRunOnDestroy(Data.accounts()); + Data.accounts(_.map(oData.Result['Accounts'], function (sValue) { - return new AccountModel(sValue, sValue !== sParentEmail); + return new AccountModel(sValue, sValue !== sParentEmail, aCounts[sValue] || 0); })); } + if (Utils.isUnd(bBoot) ? false : !!bBoot) + { + self.accountsCounts(); + } + if (Utils.isArray(oData.Result['Identities'])) { Utils.delegateRunOnDestroy(Data.identities()); @@ -723,8 +772,9 @@ }); if (bBoot) - { - self.folderInformationMultiply(true); + { _.delay(function () { + self.folderInformationMultiply(true); + }, 2000); } } } @@ -1342,7 +1392,7 @@ self.folderInformation(Cache.getFolderInboxName()); }); - Events.sub('interval.2m', function () { + Events.sub('interval.3m', function () { var sF = Data.currentFolderFullNameRaw(); if (Cache.getFolderInboxName() !== sF) { @@ -1350,15 +1400,15 @@ } }); - Events.sub('interval.3m', function () { + Events.sub('interval.5m-after5m', function () { self.folderInformationMultiply(); }); - Events.sub('interval.5m', function () { + Events.sub('interval.15m', function () { self.quota(); }); - Events.sub('interval.10m', function () { + Events.sub('interval.20m', function () { self.folders(); }); @@ -1379,7 +1429,7 @@ if (Settings.capa(Enums.Capa.AdditionalAccounts) || Settings.capa(Enums.Capa.AdditionalIdentities)) { - self.accountsAndIdentities(); + self.accountsAndIdentities(true); } _.delay(function () { diff --git a/dev/Common/Utils.js b/dev/Common/Utils.js index fbc764f5e..178d8d8f4 100644 --- a/dev/Common/Utils.js +++ b/dev/Common/Utils.js @@ -1253,9 +1253,11 @@ return sResult; }; - Utils.draggeblePlace = function () + Utils.draggablePlace = function () { - return $('
 
').appendTo('#rl-hidden'); + return $('
' + + ' ' + + '
').appendTo('#rl-hidden'); }; Utils.defautOptionsAfterRender = function (oDomOption, oItem) diff --git a/dev/External/ko.js b/dev/External/ko.js index 58b9570c8..8d3514f49 100644 --- a/dev/External/ko.js +++ b/dev/External/ko.js @@ -793,6 +793,30 @@ return oTarget; }; + ko.extenders.toggleSubscribeProperty = function (oTarget, oOptions) + { + var sProp = oOptions[1]; + + if (sProp) + { + oTarget.subscribe(function (oPrev) { + if (oPrev && oPrev[sProp]) + { + oPrev[sProp](false); + } + }, oOptions[0], 'beforeChange'); + + oTarget.subscribe(function (oNext) { + if (oNext && oNext[sProp]) + { + oNext[sProp](true); + } + }, oOptions[0]); + } + + return oTarget; + }; + ko.extenders.falseTimeout = function (oTarget, iOption) { var Utils = require('Common/Utils'); diff --git a/dev/Model/Account.js b/dev/Model/Account.js index 51bb44643..ea8c11270 100644 --- a/dev/Model/Account.js +++ b/dev/Model/Account.js @@ -17,13 +17,16 @@ * * @param {string} sEmail * @param {boolean=} bCanBeDelete = true + * @param {number=} iCount = 0 */ - function AccountModel(sEmail, bCanBeDelete) + function AccountModel(sEmail, bCanBeDelete, iCount) { AbstractModel.call(this, 'AccountModel'); this.email = sEmail; + this.count = ko.observable(iCount || 0); + this.deleteAccess = ko.observable(false); this.canBeDalete = ko.observable(Utils.isUnd(bCanBeDelete) ? true : !!bCanBeDelete); this.canBeEdit = this.canBeDalete; diff --git a/dev/Model/Filter.js b/dev/Model/Filter.js index c1901a09e..4845ce01b 100644 --- a/dev/Model/Filter.js +++ b/dev/Model/Filter.js @@ -21,42 +21,20 @@ { AbstractModel.call(this, 'FilterModel'); - this.isNew = ko.observable(true); this.enabled = ko.observable(true); this.name = ko.observable(''); - - this.conditionsType = ko.observable(Enums.FilterRulesType.All); + this.name.error = ko.observable(false); + this.name.focused = ko.observable(false); this.conditions = ko.observableArray([]); + this.conditionsType = ko.observable(Enums.FilterRulesType.All); // Actions - this.actionMarkAsRead = ko.observable(false); - this.actionSkipOtherFilters = ko.observable(true); this.actionValue = ko.observable(''); + this.actionMarkAsRead = ko.observable(false); this.actionType = ko.observable(Enums.FiltersAction.Move); - this.actionTypeOptions = [ // TODO i18n - {'id': Enums.FiltersAction.None, 'name': 'None'}, - {'id': Enums.FiltersAction.Move, 'name': ' Move to'}, - // {'id': Enums.FiltersAction.Forward, 'name': 'Forward to'}, - {'id': Enums.FiltersAction.Discard, 'name': 'Discard'} - ]; - - this.enableSkipOtherFilters = ko.computed(function () { - return -1 === Utils.inArray(this.actionType(), [ - Enums.FiltersAction.Move, Enums.FiltersAction.Forward, Enums.FiltersAction.Discard - ]); - }, this); - - this.actionSkipOtherFiltersResult = ko.computed({ - 'read': function () { - return this.actionSkipOtherFilters() || - !this.enableSkipOtherFilters(); - }, - 'write': this.actionSkipOtherFilters, - 'owner': this - }); this.actionTemplate = ko.computed(function () { @@ -84,14 +62,42 @@ Utils.windowResize(); })); - this.regDisposables([this.enableSkipOtherFilters, this.actionSkipOtherFiltersResult, this.actionTemplate]); + this.regDisposables(this.name.subscribe(function (sValue) { + this.name.error('' === sValue); + }, this)); + + this.regDisposables([this.actionTemplate]); + + this.deleteAccess = ko.observable(false); + this.canBeDalete = ko.observable(true); } _.extend(FilterModel.prototype, AbstractModel.prototype); + FilterModel.prototype.toJson = function () + { + return { + 'Enabled': this.enabled(), + 'Name': this.name(), + 'ConditionsType': this.conditionsType(), + 'Conditions': _.map(this.conditions(), function (oItem) { + return oItem.toJson(); + }), + 'ActionMarkAsRead': this.actionMarkAsRead(), + 'ActionValue': this.actionValue(), + 'ActionType': this.actionType() + }; + }; + FilterModel.prototype.addCondition = function () { - this.conditions.push(new FilterConditionModel(this.conditions)); + this.conditions.push(new FilterConditionModel()); + }; + + FilterModel.prototype.removeCondition = function (oConditionToDelete) + { + this.conditions.remove(oConditionToDelete); + Utils.delegateRunOnDestroy(oConditionToDelete); }; FilterModel.prototype.parse = function (oItem) @@ -107,6 +113,29 @@ return bResult; }; + FilterModel.prototype.cloneSelf = function () + { + var oClone = new FilterModel(); + + oClone.enabled(this.enabled()); + + oClone.name(this.name()); + oClone.name.error(this.name.error()); + + oClone.conditionsType(this.conditionsType()); + + oClone.actionMarkAsRead(this.actionMarkAsRead()); + oClone.actionValue(this.actionValue()); + + oClone.actionType(this.actionType()); + + oClone.conditions(_.map(this.conditions(), function (oCondition) { + return oCondition.cloneSelf(); + })); + + return oClone; + }; + module.exports = FilterModel; }()); \ No newline at end of file diff --git a/dev/Model/FilterCondition.js b/dev/Model/FilterCondition.js index 821aeabee..4f6660d5c 100644 --- a/dev/Model/FilterCondition.js +++ b/dev/Model/FilterCondition.js @@ -16,29 +16,12 @@ * @param {*} oKoList * @constructor */ - function FilterConditionModel(oKoList) + function FilterConditionModel() { AbstractModel.call(this, 'FilterConditionModel'); - this.parentList = oKoList; - this.field = ko.observable(Enums.FilterConditionField.From); - - this.fieldOptions = [ // TODO i18n - {'id': Enums.FilterConditionField.From, 'name': 'From'}, - {'id': Enums.FilterConditionField.Recipient, 'name': 'Recipient (To or CC)'}, - {'id': Enums.FilterConditionField.Subject, 'name': 'Subject'} - ]; - this.type = ko.observable(Enums.FilterConditionType.EqualTo); - - this.typeOptions = [ // TODO i18n - {'id': Enums.FilterConditionType.EqualTo, 'name': 'Equal To'}, - {'id': Enums.FilterConditionType.NotEqualTo, 'name': 'Not Equal To'}, - {'id': Enums.FilterConditionType.Contains, 'name': 'Contains'}, - {'id': Enums.FilterConditionType.NotContains, 'name': 'Not Contains'} - ]; - this.value = ko.observable(''); this.template = ko.computed(function () { @@ -60,9 +43,24 @@ _.extend(FilterConditionModel.prototype, AbstractModel.prototype); - FilterConditionModel.prototype.removeSelf = function () + FilterConditionModel.prototype.toJson = function () { - this.parentList.remove(this); + return { + 'Field': this.field(), + 'Type': this.type(), + 'Value': this.value() + }; + }; + + FilterConditionModel.prototype.cloneSelf = function () + { + var oClone = new FilterConditionModel(); + + oClone.field(this.field()); + oClone.type(this.type()); + oClone.value(this.value()); + + return oClone; }; module.exports = FilterConditionModel; diff --git a/dev/Settings/User/Filters.js b/dev/Settings/User/Filters.js index ea677c5e8..0e1ceb99c 100644 --- a/dev/Settings/User/Filters.js +++ b/dev/Settings/User/Filters.js @@ -6,7 +6,10 @@ var ko = require('ko'), - Utils = require('Common/Utils') + Enums = require('Common/Enums'), + Utils = require('Common/Utils'), + + Remote = require('Storage/User/Remote') ; /** @@ -14,14 +17,80 @@ */ function FiltersUserSettings() { + var self = this; + + this.haveChanges = ko.observable(false); + + this.processText = ko.observable(''); + this.visibility = ko.observable(false); + this.filters = ko.observableArray([]); - this.filters.loading = ko.observable(false); + 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.processText = ko.computed(function () { + return this.filters.loading() ? Utils.i18n('SETTINGS_FILTERS/LOADING_PROCESS') : ''; + }, this); + + this.visibility = ko.computed(function () { + return '' === this.processText() ? 'hidden' : 'visible'; + }, this); + + this.filterForDeletion = ko.observable(null).extend({'falseTimeout': 3000}).extend( + {'toggleSubscribeProperty': [this, 'deleteAccess']}); + + this.saveChanges = Utils.createCommand(this, function () { + + if (!this.filters.saving()) + { + this.filters.saving(true); + + Remote.filtersSave(function (sResult, oData) { + + self.filters.saving(false); + + if (Enums.StorageResultType.Success === sResult && oData && oData.Result) + { + + self.haveChanges(false); + self.updateList(); + } + + }, this.filters()); + } + + return true; + + }, function () { + return this.haveChanges(); + }); + + this.filters.subscribe(function () { + this.haveChanges(true); + }, this); } + FiltersUserSettings.prototype.scrollableOptions = function () + { + return { + // handle: '.drag-handle' + }; + }; + + FiltersUserSettings.prototype.updateList = function () + { + var self = this; + + this.filters.loading(true); + // Remote.filtersGet(function (sResult, oData) { + self.filters.loading(false); + // }); + }; + FiltersUserSettings.prototype.deleteFilter = function (oFilter) { this.filters.remove(oFilter); @@ -31,11 +100,60 @@ FiltersUserSettings.prototype.addFilter = function () { var - FilterModel = require('Model/Filter') + self = this, + FilterModel = require('Model/Filter'), + oNew = new FilterModel() ; require('Knoin/Knoin').showScreenPopup( - require('View/Popup/Filter'), [new FilterModel()]); + require('View/Popup/Filter'), [oNew, function () { + self.filters.push(oNew); + self.haveChanges(true); + }, false]); + }; + + FiltersUserSettings.prototype.editFilter = function (oEdit) + { + var + self = this, + oCloned = oEdit.cloneSelf() + ; + + require('Knoin/Knoin').showScreenPopup( + require('View/Popup/Filter'), [oCloned, function () { + + var + oFilters = self.filters(), + iIndex = oFilters.indexOf(oEdit) + ; + + if (-1 < iIndex && oFilters[iIndex]) + { + Utils.delegateRunOnDestroy(oFilters[iIndex]); + oFilters[iIndex] = oCloned; + + self.filters(oFilters); + self.haveChanges(true); + } + + }, false]); + }; + + FiltersUserSettings.prototype.onBuild = function (oDom) + { + var self = this; + + oDom + .on('click', '.filter-item .e-action', function () { + var oFilterItem = ko.dataFor(this); + if (oFilterItem) + { + self.editFilter(oFilterItem); + } + }) + ; + + this.updateList(); }; module.exports = FiltersUserSettings; diff --git a/dev/Settings/User/Identities.js b/dev/Settings/User/Identities.js index eba18b614..2022b1bee 100644 --- a/dev/Settings/User/Identities.js +++ b/dev/Settings/User/Identities.js @@ -129,15 +129,11 @@ { this.identityForDeletion(null); - var - fRemoveFolder = function (oIdentity) { - return oIdentityToRemove === oIdentity; - } - ; - if (oIdentityToRemove) { - this.identities.remove(fRemoveFolder); + this.identities.remove(function (oIdentity) { + return oIdentityToRemove === oIdentity; + }); Remote.identityDelete(function () { require('App/User').accountsAndIdentities(); diff --git a/dev/Storage/User/Data.js b/dev/Storage/User/Data.js index 57a140d41..e9bcaa729 100644 --- a/dev/Storage/User/Data.js +++ b/dev/Storage/User/Data.js @@ -631,8 +631,8 @@ iUtc = moment().unix(), iTimeout = iUtc - 60 * 5, aTimeouts = [], + sInboxFolderName = Cache.getFolderInboxName(), fSearchFunction = function (aList) { - var sInboxFolderName = Cache.getFolderInboxName(); _.each(aList, function (oFolder) { if (oFolder && sInboxFolderName !== oFolder.fullNameRaw && oFolder.selectable && oFolder.existen && @@ -653,12 +653,9 @@ fSearchFunction(this.folderList()); aTimeouts.sort(function(a, b) { - if (a[0] < b[0]) - { + if (a[0] < b[0]) { return -1; - } - else if (a[0] > b[0]) - { + } else if (a[0] > b[0]) { return 1; } diff --git a/dev/Storage/User/Remote.js b/dev/Storage/User/Remote.js index c7373a0ae..9ab486695 100644 --- a/dev/Storage/User/Remote.js +++ b/dev/Storage/User/Remote.js @@ -221,6 +221,35 @@ this.defaultRequest(fCallback, 'AccountsAndIdentities'); }; + /** + * @param {?Function} fCallback + */ + RemoteUserStorage.prototype.accountsCounts = function (fCallback) + { + this.defaultRequest(fCallback, 'AccountsCounts'); + }; + + /** + * @param {?Function} fCallback + */ + RemoteUserStorage.prototype.filtersSave = function (fCallback, aFilters) + { + this.defaultRequest(fCallback, 'FiltersSave', { + 'Filters': _.map(aFilters, function (oItem) { + return oItem.toJson(); + }) + }); + }; + + /** + * @param {?Function} fCallback + */ + RemoteUserStorage.prototype.filtersGet = function (fCallback) + { + this.defaultRequest(fCallback, 'Filters', { + }); + }; + /** * @param {?Function} fCallback * @param {string} sFolderFullNameRaw diff --git a/dev/Styles/SettingsFilters.less b/dev/Styles/SettingsFilters.less index a1c07b5a4..9bcf8e558 100644 --- a/dev/Styles/SettingsFilters.less +++ b/dev/Styles/SettingsFilters.less @@ -55,5 +55,11 @@ cursor: pointer; opacity: 0.5; } + + &.ui-sortable-helper { + .delete-filter { + display: none; + } + } } } diff --git a/dev/Styles/SystemDropDown.less b/dev/Styles/SystemDropDown.less index 404be4649..8aa437661 100644 --- a/dev/Styles/SystemDropDown.less +++ b/dev/Styles/SystemDropDown.less @@ -1,34 +1,60 @@ - -.b-system-drop-down { - - .b-toolbar { - position: absolute; - top: 0; - right: 0; - height: 30px; - padding: 10px @rlLowMargin; - z-index: 103; - } - - .e-facebook-name { - display: inline-block; - padding-top: 4px; - } - - .btn.system-dropdown { - padding-left: 10px; - padding-right: 10px; - } - - .button-fb-logout { - margin: 5px; - } - - .email-title { - display: inline-block; - max-width: 200px; - text-align: left; - text-overflow: ellipsis; - overflow: hidden; - } + +.b-system-drop-down { + + .b-toolbar { + position: absolute; + top: 0; + right: 0; + height: 30px; + padding: 10px @rlLowMargin; + z-index: 103; + } + + .e-facebook-name { + display: inline-block; + padding-top: 4px; + } + + .btn.system-dropdown { + padding-left: 10px; + padding-right: 10px; + } + + .button-fb-logout { + margin: 5px; + } + + .email-title { + display: inline-block; + max-width: 200px; + text-align: left; + text-overflow: ellipsis; + overflow: hidden; + margin-right: 28px; + vertical-align: middle; + } + + .account-item { + + .icon-ok { + display: none; + } + + &.current { + .icon-ok { + display: inline-block; + } + .icon-user { + display: none; + } + } + } + + .counter { + display: inline-block; + } + + .g-ui-menu .e-link.account-item { + padding-right: 5px; + } } \ No newline at end of file diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index e0ef5a387..ed03d56c7 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -1180,14 +1180,13 @@ if (window.google && window.google.picker) { var drivePicker = new window.google.picker.PickerBuilder() - .addView( - new window.google.picker.DocsView() - .setIncludeFolders(true) - ) + // .addView(window.google.picker.ViewId.FOLDERS) + .addView(window.google.picker.ViewId.DOCS) .setAppId(Settings.settingsGet('GoogleClientID')) .setOAuthToken(oOauthToken.access_token) .setCallback(_.bind(self.driveCallback, self, oOauthToken.access_token)) .enableFeature(window.google.picker.Feature.NAV_HIDDEN) + // .setOrigin(window.location.protocol + '//' + window.location.host) .build() ; @@ -1205,14 +1204,9 @@ window.gapi.load('auth', {'callback': function () { - var oAuthToken = window.gapi.auth.getToken(); - if (!oAuthToken) - { - window.gapi.auth.authorize({ - 'client_id': Settings.settingsGet('GoogleClientID'), - 'scope': 'https://www.googleapis.com/auth/drive.readonly', - 'immediate': true - }, function (oAuthResult) { + var + oAuthToken = window.gapi.auth.getToken(), + fResult = function (oAuthResult) { if (oAuthResult && !oAuthResult.error) { var oAuthToken = window.gapi.auth.getToken(); @@ -1220,23 +1214,29 @@ { self.driveCreatePiker(oAuthToken); } + + return true; } - else + + return false; + } + ; + + if (!oAuthToken) + { + window.gapi.auth.authorize({ + 'client_id': Settings.settingsGet('GoogleClientID'), + 'scope': 'https://www.googleapis.com/auth/drive.readonly', + 'immediate': true + }, function (oAuthResult) { + + if (!fResult(oAuthResult)) { window.gapi.auth.authorize({ 'client_id': Settings.settingsGet('GoogleClientID'), 'scope': 'https://www.googleapis.com/auth/drive.readonly', 'immediate': false - }, function (oAuthResult) { - if (oAuthResult && !oAuthResult.error) - { - var oAuthToken = window.gapi.auth.getToken(); - if (oAuthToken) - { - self.driveCreatePiker(oAuthToken); - } - } - }); + }, fResult); } }); } diff --git a/dev/View/Popup/Filter.js b/dev/View/Popup/Filter.js index ede447e58..1f4570261 100644 --- a/dev/View/Popup/Filter.js +++ b/dev/View/Popup/Filter.js @@ -8,6 +8,7 @@ ko = require('ko'), Consts = require('Common/Consts'), + Enums = require('Common/Enums'), Utils = require('Common/Utils'), Data = require('Storage/User/Data'), @@ -24,28 +25,102 @@ { AbstractView.call(this, 'Popups', 'PopupsFilter'); + this.isNew = ko.observable(true); + + this.fTrueCallback = null; this.filter = ko.observable(null); this.selectedFolderValue = ko.observable(Consts.Values.UnuseOptionValue); this.folderSelectList = Data.folderMenuForMove; this.defautOptionsAfterRender = Utils.defautOptionsAfterRender; + this.saveFilter = Utils.createCommand(this, function () { + + if (this.filter()) + { + if ('' === this.filter().name()) + { + this.filter().name.error(true); + return false; + } + + if (this.fTrueCallback) + { + this.fTrueCallback(this.filter()); + } + + if (this.modalVisibility()) + { + Utils.delegateRun(this, 'closeCommand'); + } + } + + return true; + }); + + this.actionTypeOptions = [ + {'id': Enums.FiltersAction.None, 'name': 'None @i18n'}, + {'id': Enums.FiltersAction.Move, 'name': ' Move to @i18n'}, +// {'id': Enums.FiltersAction.Forward, 'name': 'Forward to @i18n'}, + {'id': Enums.FiltersAction.Discard, 'name': 'Discard @i18n'} + ]; + + this.fieldOptions = [ + {'id': Enums.FilterConditionField.From, 'name': 'From @i18n'}, + {'id': Enums.FilterConditionField.Recipient, 'name': 'Recipient (To or CC) @i18n'}, + {'id': Enums.FilterConditionField.Subject, 'name': 'Subject @i18n'} + ]; + + this.typeOptions = [ + {'id': Enums.FilterConditionType.EqualTo, 'name': 'Equal To @i18n'}, + {'id': Enums.FilterConditionType.NotEqualTo, 'name': 'Not Equal To @i18n'}, + {'id': Enums.FilterConditionType.Contains, 'name': 'Contains @i18n'}, + {'id': Enums.FilterConditionType.NotContains, 'name': 'Not Contains @i18n'} + ]; + kn.constructorEnd(this); } kn.extendAsViewModel(['View/Popup/Filter', 'PopupsFilterViewModel'], FilterPopupView); _.extend(FilterPopupView.prototype, AbstractView.prototype); - FilterPopupView.prototype.clearPopup = function () + FilterPopupView.prototype.removeCondition = function (oConditionToDelete) { - // TODO + if (this.filter()) + { + this.filter().removeCondition(oConditionToDelete); + } }; - FilterPopupView.prototype.onShow = function (oFilter) + FilterPopupView.prototype.clearPopup = function () + { + this.isNew(true); + + this.fTrueCallback = null; + this.filter(null); + }; + + FilterPopupView.prototype.onShow = function (oFilter, fTrueCallback, bEdit) { this.clearPopup(); + this.fTrueCallback = fTrueCallback; this.filter(oFilter); + + this.isNew(!bEdit); + + if (!bEdit && oFilter) + { + oFilter.name.focused(true); + } + }; + + FilterPopupView.prototype.onFocus = function () + { + if (this.isNew() && this.filter()) + { + this.filter().name.focused(true); + } }; module.exports = FilterPopupView; diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index fb92be532..ebea101b0 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -319,7 +319,7 @@ } var - oEl = Utils.draggeblePlace(), + oEl = Utils.draggablePlace(), aUids = Data.messageListCheckedOrSelectedUidsWithSubMails() ; diff --git a/gulpfile.js b/gulpfile.js index c6a8e3d70..788788133 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -144,6 +144,7 @@ cfg.paths.js = { 'vendors/underscore/1.6.0/underscore-min.js', 'vendors/jquery/jquery-1.11.1.min.js', 'vendors/jquery-ui/js/jquery-ui-1.10.3.custom.min.js', +// 'vendors/jquery-ui.touch-punch/jquery.ui.touch-punch.min.js', 'vendors/jquery-cookie/jquery.cookie-1.4.0.min.js', 'vendors/jquery-finger/jquery.finger.min.js', 'vendors/jquery-mousewheel/jquery.mousewheel-3.1.4.min.js', @@ -161,6 +162,7 @@ cfg.paths.js = { 'vendors/routes/crossroads.min.js', 'vendors/knockout/knockout-3.2.0.js', 'vendors/knockout-projections/knockout-projections-1.0.0.min.js', + 'vendors/knockout-sortable/knockout-sortable.min.js', 'vendors/ssm/ssm.min.js', 'vendors/jua/jua.min.js', 'vendors/Autolinker/Autolinker.min.js', @@ -169,7 +171,6 @@ cfg.paths.js = { 'vendors/jsencrypt/jsencrypt.min.js', 'vendors/keymaster/keymaster.min.js', 'vendors/ifvisible/ifvisible.min.js', -// 'vendors/jquery-magnific-popup/jquery.magnific-popup.min.js', 'vendors/bootstrap/js/bootstrap.min.js' ] }, diff --git a/package.json b/package.json index 6e52b4a3a..f9d61a4f0 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "RainLoop", "title": "RainLoop Webmail", "version": "1.7.0", - "release": "210", + "release": "211", "description": "Simple, modern & fast web-based email client", "homepage": "http://rainloop.net", "main": "gulpfile.js", diff --git a/rainloop/v/0.0.0/app/src/RainLoop/Actions.php b/rainloop/v/0.0.0/app/src/RainLoop/Actions.php index 42d42470d..1ccab3ea0 100644 --- a/rainloop/v/0.0.0/app/src/RainLoop/Actions.php +++ b/rainloop/v/0.0.0/app/src/RainLoop/Actions.php @@ -2031,6 +2031,28 @@ class Actions return false; } + /** + * @return array + * + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoFilters() + { + return $this->TrueResponse(__FUNCTION__); + } + + + /** + * @return array + * + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoFiltersSave() + { + sleep(2); + return $this->TrueResponse(__FUNCTION__); + } + /** * @return array * @@ -2246,6 +2268,35 @@ class Actions )); } + /** + * @return array + * + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoAccountsCounts() + { + $oAccount = $this->getAccountFromToken(); + + $aCounts = array(); + if ($this->Config()->Get('webmail', 'allow_additional_accounts', true)) + { + $mAccounts = $this->GetAccounts($oAccount); + foreach ($mAccounts as $sEmail => $sHash) + { + $aCounts[] = array(\MailSo\Base\Utils::IdnToUtf8($sEmail), 0); + } + } + else + { + $aCounts[] = array(\MailSo\Base\Utils::IdnToUtf8($oAccount->Email()), 0); + } + + return $this->DefaultResponse(__FUNCTION__, array( + 'Complete' => true, + 'Counts' => $aCounts + )); + } + /** * @return array * diff --git a/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsLicensing.html b/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsLicensing.html index ff6f28910..8c7b89320 100644 --- a/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsLicensing.html +++ b/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsLicensing.html @@ -65,8 +65,8 @@

- Lifetime (Premium) - Premium + Premium + (Lifetime)

Subscription expires: diff --git a/rainloop/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html b/rainloop/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html index f6e549ca0..55eb77abd 100644 --- a/rainloop/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html +++ b/rainloop/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html @@ -21,7 +21,7 @@

- IMAP + IMAP
@@ -73,7 +73,7 @@   - Sieve (Filetrs) configuration + Sieve configuration
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 f7316b194..7a261e52b 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 @@ -4,29 +4,34 @@
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFilters.html b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFilters.html index e23d49d31..53d2846d5 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFilters.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFilters.html @@ -5,15 +5,57 @@
-
- -
+ +
+
+
+
+ +    + +
+ + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
-
- - -    - -
\ No newline at end of file diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionNoValue.html b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionNoValue.html index c70956749..7ca0fc7c3 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionNoValue.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionNoValue.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionValueAsFolders.html b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionValueAsFolders.html index 5f6aa0c29..e4addf7af 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionValueAsFolders.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionValueAsFolders.html @@ -1,4 +1,4 @@ - +   \ No newline at end of file diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionWithValue.html b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionWithValue.html index 48f742fd0..25c6e505f 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionWithValue.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionWithValue.html @@ -1,3 +1,3 @@ - +   \ No newline at end of file diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersConditionDefault.html b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersConditionDefault.html index 1a203ca2d..61f6978de 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersConditionDefault.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersConditionDefault.html @@ -1,9 +1,9 @@ - +   - +     - + \ No newline at end of file diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SystemDropDown.html b/rainloop/v/0.0.0/app/templates/Views/User/SystemDropDown.html index 7338db65d..c7bc2aca3 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/SystemDropDown.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/SystemDropDown.html @@ -11,19 +11,26 @@