mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
Split Sieve/Filters code from app.js so that i can work on the new Sieve GUI
This commit is contained in:
parent
ec6a79d9d6
commit
d6dc4d291c
23 changed files with 460 additions and 490 deletions
16
README.md
16
README.md
|
|
@ -139,23 +139,25 @@ RainLoop 1.15 vs SnappyMail
|
||||||
|
|
||||||
|js/* |RainLoop |Snappy |
|
|js/* |RainLoop |Snappy |
|
||||||
|--------------- |--------: |--------: |
|
|--------------- |--------: |--------: |
|
||||||
|admin.js |2.158.025 | 79.040 |
|
|admin.js |2.158.025 | 79.018 |
|
||||||
|app.js |4.215.733 | 431.641 |
|
|app.js |4.215.733 | 407.680 |
|
||||||
|boot.js | 672.433 | 1.996 |
|
|boot.js | 672.433 | 1.996 |
|
||||||
|libs.js | 647.679 | 200.131 |
|
|libs.js | 647.679 | 200.131 |
|
||||||
|
|sieve.js | 0 | 30.809 |
|
||||||
|polyfills.js | 325.908 | 0 |
|
|polyfills.js | 325.908 | 0 |
|
||||||
|serviceworker.js | 0 | 285 |
|
|serviceworker.js | 0 | 285 |
|
||||||
|TOTAL |8.019.778 | 713.093 |
|
|TOTAL |8.019.778 | 719.919 |
|
||||||
|
|
||||||
|js/min/* |RainLoop |Snappy |RL gzip |SM gzip |RL brotli |SM brotli |
|
|js/min/* |RainLoop |Snappy |RL gzip |SM gzip |RL brotli |SM brotli |
|
||||||
|--------------- |--------: |--------: |------: |------: |--------: |--------: |
|
|--------------- |--------: |--------: |------: |------: |--------: |--------: |
|
||||||
|admin.min.js | 255.514 | 39.272 | 73.899 | 13.076 | 60.674 | 11.727 |
|
|admin.min.js | 255.514 | 39.256 | 73.899 | 13.076 | 60.674 | 11.702 |
|
||||||
|app.min.js | 516.000 | 207.038 |140.430 | 66.265 |110.657 | 56.788 |
|
|app.min.js | 516.000 | 194.277 |140.430 | 62.348 |110.657 | 53.485 |
|
||||||
|boot.min.js | 66.456 | 1.230 | 22.553 | 768 | 20.043 | 619 |
|
|boot.min.js | 66.456 | 1.230 | 22.553 | 768 | 20.043 | 619 |
|
||||||
|libs.min.js | 574.626 | 96.201 |177.280 | 35.522 |151.855 | 31.746 |
|
|libs.min.js | 574.626 | 96.201 |177.280 | 35.522 |151.855 | 31.746 |
|
||||||
|
|sieve.min.js | 0 | 15.009 | 0 | 5.228 | 0 | 4.702 |
|
||||||
|polyfills.min.js | 32.608 | 0 | 11.315 | 0 | 10.072 | 0 |
|
|polyfills.min.js | 32.608 | 0 | 11.315 | 0 | 10.072 | 0 |
|
||||||
|TOTAL |1.445.204 | 343.741 |425.477 |115.631 |353.301 |100.880 |
|
|TOTAL |1.445.204 | 345.973 |425.477 |116.942 |353.301 |102.254 |
|
||||||
|TOTAL (no admin) |1.189.690 | 304.469 |351.061 |102.555 |292.627 | 89.153 |
|
|TOTAL (no admin) |1.189.690 | 306.717 |351.061 |103.866 |292.627 | 90.552 |
|
||||||
|
|
||||||
For a user its around 69% smaller and faster than traditional RainLoop.
|
For a user its around 69% smaller and faster than traditional RainLoop.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,8 @@ fetchFolderInformation = (fCallback, folder, list = []) => {
|
||||||
/**
|
/**
|
||||||
* @param {Array=} aDisabled
|
* @param {Array=} aDisabled
|
||||||
* @param {Array=} aHeaderLines
|
* @param {Array=} aHeaderLines
|
||||||
* @param {Function=} fDisableCallback
|
|
||||||
* @param {Function=} fRenameCallback
|
* @param {Function=} fRenameCallback
|
||||||
|
* @param {Function=} fDisableCallback
|
||||||
* @param {boolean=} bNoSelectSelectable Used in FolderCreatePopupView
|
* @param {boolean=} bNoSelectSelectable Used in FolderCreatePopupView
|
||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ class AbstractView {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
onBuild() {}
|
onBuild() {}
|
||||||
onBeforeShow() {} // Happens before: hidden = false
|
beforeShow() {} // Happens before: hidden = false
|
||||||
onShow() {} // Happens after: hidden = false
|
onShow() {} // Happens after: hidden = false
|
||||||
onHide() {}
|
onHide() {}
|
||||||
*/
|
*/
|
||||||
|
|
@ -73,7 +73,7 @@ export class AbstractViewPopup extends AbstractView
|
||||||
onClose() {}
|
onClose() {}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
onBeforeShow() {} // Happens before showModal()
|
beforeShow() {} // Happens before showModal()
|
||||||
onShow() {} // Happens after showModal()
|
onShow() {} // Happens after showModal()
|
||||||
afterShow() {} // Happens after showModal() animation transitionend
|
afterShow() {} // Happens after showModal() animation transitionend
|
||||||
onHide() {} // Happens before animation transitionend
|
onHide() {} // Happens before animation transitionend
|
||||||
|
|
@ -112,7 +112,7 @@ export class AbstractViewSettings
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
onBuild(viewModelDom) {}
|
onBuild(viewModelDom) {}
|
||||||
onBeforeShow() {}
|
beforeShow() {}
|
||||||
onShow() {}
|
onShow() {}
|
||||||
onHide() {}
|
onHide() {}
|
||||||
viewModelDom
|
viewModelDom
|
||||||
|
|
|
||||||
|
|
@ -214,7 +214,7 @@ const
|
||||||
vmScreen.onShow && vmScreen.onShow();
|
vmScreen.onShow && vmScreen.onShow();
|
||||||
|
|
||||||
forEachViewModel(vmScreen, (vm, dom) => {
|
forEachViewModel(vmScreen, (vm, dom) => {
|
||||||
vm.onBeforeShow && vm.onBeforeShow();
|
vm.beforeShow && vm.beforeShow();
|
||||||
dom.hidden = false;
|
dom.hidden = false;
|
||||||
vm.onShow && vm.onShow();
|
vm.onShow && vm.onShow();
|
||||||
autofocus(dom);
|
autofocus(dom);
|
||||||
|
|
@ -243,7 +243,7 @@ export const
|
||||||
if (vm) {
|
if (vm) {
|
||||||
params = params || [];
|
params = params || [];
|
||||||
|
|
||||||
vm.onBeforeShow && vm.onBeforeShow(...params);
|
vm.beforeShow && vm.beforeShow(...params);
|
||||||
|
|
||||||
vm.modalVisible(true);
|
vm.modalVisible(true);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ export class AbstractSettingsScreen extends AbstractScreen {
|
||||||
this.oCurrentSubScreen = settingsScreen;
|
this.oCurrentSubScreen = settingsScreen;
|
||||||
|
|
||||||
// show
|
// show
|
||||||
settingsScreen.onBeforeShow && settingsScreen.onBeforeShow();
|
settingsScreen.beforeShow && settingsScreen.beforeShow();
|
||||||
settingsScreen.viewModelDom.hidden = false;
|
settingsScreen.viewModelDom.hidden = false;
|
||||||
settingsScreen.onShow && settingsScreen.onShow();
|
settingsScreen.onShow && settingsScreen.onShow();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,6 @@ import { SystemDropDownUserView } from 'View/User/SystemDropDown';
|
||||||
import { SettingsMenuUserView } from 'View/User/Settings/Menu';
|
import { SettingsMenuUserView } from 'View/User/Settings/Menu';
|
||||||
import { SettingsPaneUserView } from 'View/User/Settings/Pane';
|
import { SettingsPaneUserView } from 'View/User/Settings/Pane';
|
||||||
|
|
||||||
//import { staticLink } from 'Common/Links';
|
|
||||||
|
|
||||||
export class SettingsUserScreen extends AbstractSettingsScreen {
|
export class SettingsUserScreen extends AbstractSettingsScreen {
|
||||||
constructor() {
|
constructor() {
|
||||||
super([SystemDropDownUserView, SettingsMenuUserView, SettingsPaneUserView]);
|
super([SystemDropDownUserView, SettingsMenuUserView, SettingsPaneUserView]);
|
||||||
|
|
@ -41,7 +39,6 @@ export class SettingsUserScreen extends AbstractSettingsScreen {
|
||||||
|
|
||||||
if (SettingsCapa('Sieve')) {
|
if (SettingsCapa('Sieve')) {
|
||||||
views.push(UserSettingsFilters);
|
views.push(UserSettingsFilters);
|
||||||
// rl.loadScript(staticLink('js/sieve.js')).then(() => 0).catch(e => console.error(e));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SettingsCapa('AutoLogout') || SettingsCapa('OpenPGP') || SettingsCapa('GnuPG')) {
|
if (SettingsCapa('AutoLogout') || SettingsCapa('OpenPGP') || SettingsCapa('GnuPG')) {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export class AdminSettingsConfig /*extends AbstractViewSettings*/ {
|
||||||
this.config = ko.observableArray();
|
this.config = ko.observableArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeShow() {
|
beforeShow() {
|
||||||
Remote.request('AdminSettingsGet', (iError, data) => {
|
Remote.request('AdminSettingsGet', (iError, data) => {
|
||||||
if (!iError) {
|
if (!iError) {
|
||||||
const cfg = [],
|
const cfg = [],
|
||||||
|
|
|
||||||
|
|
@ -1,97 +1,44 @@
|
||||||
import { getNotification } from 'Common/Translator';
|
|
||||||
import { forEachObjectValue } from 'Common/Utils';
|
|
||||||
import { addObservablesTo } from 'External/ko';
|
import { addObservablesTo } from 'External/ko';
|
||||||
|
import { staticLink } from 'Common/Links';
|
||||||
|
import { FolderUserStore } from 'Stores/User/Folder';
|
||||||
|
|
||||||
import { SieveUserStore } from 'Stores/User/Sieve';
|
//export class UserSettingsFilters /*extends AbstractViewSettings*/ {
|
||||||
import Remote from 'Remote/User/Fetch';
|
|
||||||
|
|
||||||
import { SieveScriptModel } from 'Model/SieveScript';
|
|
||||||
|
|
||||||
import { showScreenPopup } from 'Knoin/Knoin';
|
|
||||||
|
|
||||||
import { SieveScriptPopupView } from 'View/Popup/SieveScript';
|
|
||||||
|
|
||||||
export class UserSettingsFilters /*extends AbstractViewSettings*/ {
|
export class UserSettingsFilters /*extends AbstractViewSettings*/ {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.scripts = SieveUserStore.scripts;
|
this.scripts = ko.observableArray();
|
||||||
this.loading = ko.observable(false).extend({ debounce: 200 });
|
this.loading = ko.observable(true).extend({ debounce: 200 });
|
||||||
|
|
||||||
addObservablesTo(this, {
|
addObservablesTo(this, {
|
||||||
serverError: false,
|
serverError: false,
|
||||||
serverErrorDesc: ''
|
serverErrorDesc: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
|
rl.loadScript(staticLink('js/sieve.js')).then(() => {
|
||||||
|
const Sieve = window.Sieve;
|
||||||
|
Sieve.folderList = FolderUserStore.folderList;
|
||||||
|
Sieve.serverError.subscribe(value => this.serverError(value));
|
||||||
|
Sieve.serverErrorDesc.subscribe(value => this.serverErrorDesc(value));
|
||||||
|
Sieve.loading.subscribe(value => this.loading(value));
|
||||||
|
Sieve.scripts.subscribe(value => this.scripts(value));
|
||||||
|
Sieve.updateList();
|
||||||
|
}).catch(e => console.error(e));
|
||||||
|
|
||||||
this.scriptForDeletion = ko.observable(null).askDeleteHelper();
|
this.scriptForDeletion = ko.observable(null).askDeleteHelper();
|
||||||
}
|
}
|
||||||
|
|
||||||
setError(text) {
|
|
||||||
this.serverError(true);
|
|
||||||
this.serverErrorDesc(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateList() {
|
|
||||||
if (!this.loading()) {
|
|
||||||
this.loading(true);
|
|
||||||
this.serverError(false);
|
|
||||||
|
|
||||||
Remote.request('Filters', (iError, data) => {
|
|
||||||
this.loading(false);
|
|
||||||
this.scripts([]);
|
|
||||||
|
|
||||||
if (iError) {
|
|
||||||
SieveUserStore.capa([]);
|
|
||||||
this.setError(getNotification(iError));
|
|
||||||
} else {
|
|
||||||
SieveUserStore.capa(data.Result.Capa);
|
|
||||||
/*
|
|
||||||
this.scripts(
|
|
||||||
data.Result.Scripts.map(aItem => SieveScriptModel.reviveFromJson(aItem)).filter(v => v)
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
forEachObjectValue(data.Result.Scripts, value => {
|
|
||||||
value = SieveScriptModel.reviveFromJson(value);
|
|
||||||
value && this.scripts.push(value)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addScript() {
|
addScript() {
|
||||||
showScreenPopup(SieveScriptPopupView, [new SieveScriptModel()]);
|
this.editScript();
|
||||||
}
|
}
|
||||||
|
|
||||||
editScript(script) {
|
editScript(script) {
|
||||||
showScreenPopup(SieveScriptPopupView, [script]);
|
window.Sieve.ScriptView.showModal(script ? [script] : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteScript(script) {
|
deleteScript(script) {
|
||||||
this.serverError(false);
|
window.Sieve.deleteScript(script);
|
||||||
Remote.request('FiltersScriptDelete',
|
|
||||||
(iError, data) => {
|
|
||||||
if (iError) {
|
|
||||||
this.setError((data && data.ErrorMessageAdditional) || getNotification(iError));
|
|
||||||
} else {
|
|
||||||
this.scripts.remove(script);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{name:script.name()}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleScript(script) {
|
toggleScript(script) {
|
||||||
let name = script.active() ? '' : script.name();
|
window.Sieve.toggleScript(script);
|
||||||
this.serverError(false);
|
|
||||||
Remote.request('FiltersScriptActivate',
|
|
||||||
(iError, data) => {
|
|
||||||
if (iError) {
|
|
||||||
this.setError((data && data.ErrorMessageAdditional) || iError)
|
|
||||||
} else {
|
|
||||||
this.scripts.forEach(script => script.active(script.name() === name));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{name:name}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onBuild(oDom) {
|
onBuild(oDom) {
|
||||||
|
|
@ -103,6 +50,6 @@ export class UserSettingsFilters /*extends AbstractViewSettings*/ {
|
||||||
}
|
}
|
||||||
|
|
||||||
onShow() {
|
onShow() {
|
||||||
this.updateList();
|
window.Sieve && window.Sieve.updateList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
import { getNotification } from 'Common/Translator';
|
|
||||||
|
|
||||||
import Remote from 'Remote/User/Fetch';
|
|
||||||
|
|
||||||
//export class UserSettingsFilters /*extends AbstractViewSettings*/ {
|
|
||||||
export class UserSettingsSieve /*extends AbstractViewSettings*/ {
|
|
||||||
constructor() {
|
|
||||||
const Sieve = window.Sieve;
|
|
||||||
this.scripts = Sieve.scripts;
|
|
||||||
this.loading = Sieve.loading;
|
|
||||||
this.serverError = Sieve.serverError;
|
|
||||||
this.serverErrorDesc = Sieve.serverErrorDesc;
|
|
||||||
this.scriptForDeletion = ko.observable(null).askDeleteHelper();
|
|
||||||
}
|
|
||||||
|
|
||||||
setError(text) {
|
|
||||||
this.serverError(true);
|
|
||||||
this.serverErrorDesc(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateList() {
|
|
||||||
window.Sieve.updateList();
|
|
||||||
}
|
|
||||||
|
|
||||||
addScript() {
|
|
||||||
window.Sieve.ScriptView.showModal();
|
|
||||||
}
|
|
||||||
|
|
||||||
editScript(script) {
|
|
||||||
window.Sieve.ScriptView.showModal([script]);
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteScript(script) {
|
|
||||||
this.serverError(false);
|
|
||||||
Remote.request('FiltersScriptDelete',
|
|
||||||
(iError, data) => {
|
|
||||||
if (iError) {
|
|
||||||
this.setError((data && data.ErrorMessageAdditional) || getNotification(iError));
|
|
||||||
} else {
|
|
||||||
this.scripts.remove(script);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{name:script.name()}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleScript(script) {
|
|
||||||
let name = script.active() ? '' : script.name();
|
|
||||||
this.serverError(false);
|
|
||||||
Remote.request('FiltersScriptActivate',
|
|
||||||
(iError, data) => {
|
|
||||||
if (iError) {
|
|
||||||
this.setError((data && data.ErrorMessageAdditional) || iError)
|
|
||||||
} else {
|
|
||||||
this.scripts.forEach(script => script.active(script.name() === name));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{name:name}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
onBuild(oDom) {
|
|
||||||
oDom.addEventListener('click', event => {
|
|
||||||
const el = event.target.closestWithin('.script-item .e-action', oDom),
|
|
||||||
script = el && ko.dataFor(el);
|
|
||||||
script && this.editScript(script);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onShow() {
|
|
||||||
this.updateList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
121
dev/Sieve/Model/Abstract.js
Normal file
121
dev/Sieve/Model/Abstract.js
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
import { forEachObjectValue, forEachObjectEntry, koComputable } from 'Sieve/Utils';
|
||||||
|
|
||||||
|
function typeCast(curValue, newValue) {
|
||||||
|
if (null != curValue) {
|
||||||
|
switch (typeof curValue)
|
||||||
|
{
|
||||||
|
case 'boolean': return 0 != newValue && !!newValue;
|
||||||
|
case 'number': return isFinite(newValue) ? parseFloat(newValue) : 0;
|
||||||
|
case 'string': return null != newValue ? '' + newValue : '';
|
||||||
|
case 'object':
|
||||||
|
if (curValue.constructor.reviveFromJson) {
|
||||||
|
return curValue.constructor.reviveFromJson(newValue);
|
||||||
|
}
|
||||||
|
if (Array.isArray(curValue) && !Array.isArray(newValue))
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AbstractModel {
|
||||||
|
constructor() {
|
||||||
|
/*
|
||||||
|
if (new.target === AbstractModel) {
|
||||||
|
throw new Error("Can't instantiate AbstractModel!");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
this.disposables = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
addObservables(observables) {
|
||||||
|
forEachObjectEntry(observables, (key, value) =>
|
||||||
|
this[key] || (this[key] = /*isArray(value) ? ko.observableArray(value) :*/ ko.observable(value))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
addComputables(computables) {
|
||||||
|
forEachObjectEntry(computables, (key, fn) => this[key] = koComputable(fn));
|
||||||
|
}
|
||||||
|
|
||||||
|
addSubscribables(subscribables) {
|
||||||
|
forEachObjectEntry(subscribables, (key, fn) => this.disposables.push( this[key].subscribe(fn) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Called by delegateRunOnDestroy */
|
||||||
|
onDestroy() {
|
||||||
|
/** dispose ko subscribables */
|
||||||
|
this.disposables.forEach(disposable => {
|
||||||
|
disposable && typeof disposable.dispose === 'function' && disposable.dispose();
|
||||||
|
});
|
||||||
|
/** clear object entries */
|
||||||
|
// forEachObjectEntry(this, (key, value) => {
|
||||||
|
forEachObjectValue(this, value => {
|
||||||
|
/** clear CollectionModel */
|
||||||
|
let arr = ko.isObservableArray(value) ? value() : value;
|
||||||
|
arr && arr.onDestroy && arr.onDestroy();
|
||||||
|
/** destroy ko.observable/ko.computed? */
|
||||||
|
// dispose(value);
|
||||||
|
/** clear object value */
|
||||||
|
// this[key] = null; // TODO: issue with Contacts view
|
||||||
|
});
|
||||||
|
// this.disposables = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @static
|
||||||
|
* @param {FetchJson} json
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
static validJson(json) {
|
||||||
|
return !!(json && ('Object/'+this.name.replace('Model', '') === json['@Object']));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @static
|
||||||
|
* @param {FetchJson} json
|
||||||
|
* @returns {*Model}
|
||||||
|
*/
|
||||||
|
static reviveFromJson(json) {
|
||||||
|
let obj = this.validJson(json) ? new this() : null;
|
||||||
|
obj && obj.revivePropertiesFromJson(json);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
revivePropertiesFromJson(json) {
|
||||||
|
let model = this.constructor;
|
||||||
|
if (!model.validJson(json)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
forEachObjectEntry(json, (key, value) => {
|
||||||
|
if ('@' !== key[0]) try {
|
||||||
|
key = key[0].toLowerCase() + key.slice(1);
|
||||||
|
switch (typeof this[key])
|
||||||
|
{
|
||||||
|
case 'function':
|
||||||
|
if (ko.isObservable(this[key])) {
|
||||||
|
this[key](typeCast(this[key](), value));
|
||||||
|
// console.log('Observable ' + (typeof this[key]()) + ' ' + (model.name) + '.' + key + ' revived');
|
||||||
|
}
|
||||||
|
// else console.log(model.name + '.' + key + ' is a function');
|
||||||
|
break;
|
||||||
|
case 'boolean':
|
||||||
|
case 'number':
|
||||||
|
case 'object':
|
||||||
|
case 'string':
|
||||||
|
this[key] = typeCast(this[key], value);
|
||||||
|
break;
|
||||||
|
// fall through
|
||||||
|
case 'undefined':
|
||||||
|
default:
|
||||||
|
// console.log((typeof this[key])+' '+(model.name)+'.'+key+' not revived');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(model.name + '.' + key);
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
import { koArrayWithDestroy } from 'External/ko';
|
import { koArrayWithDestroy } from 'Sieve/Utils';
|
||||||
|
|
||||||
import { arrayLength, pString } from 'Common/Utils';
|
//import { getFolderFromCacheList } from 'Common/Cache';
|
||||||
import { i18n } from 'Common/Translator';
|
|
||||||
import { getFolderFromCacheList } from 'Common/Cache';
|
|
||||||
|
|
||||||
import { AccountUserStore } from 'Stores/User/Account';
|
//import { AccountUserStore } from 'Stores/User/Account';
|
||||||
|
|
||||||
import { FilterConditionModel } from 'Model/FilterCondition';
|
import { FilterConditionModel } from 'Sieve/Model/FilterCondition';
|
||||||
import { AbstractModel } from 'Knoin/AbstractModel';
|
import { AbstractModel } from 'Sieve/Model/Abstract';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @enum {string}
|
* @enum {string}
|
||||||
|
|
@ -66,9 +64,10 @@ export class FilterModel extends AbstractModel {
|
||||||
|
|
||||||
this.conditions = koArrayWithDestroy();
|
this.conditions = koArrayWithDestroy();
|
||||||
|
|
||||||
const fGetRealFolderName = (folderFullName) => {
|
const fGetRealFolderName = folderFullName => {
|
||||||
const folder = getFolderFromCacheList(folderFullName);
|
// const folder = getFolderFromCacheList(folderFullName);
|
||||||
return folder ? folder.fullName.replace('.' === folder.delimiter ? /\./ : /[\\/]+/, ' / ') : folderFullName;
|
// return folder ? folder.fullName.replace('.' === folder.delimiter ? /\./ : /[\\/]+/, ' / ') : folderFullName;
|
||||||
|
return folderFullName;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.addComputables({
|
this.addComputables({
|
||||||
|
|
@ -78,23 +77,23 @@ export class FilterModel extends AbstractModel {
|
||||||
|
|
||||||
switch (this.actionType()) {
|
switch (this.actionType()) {
|
||||||
case FilterAction.MoveTo:
|
case FilterAction.MoveTo:
|
||||||
result = i18n(root + 'MOVE_TO', {
|
result = rl.i18n(root + 'MOVE_TO', {
|
||||||
FOLDER: fGetRealFolderName(actionValue)
|
FOLDER: fGetRealFolderName(actionValue)
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case FilterAction.Forward:
|
case FilterAction.Forward:
|
||||||
result = i18n(root + 'FORWARD_TO', {
|
result = rl.i18n(root + 'FORWARD_TO', {
|
||||||
EMAIL: actionValue
|
EMAIL: actionValue
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case FilterAction.Vacation:
|
case FilterAction.Vacation:
|
||||||
result = i18n(root + 'VACATION_MESSAGE');
|
result = rl.i18n(root + 'VACATION_MESSAGE');
|
||||||
break;
|
break;
|
||||||
case FilterAction.Reject:
|
case FilterAction.Reject:
|
||||||
result = i18n(root + 'REJECT');
|
result = rl.i18n(root + 'REJECT');
|
||||||
break;
|
break;
|
||||||
case FilterAction.Discard:
|
case FilterAction.Discard:
|
||||||
result = i18n(root + 'DISCARD');
|
result = rl.i18n(root + 'DISCARD');
|
||||||
break;
|
break;
|
||||||
// no default
|
// no default
|
||||||
}
|
}
|
||||||
|
|
@ -213,7 +212,7 @@ export class FilterModel extends AbstractModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
setRecipients() {
|
setRecipients() {
|
||||||
this.actionValueFourth(AccountUserStore.getEmailAddresses().join(', '));
|
// this.actionValueFourth(AccountUserStore.getEmailAddresses().join(', '));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -224,16 +223,10 @@ export class FilterModel extends AbstractModel {
|
||||||
static reviveFromJson(json) {
|
static reviveFromJson(json) {
|
||||||
const filter = super.reviveFromJson(json);
|
const filter = super.reviveFromJson(json);
|
||||||
if (filter) {
|
if (filter) {
|
||||||
filter.id = pString(json.ID);
|
filter.id = filter.id ? '' + filter.id : '';
|
||||||
|
|
||||||
filter.conditions([]);
|
|
||||||
|
|
||||||
if (arrayLength(json.Conditions)) {
|
|
||||||
filter.conditions(
|
filter.conditions(
|
||||||
json.Conditions.map(aData => FilterConditionModel.reviveFromJson(aData)).filter(v => v)
|
json.Conditions ? json.Conditions.map(aData => FilterConditionModel.reviveFromJson(aData)).filter(v => v) : []
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
filter.actionKeep(0 != json.Keep);
|
filter.actionKeep(0 != json.Keep);
|
||||||
filter.actionNoStop(0 == json.Stop);
|
filter.actionNoStop(0 == json.Stop);
|
||||||
filter.actionMarkAsRead(1 == json.MarkAsRead);
|
filter.actionMarkAsRead(1 == json.MarkAsRead);
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { koComputable } from 'External/ko';
|
import { koComputable } from 'Sieve/Utils';
|
||||||
|
|
||||||
import { AbstractModel } from 'Knoin/AbstractModel';
|
import { AbstractModel } from 'Sieve/Model/Abstract';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @enum {string}
|
* @enum {string}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { AbstractModel } from 'Knoin/AbstractModel';
|
import { AbstractModel } from 'Sieve/Model/Abstract';
|
||||||
import { FilterModel } from 'Model/Filter';
|
import { FilterModel } from 'Sieve/Model/Filter';
|
||||||
import { arrayLength, pString, b64EncodeJSON } from 'Common/Utils';
|
import { koArrayWithDestroy } from 'Sieve/Utils';
|
||||||
import { koArrayWithDestroy } from 'External/ko';
|
|
||||||
|
|
||||||
const SIEVE_FILE_NAME = 'rainloop.user';
|
const SIEVE_FILE_NAME = 'rainloop.user';
|
||||||
|
|
||||||
|
|
@ -153,12 +152,12 @@ function filtersToSieveScript(filters)
|
||||||
subject = ':subject ' + quote(StripSpaces(paramValue)) + ' ';
|
subject = ':subject ' + quote(StripSpaces(paramValue)) + ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
paramValue = pString(filter.actionValueThird()).trim();
|
paramValue = (filter.actionValueThird() || '').trim();
|
||||||
if (paramValue.length) {
|
if (paramValue.length) {
|
||||||
days = Math.max(1, parseInt(paramValue, 10));
|
days = Math.max(1, parseInt(paramValue, 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
paramValue = pString(filter.actionValueFourth()).trim()
|
paramValue = (filter.actionValueFourth() || '').trim()
|
||||||
if (paramValue.length) {
|
if (paramValue.length) {
|
||||||
paramValue = paramValue.split(',').map(email =>
|
paramValue = paramValue.split(',').map(email =>
|
||||||
email.trim().length ? quote(email) : ''
|
email.trim().length ? quote(email) : ''
|
||||||
|
|
@ -214,7 +213,7 @@ function filtersToSieveScript(filters)
|
||||||
'/*',
|
'/*',
|
||||||
'BEGIN:FILTER:' + filter.id,
|
'BEGIN:FILTER:' + filter.id,
|
||||||
'BEGIN:HEADER',
|
'BEGIN:HEADER',
|
||||||
b64EncodeJSON(filter.toJson()).match(split).join(eol) + 'END:HEADER',
|
btoa(unescape(encodeURIComponent(JSON.stringify(filter.toJson())))).match(split).join(eol) + 'END:HEADER',
|
||||||
'*/',
|
'*/',
|
||||||
filter.enabled() ? '' : '/* @Filter is disabled ',
|
filter.enabled() ? '' : '/* @Filter is disabled ',
|
||||||
filterToString(filter, require),
|
filterToString(filter, require),
|
||||||
|
|
@ -317,7 +316,7 @@ export class SieveScriptModel extends AbstractModel
|
||||||
if (script) {
|
if (script) {
|
||||||
if (script.allowFilters()) {
|
if (script.allowFilters()) {
|
||||||
script.filters(
|
script.filters(
|
||||||
arrayLength(json.filters)
|
Array.isArray(json.filters) && json.filters.length
|
||||||
? json.filters.map(aData => FilterModel.reviveFromJson(aData)).filter(v => v)
|
? json.filters.map(aData => FilterModel.reviveFromJson(aData)).filter(v => v)
|
||||||
: sieveScriptToFilters(script.body())
|
: sieveScriptToFilters(script.body())
|
||||||
);
|
);
|
||||||
|
|
@ -1,4 +1,89 @@
|
||||||
|
import { SieveScriptModel } from 'Sieve/Model/Script';
|
||||||
|
|
||||||
export const
|
export const
|
||||||
|
// import { i18n } from 'Common/Translator';
|
||||||
|
i18n = rl.i18n,
|
||||||
|
|
||||||
|
// import { forEachObjectValue, forEachObjectEntry } from 'Common/Utils';
|
||||||
|
forEachObjectValue = (obj, fn) => Object.values(obj).forEach(fn),
|
||||||
|
forEachObjectEntry = (obj, fn) => Object.entries(obj).forEach(([key, value]) => fn(key, value)),
|
||||||
|
|
||||||
|
// import { koArrayWithDestroy } from 'External/ko';
|
||||||
|
// With this we don't need delegateRunOnDestroy
|
||||||
|
koArrayWithDestroy = data => {
|
||||||
|
data = ko.observableArray(data);
|
||||||
|
data.subscribe(changes =>
|
||||||
|
changes.forEach(item =>
|
||||||
|
'deleted' === item.status && null == item.moved && item.value.onDestroy && item.value.onDestroy()
|
||||||
|
)
|
||||||
|
, data, 'arrayChange');
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
|
||||||
|
// import { koComputable } from 'External/ko';
|
||||||
|
koComputable = fn => ko.computed(fn, {'pure':true}),
|
||||||
|
|
||||||
arrayToString = (arr, separator) =>
|
arrayToString = (arr, separator) =>
|
||||||
arr.map(item => item.toString ? item.toString() : item).join(separator);
|
arr.map(item => item.toString ? item.toString() : item).join(separator),
|
||||||
|
/*
|
||||||
|
getNotificationMessage = code => {
|
||||||
|
let key = getKeyByValue(Notification, code);
|
||||||
|
return key ? I18N_DATA.NOTIFICATIONS[i18nKey(key).replace('_NOTIFICATION', '_ERROR')] : '';
|
||||||
|
rl.i18n('NOTIFICATIONS/')
|
||||||
|
},
|
||||||
|
getNotification = (code, message = '', defCode = 0) => {
|
||||||
|
code = parseInt(code, 10) || 0;
|
||||||
|
if (Notification.ClientViewError === code && message) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getNotificationMessage(code)
|
||||||
|
|| getNotificationMessage(parseInt(defCode, 10))
|
||||||
|
|| '';
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
getNotification = code => 'ERROR ' + code,
|
||||||
|
|
||||||
|
Remote = rl.app.Remote,
|
||||||
|
|
||||||
|
// capabilities
|
||||||
|
capa = ko.observableArray(),
|
||||||
|
|
||||||
|
// Sieve scripts SieveScriptModel
|
||||||
|
scripts = koArrayWithDestroy(),
|
||||||
|
|
||||||
|
loading = ko.observable(false),
|
||||||
|
serverError = ko.observable(false),
|
||||||
|
serverErrorDesc = ko.observable(''),
|
||||||
|
setError = text => {
|
||||||
|
serverError(true);
|
||||||
|
serverErrorDesc(text);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateList = () => {
|
||||||
|
if (!loading()) {
|
||||||
|
loading(true);
|
||||||
|
serverError(false);
|
||||||
|
|
||||||
|
Remote.request('Filters', (iError, data) => {
|
||||||
|
loading(false);
|
||||||
|
scripts([]);
|
||||||
|
|
||||||
|
if (iError) {
|
||||||
|
capa([]);
|
||||||
|
setError(getNotification(iError));
|
||||||
|
} else {
|
||||||
|
capa(data.Result.Capa);
|
||||||
|
/*
|
||||||
|
scripts(
|
||||||
|
data.Result.Scripts.map(aItem => SieveScriptModel.reviveFromJson(aItem)).filter(v => v)
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
forEachObjectValue(data.Result.Scripts, value => {
|
||||||
|
value = SieveScriptModel.reviveFromJson(value);
|
||||||
|
value && scripts.push(value)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,77 @@
|
||||||
import ko from 'ko';
|
import { FilterAction } from 'Sieve/Model/Filter';
|
||||||
import { koComputable } from 'External/ko';
|
import { FilterConditionField, FilterConditionType } from 'Sieve/Model/FilterCondition';
|
||||||
|
/*
|
||||||
|
import { SettingsUserStore } from 'Stores/User/Settings';
|
||||||
|
*/
|
||||||
|
|
||||||
import { FilterAction } from 'Model/Filter';
|
import {
|
||||||
import { FilterConditionField, FilterConditionType } from 'Model/FilterCondition';
|
capa,
|
||||||
import { SettingsGet } from 'Common/Globals';
|
i18n,
|
||||||
import { defaultOptionsAfterRender } from 'Common/Utils';
|
koComputable
|
||||||
import { i18n, initOnStartOrLangChange } from 'Common/Translator';
|
} from 'Sieve/Utils';
|
||||||
|
|
||||||
import { SieveUserStore } from 'Stores/User/Sieve';
|
const
|
||||||
|
// import { defaultOptionsAfterRender } from 'Common/Utils';
|
||||||
|
defaultOptionsAfterRender = (domItem, item) =>
|
||||||
|
domItem && item && undefined !== item.disabled
|
||||||
|
&& domItem.classList.toggle('disabled', domItem.disabled = item.disabled),
|
||||||
|
|
||||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
// import { folderListOptionsBuilder } from 'Common/Folders';
|
||||||
|
/**
|
||||||
|
* @param {Array=} aDisabled
|
||||||
|
* @param {Array=} aHeaderLines
|
||||||
|
* @param {Function=} fRenameCallback
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
folderListOptionsBuilder = (
|
||||||
|
aDisabled,
|
||||||
|
aHeaderLines,
|
||||||
|
fRenameCallback
|
||||||
|
) => {
|
||||||
|
const
|
||||||
|
aResult = [],
|
||||||
|
sDeepPrefix = '\u00A0\u00A0\u00A0',
|
||||||
|
showUnsubscribed = true/*!SettingsUserStore.hideUnsubscribed()*/,
|
||||||
|
|
||||||
import { folderListOptionsBuilder } from 'Common/Folders';
|
foldersWalk = folders => {
|
||||||
|
folders.forEach(oItem => {
|
||||||
|
if (showUnsubscribed || oItem.hasSubscriptions() || !oItem.exists) {
|
||||||
|
aResult.push({
|
||||||
|
id: oItem.fullName,
|
||||||
|
name:
|
||||||
|
sDeepPrefix.repeat(oItem.deep) +
|
||||||
|
fRenameCallback(oItem),
|
||||||
|
system: false,
|
||||||
|
disabled: !oItem.selectable() || aDisabled.includes(oItem.fullName)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export class FilterPopupView extends AbstractViewPopup {
|
if (oItem.subFolders.length) {
|
||||||
|
foldersWalk(oItem.subFolders());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
fRenameCallback = fRenameCallback || (oItem => oItem.name());
|
||||||
|
Array.isArray(aDisabled) || (aDisabled = []);
|
||||||
|
|
||||||
|
Array.isArray(aHeaderLines) && aHeaderLines.forEach(line =>
|
||||||
|
aResult.push({
|
||||||
|
id: line[0],
|
||||||
|
name: line[1],
|
||||||
|
system: false,
|
||||||
|
disabled: false
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// FolderUserStore.folderList()
|
||||||
|
foldersWalk(window.Sieve.folderList() || []);
|
||||||
|
|
||||||
|
return aResult;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class FilterPopupView extends rl.pluginPopupView {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('Filter');
|
super('Filter');
|
||||||
|
|
||||||
|
|
@ -27,7 +85,7 @@ export class FilterPopupView extends AbstractViewPopup {
|
||||||
this.defaultOptionsAfterRender = defaultOptionsAfterRender;
|
this.defaultOptionsAfterRender = defaultOptionsAfterRender;
|
||||||
this.folderSelectList = koComputable(() =>
|
this.folderSelectList = koComputable(() =>
|
||||||
folderListOptionsBuilder(
|
folderListOptionsBuilder(
|
||||||
[SettingsGet('SieveAllowFileintoInbox') ? '' : 'INBOX'],
|
[rl.settings.get('SieveAllowFileintoInbox') ? '' : 'INBOX'],
|
||||||
[['', '']],
|
[['', '']],
|
||||||
item => item ? item.localName() : ''
|
item => item ? item.localName() : ''
|
||||||
)
|
)
|
||||||
|
|
@ -39,9 +97,7 @@ export class FilterPopupView extends AbstractViewPopup {
|
||||||
key => this[key] = ko.observableArray()
|
key => this[key] = ko.observableArray()
|
||||||
);
|
);
|
||||||
|
|
||||||
initOnStartOrLangChange(this.populateOptions.bind(this));
|
this.populateOptions();
|
||||||
|
|
||||||
SieveUserStore.capa.subscribe(this.populateOptions, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
saveFilter() {
|
saveFilter() {
|
||||||
|
|
@ -77,11 +133,10 @@ export class FilterPopupView extends AbstractViewPopup {
|
||||||
|
|
||||||
// this.actionTypeOptions.push({id: FilterAction.None,
|
// this.actionTypeOptions.push({id: FilterAction.None,
|
||||||
// name: i18n('GLOBAL/NONE')});
|
// name: i18n('GLOBAL/NONE')});
|
||||||
const modules = SieveUserStore.capa;
|
if (capa) {
|
||||||
if (modules) {
|
this.allowMarkAsRead(capa.includes('imap4flags'));
|
||||||
this.allowMarkAsRead(modules.includes('imap4flags'));
|
|
||||||
|
|
||||||
if (modules.includes('fileinto')) {
|
if (capa.includes('fileinto')) {
|
||||||
this.actionTypeOptions.push({
|
this.actionTypeOptions.push({
|
||||||
id: FilterAction.MoveTo,
|
id: FilterAction.MoveTo,
|
||||||
name: i18nFilter('ACTION_MOVE_TO')
|
name: i18nFilter('ACTION_MOVE_TO')
|
||||||
|
|
@ -92,22 +147,22 @@ export class FilterPopupView extends AbstractViewPopup {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modules.includes('reject')) {
|
if (capa.includes('reject')) {
|
||||||
this.actionTypeOptions.push({ id: FilterAction.Reject, name: i18nFilter('ACTION_REJECT') });
|
this.actionTypeOptions.push({ id: FilterAction.Reject, name: i18nFilter('ACTION_REJECT') });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modules.includes('vacation')) {
|
if (capa.includes('vacation')) {
|
||||||
this.actionTypeOptions.push({
|
this.actionTypeOptions.push({
|
||||||
id: FilterAction.Vacation,
|
id: FilterAction.Vacation,
|
||||||
name: i18nFilter('ACTION_VACATION_MESSAGE')
|
name: i18nFilter('ACTION_VACATION_MESSAGE')
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modules.includes('body')) {
|
if (capa.includes('body')) {
|
||||||
this.fieldOptions.push({ id: FilterConditionField.Body, name: i18nFilter('FIELD_BODY') });
|
this.fieldOptions.push({ id: FilterConditionField.Body, name: i18nFilter('FIELD_BODY') });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modules.includes('regex')) {
|
if (capa.includes('regex')) {
|
||||||
this.typeOptions.push({ id: FilterConditionType.Regex, name: 'Regex' });
|
this.typeOptions.push({ id: FilterConditionType.Regex, name: 'Regex' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -129,7 +184,8 @@ export class FilterPopupView extends AbstractViewPopup {
|
||||||
this.filter().removeCondition(oConditionToDelete);
|
this.filter().removeCondition(oConditionToDelete);
|
||||||
}
|
}
|
||||||
|
|
||||||
onShow(oFilter, fTrueCallback, bEdit) {
|
beforeShow(oFilter, fTrueCallback, bEdit) {
|
||||||
|
// onShow(oFilter, fTrueCallback, bEdit) {
|
||||||
this.isNew(!bEdit);
|
this.isNew(!bEdit);
|
||||||
|
|
||||||
this.fTrueCallback = fTrueCallback;
|
this.fTrueCallback = fTrueCallback;
|
||||||
|
|
@ -138,6 +194,8 @@ export class FilterPopupView extends AbstractViewPopup {
|
||||||
this.selectedFolderValue(oFilter.actionValue());
|
this.selectedFolderValue(oFilter.actionValue());
|
||||||
|
|
||||||
bEdit || oFilter.nameFocused(true);
|
bEdit || oFilter.nameFocused(true);
|
||||||
|
|
||||||
|
this.populateOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
afterShow() {
|
afterShow() {
|
||||||
|
|
@ -1,24 +1,22 @@
|
||||||
import ko from 'ko';
|
import { FilterModel } from 'Sieve/Model/Filter';
|
||||||
|
import { SieveScriptModel } from 'Sieve/Model/Script';
|
||||||
|
|
||||||
import { getNotification, i18nToNodes } from 'Common/Translator';
|
import { FilterPopupView } from 'Sieve/View/Filter';
|
||||||
import { addObservablesTo } from 'External/ko';
|
|
||||||
|
|
||||||
import Remote from 'Remote/User/Fetch';
|
|
||||||
import { FilterModel } from 'Model/Filter';
|
|
||||||
import { SieveUserStore } from 'Stores/User/Sieve';
|
|
||||||
|
|
||||||
import { showScreenPopup } from 'Knoin/Knoin';
|
|
||||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
|
||||||
|
|
||||||
import { FilterPopupView } from 'View/Popup/Filter';
|
|
||||||
|
|
||||||
//import { parseScript } from 'Sieve/Parser';
|
//import { parseScript } from 'Sieve/Parser';
|
||||||
|
|
||||||
export class SieveScriptPopupView extends AbstractViewPopup {
|
import {
|
||||||
|
capa,
|
||||||
|
scripts,
|
||||||
|
getNotification,
|
||||||
|
Remote
|
||||||
|
} from 'Sieve/Utils';
|
||||||
|
|
||||||
|
export class SieveScriptPopupView extends rl.pluginPopupView {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('SieveScript');
|
super('SieveScript');
|
||||||
|
|
||||||
addObservablesTo(this, {
|
this.addObservables({
|
||||||
saveError: false,
|
saveError: false,
|
||||||
saveErrorText: '',
|
saveErrorText: '',
|
||||||
rawActive: false,
|
rawActive: false,
|
||||||
|
|
@ -26,7 +24,7 @@ export class SieveScriptPopupView extends AbstractViewPopup {
|
||||||
script: null
|
script: null
|
||||||
});
|
});
|
||||||
|
|
||||||
this.sieveCapabilities = SieveUserStore.capa.join(' ');
|
this.sieveCapabilities = capa.join(' ');
|
||||||
this.saving = false;
|
this.saving = false;
|
||||||
|
|
||||||
this.filterForDeletion = ko.observable(null).askDeleteHelper();
|
this.filterForDeletion = ko.observable(null).askDeleteHelper();
|
||||||
|
|
@ -40,7 +38,7 @@ export class SieveScriptPopupView extends AbstractViewPopup {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!script.exists() && SieveUserStore.scripts.find(item => item.name() === script.name())) {
|
if (!script.exists() && scripts.find(item => item.name() === script.name())) {
|
||||||
script.nameError(true);
|
script.nameError(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -60,7 +58,7 @@ export class SieveScriptPopupView extends AbstractViewPopup {
|
||||||
self.saveError(true);
|
self.saveError(true);
|
||||||
self.saveErrorText((data && data.ErrorMessageAdditional) || getNotification(iError));
|
self.saveErrorText((data && data.ErrorMessageAdditional) || getNotification(iError));
|
||||||
} else {
|
} else {
|
||||||
script.exists() || SieveUserStore.scripts.push(script);
|
script.exists() || scripts.push(script);
|
||||||
script.exists(true);
|
script.exists(true);
|
||||||
script.hasChanges(false);
|
script.hasChanges(false);
|
||||||
}
|
}
|
||||||
|
|
@ -78,7 +76,7 @@ export class SieveScriptPopupView extends AbstractViewPopup {
|
||||||
/* this = SieveScriptModel */
|
/* this = SieveScriptModel */
|
||||||
const filter = new FilterModel();
|
const filter = new FilterModel();
|
||||||
filter.generateID();
|
filter.generateID();
|
||||||
showScreenPopup(FilterPopupView, [
|
FilterPopupView.showModal([
|
||||||
filter,
|
filter,
|
||||||
() => this.filters.push(filter)
|
() => this.filters.push(filter)
|
||||||
]);
|
]);
|
||||||
|
|
@ -86,13 +84,14 @@ export class SieveScriptPopupView extends AbstractViewPopup {
|
||||||
|
|
||||||
editFilter(filter) {
|
editFilter(filter) {
|
||||||
const clonedFilter = filter.cloneSelf();
|
const clonedFilter = filter.cloneSelf();
|
||||||
showScreenPopup(FilterPopupView, [
|
FilterPopupView.showModal([
|
||||||
clonedFilter,
|
clonedFilter,
|
||||||
() => {
|
() => {
|
||||||
const script = this.script(),
|
const script = this.script(),
|
||||||
filters = script.filters(),
|
filters = script.filters(),
|
||||||
index = filters.indexOf(filter);
|
index = filters.indexOf(filter);
|
||||||
if (-1 < index) {
|
if (-1 < index) {
|
||||||
|
// script.filters.splice(index, 1, clonedFilter);
|
||||||
filters[index] = clonedFilter;
|
filters[index] = clonedFilter;
|
||||||
script.filters(filters);
|
script.filters(filters);
|
||||||
}
|
}
|
||||||
|
|
@ -118,7 +117,9 @@ export class SieveScriptPopupView extends AbstractViewPopup {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onShow(oScript) {
|
beforeShow(oScript) {
|
||||||
|
// onShow(oScript) {
|
||||||
|
oScript = oScript || new SieveScriptModel();
|
||||||
let raw = !oScript.allowFilters();
|
let raw = !oScript.allowFilters();
|
||||||
this.script(oScript);
|
this.script(oScript);
|
||||||
this.rawActive(raw);
|
this.rawActive(raw);
|
||||||
|
|
@ -132,9 +133,4 @@ export class SieveScriptPopupView extends AbstractViewPopup {
|
||||||
console.log(tree.join('\r\n'));
|
console.log(tree.join('\r\n'));
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
afterShow() {
|
|
||||||
// Sometimes not everything is translated, try again
|
|
||||||
i18nToNodes(this.viewModelDom);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -36,7 +36,7 @@ export class LanguagesPopupView extends AbstractViewPopup {
|
||||||
this.languages().forEach(item => item.selected(item.key === currentLang));
|
this.languages().forEach(item => item.selected(item.key === currentLang));
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeShow() {
|
beforeShow() {
|
||||||
this.fLang = null;
|
this.fLang = null;
|
||||||
this.userLanguage('');
|
this.userLanguage('');
|
||||||
|
|
||||||
|
|
|
||||||
238
dev/sieve.js
238
dev/sieve.js
|
|
@ -1,206 +1,56 @@
|
||||||
import { parseScript } from 'Sieve/Parser';
|
import {
|
||||||
|
capa,
|
||||||
|
scripts,
|
||||||
|
loading,
|
||||||
|
serverError,
|
||||||
|
serverErrorDesc,
|
||||||
|
setError,
|
||||||
|
updateList,
|
||||||
|
getNotification,
|
||||||
|
Remote
|
||||||
|
} from 'Sieve/Utils';
|
||||||
|
|
||||||
import { FilterModel } from 'Model/Filter';
|
import { SieveScriptPopupView } from 'Sieve/View/Script';
|
||||||
import { SieveScriptModel } from 'Model/SieveScript';
|
|
||||||
import { FilterPopupView } from 'View/Popup/Filter';
|
|
||||||
|
|
||||||
//import { getNotification, i18nToNodes } from 'Common/Translator';
|
|
||||||
import { forEachObjectValue } from 'Common/Utils';
|
|
||||||
import { koArrayWithDestroy } from 'External/ko';
|
|
||||||
|
|
||||||
// SieveUserStore
|
// SieveUserStore
|
||||||
const
|
window.Sieve = {
|
||||||
/*
|
capa: capa,
|
||||||
getNotificationMessage = code => {
|
scripts: scripts,
|
||||||
let key = getKeyByValue(Notification, code);
|
setError: setError,
|
||||||
return key ? I18N_DATA.NOTIFICATIONS[i18nKey(key).replace('_NOTIFICATION', '_ERROR')] : '';
|
updateList: updateList,
|
||||||
rl.i18n('NOTIFICATIONS/')
|
loading: loading,
|
||||||
},
|
serverError: serverError,
|
||||||
getNotification = (code, message = '', defCode = 0) => {
|
serverErrorDesc: serverErrorDesc,
|
||||||
code = parseInt(code, 10) || 0;
|
ScriptView: SieveScriptPopupView,
|
||||||
if (Notification.ClientViewError === code && message) {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
return getNotificationMessage(code)
|
folderList: null,
|
||||||
|| getNotificationMessage(parseInt(defCode, 10))
|
|
||||||
|| '';
|
|
||||||
},
|
|
||||||
*/
|
|
||||||
getNotification = code => 'ERROR ' + code,
|
|
||||||
|
|
||||||
Remote = rl.app.Remote,
|
deleteScript: script => {
|
||||||
|
serverError(false);
|
||||||
Sieve = {
|
Remote.request('FiltersScriptDelete',
|
||||||
// capabilities
|
|
||||||
capa: ko.observableArray(),
|
|
||||||
|
|
||||||
// Sieve scripts SieveScriptModel
|
|
||||||
scripts: koArrayWithDestroy(),
|
|
||||||
|
|
||||||
parseScript: parseScript,
|
|
||||||
|
|
||||||
setError: text => {
|
|
||||||
Sieve.serverError(true);
|
|
||||||
Sieve.serverErrorDesc(text);
|
|
||||||
},
|
|
||||||
updateList: () => {
|
|
||||||
if (!Sieve.loading()) {
|
|
||||||
Sieve.loading(true);
|
|
||||||
Sieve.serverError(false);
|
|
||||||
|
|
||||||
Remote.request('Filters', (iError, data) => {
|
|
||||||
Sieve.loading(false);
|
|
||||||
Sieve.scripts([]);
|
|
||||||
|
|
||||||
if (iError) {
|
|
||||||
Sieve.capa([]);
|
|
||||||
Sieve.setError(getNotification(iError));
|
|
||||||
} else {
|
|
||||||
Sieve.capa(data.Result.Capa);
|
|
||||||
/*
|
|
||||||
Sieve.scripts(
|
|
||||||
data.Result.Scripts.map(aItem => SieveScriptModel.reviveFromJson(aItem)).filter(v => v)
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
forEachObjectValue(data.Result.Scripts, value => {
|
|
||||||
value = SieveScriptModel.reviveFromJson(value);
|
|
||||||
value && Sieve.scripts.push(value)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
loading: ko.observable(false).extend({ debounce: 200 }),
|
|
||||||
serverError: ko.observable(false),
|
|
||||||
serverErrorDesc: ko.observable('')
|
|
||||||
};
|
|
||||||
|
|
||||||
Sieve.ScriptView = class SieveScriptPopupView extends rl.pluginPopupView {
|
|
||||||
constructor() {
|
|
||||||
super('SieveScript');
|
|
||||||
|
|
||||||
this.addObservables({
|
|
||||||
saveError: false,
|
|
||||||
saveErrorText: '',
|
|
||||||
rawActive: false,
|
|
||||||
allowToggle: false,
|
|
||||||
script: null
|
|
||||||
});
|
|
||||||
|
|
||||||
this.sieveCapabilities = Sieve.capa.join(' ');
|
|
||||||
this.saving = false;
|
|
||||||
|
|
||||||
this.filterForDeletion = ko.observable(null).askDeleteHelper();
|
|
||||||
}
|
|
||||||
|
|
||||||
saveScript() {
|
|
||||||
let self = this,
|
|
||||||
script = self.script();
|
|
||||||
if (!self.saving/* && script.hasChanges()*/) {
|
|
||||||
if (!script.verify()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!script.exists() && Sieve.scripts.find(item => item.name() === script.name())) {
|
|
||||||
script.nameError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.saving = true;
|
|
||||||
self.saveError(false);
|
|
||||||
|
|
||||||
if (self.allowToggle()) {
|
|
||||||
script.body(script.filtersToRaw());
|
|
||||||
}
|
|
||||||
|
|
||||||
Remote.request('FiltersScriptSave',
|
|
||||||
(iError, data) => {
|
(iError, data) => {
|
||||||
self.saving = false;
|
|
||||||
|
|
||||||
if (iError) {
|
if (iError) {
|
||||||
self.saveError(true);
|
setError((data && data.ErrorMessageAdditional) || getNotification(iError));
|
||||||
self.saveErrorText((data && data.ErrorMessageAdditional) || getNotification(iError));
|
|
||||||
} else {
|
} else {
|
||||||
script.exists() || Sieve.scripts.push(script);
|
scripts.remove(script);
|
||||||
script.exists(true);
|
|
||||||
script.hasChanges(false);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
script.toJson()
|
{name:script.name()}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleScript(script) {
|
||||||
|
let name = script.active() ? '' : script.name();
|
||||||
|
serverError(false);
|
||||||
|
Remote.request('FiltersScriptActivate',
|
||||||
|
(iError, data) => {
|
||||||
|
if (iError) {
|
||||||
|
setError((data && data.ErrorMessageAdditional) || iError)
|
||||||
|
} else {
|
||||||
|
scripts.forEach(script => script.active(script.name() === name));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{name:name}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
deleteFilter(filter) {
|
|
||||||
this.script().filters.remove(filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
addFilter() {
|
|
||||||
/* this = SieveScriptModel */
|
|
||||||
const filter = new FilterModel();
|
|
||||||
filter.generateID();
|
|
||||||
FilterPopupView.showModal([
|
|
||||||
filter,
|
|
||||||
() => this.filters.push(filter)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
editFilter(filter) {
|
|
||||||
const clonedFilter = filter.cloneSelf();
|
|
||||||
FilterPopupView.showModal([
|
|
||||||
clonedFilter,
|
|
||||||
() => {
|
|
||||||
const script = this.script(),
|
|
||||||
filters = script.filters(),
|
|
||||||
index = filters.indexOf(filter);
|
|
||||||
if (-1 < index) {
|
|
||||||
filters[index] = clonedFilter;
|
|
||||||
script.filters(filters);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
true
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleFiltersRaw() {
|
|
||||||
let script = this.script(), notRaw = !this.rawActive();
|
|
||||||
if (notRaw) {
|
|
||||||
script.body(script.filtersToRaw());
|
|
||||||
script.hasChanges(script.hasChanges());
|
|
||||||
}
|
|
||||||
this.rawActive(notRaw);
|
|
||||||
}
|
|
||||||
|
|
||||||
onBuild(oDom) {
|
|
||||||
oDom.addEventListener('click', event => {
|
|
||||||
const el = event.target.closestWithin('td.e-action', oDom),
|
|
||||||
filter = el && ko.dataFor(el);
|
|
||||||
filter && this.editFilter(filter);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onShow(oScript) {
|
|
||||||
oScript = oScript || new SieveScriptModel();
|
|
||||||
let raw = !oScript.allowFilters();
|
|
||||||
this.script(oScript);
|
|
||||||
this.rawActive(raw);
|
|
||||||
this.allowToggle(!raw);
|
|
||||||
this.saveError(false);
|
|
||||||
|
|
||||||
/*
|
|
||||||
// TODO: Sieve GUI
|
|
||||||
let tree = parseScript(oScript.body(), oScript.name());
|
|
||||||
console.dir(tree);
|
|
||||||
console.log(tree.join('\r\n'));
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
afterShow() {
|
|
||||||
// Sometimes not everything is translated, try again
|
|
||||||
// i18nToNodes(this.viewModelDom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.Sieve = Sieve;
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ class PluginPopupView extends rl.pluginPopupView
|
||||||
onBuild(dom) {}
|
onBuild(dom) {}
|
||||||
|
|
||||||
// Happens before showModal()
|
// Happens before showModal()
|
||||||
onBeforeShow(...params) {}
|
beforeShow(...params) {}
|
||||||
// Happens after showModal()
|
// Happens after showModal()
|
||||||
onShow(...params) {}
|
onShow(...params) {}
|
||||||
// Happens after showModal() animation transitionend
|
// Happens after showModal() animation transitionend
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,7 @@
|
||||||
<span data-i18n="POPUPS_FILTER/TITLE_EDIT_FILTER" data-bind="visible: !isNew()"></span>
|
<span data-i18n="POPUPS_FILTER/TITLE_EDIT_FILTER" data-bind="visible: !isNew()"></span>
|
||||||
</h3>
|
</h3>
|
||||||
</header>
|
</header>
|
||||||
<div class="modal-body">
|
<div class="modal-body" data-bind="with: filter, i18nInit: filter">
|
||||||
<div class="filter" data-bind="with: filter, i18nInit: filter">
|
|
||||||
<div data-bind="i18nInit: true">
|
|
||||||
|
|
||||||
<div class="control-group" data-bind="css: {'error': nameError}">
|
<div class="control-group" data-bind="css: {'error': nameError}">
|
||||||
<input type="text" class="span5"
|
<input type="text" class="span5"
|
||||||
data-bind="value: name, hasfocus: nameFocused"
|
data-bind="value: name, hasfocus: nameFocused"
|
||||||
|
|
@ -41,8 +38,6 @@
|
||||||
<div class="legend" data-i18n="POPUPS_FILTER/LEGEND_ACTIONS"></div>
|
<div class="legend" data-i18n="POPUPS_FILTER/LEGEND_ACTIONS"></div>
|
||||||
<select class="span3" data-bind="options: $root.actionTypeOptions, value: actionType, optionsText: 'name', optionsValue: 'id'"></select>
|
<select class="span3" data-bind="options: $root.actionTypeOptions, value: actionType, optionsText: 'name', optionsValue: 'id'"></select>
|
||||||
<div data-bind="template: {'name': actionTemplate()}, i18nUpdate: actionTemplate"></div>
|
<div data-bind="template: {'name': actionTemplate()}, i18nUpdate: actionTemplate"></div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
<footer>
|
||||||
<a class="btn buttonSave" data-bind="click: saveFilter" data-icon="✔" data-i18n="GLOBAL/DONE"></a>
|
<a class="btn buttonSave" data-bind="click: saveFilter" data-icon="✔" data-i18n="GLOBAL/DONE"></a>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<!-- ko with: script -->
|
<!-- ko with: script -->
|
||||||
<header>
|
<header data-bind="i18nInit: true">
|
||||||
<a href="#" class="close" data-bind="click: $root.close">×</a>
|
<a href="#" class="close" data-bind="click: $root.close">×</a>
|
||||||
<h3>
|
<h3>
|
||||||
<span data-i18n="POPUPS_SIEVE_SCRIPT/TITLE_CREATE" data-bind="visible: !exists()"></span>
|
<span data-i18n="POPUPS_SIEVE_SCRIPT/TITLE_CREATE" data-bind="visible: !exists()"></span>
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<span data-bind="visible: exists">: <!-- ko text: name--><!-- /ko --></span>
|
<span data-bind="visible: exists">: <!-- ko text: name--><!-- /ko --></span>
|
||||||
</h3>
|
</h3>
|
||||||
</header>
|
</header>
|
||||||
<div class="modal-body">
|
<div class="modal-body" data-bind="i18nInit: true">
|
||||||
|
|
||||||
<div class="control-group" data-bind="css: {'error': nameError}, hidden: exists">
|
<div class="control-group" data-bind="css: {'error': nameError}, hidden: exists">
|
||||||
<input type="text" class="span5"
|
<input type="text" class="span5"
|
||||||
|
|
@ -60,7 +60,7 @@
|
||||||
<a class="btn" data-bind="click: $root.addFilter" data-icon="✚" data-i18n="POPUPS_SIEVE_SCRIPT/BUTTON_ADD_FILTER"></a>
|
<a class="btn" data-bind="click: $root.addFilter" data-icon="✚" data-i18n="POPUPS_SIEVE_SCRIPT/BUTTON_ADD_FILTER"></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
<footer data-bind="i18nInit: true">
|
||||||
<a class="btn" data-bind="visible: $root.allowToggle, click: function() { $root.toggleFiltersRaw(); }, css: {'active': $root.rawActive }" data-i18n="[title]POPUPS_SIEVE_SCRIPT/BUTTON_RAW_SCRIPT">
|
<a class="btn" data-bind="visible: $root.allowToggle, click: function() { $root.toggleFiltersRaw(); }, css: {'active': $root.rawActive }" data-i18n="[title]POPUPS_SIEVE_SCRIPT/BUTTON_RAW_SCRIPT">
|
||||||
<i class="icon-file-code"></i>
|
<i class="icon-file-code"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -30,4 +30,4 @@
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<a class="btn" data-bind="click: $root.addScript" data-icon="✚" data-i18n="SETTINGS_FILTERS/BUTTON_ADD_SCRIPT"></a>
|
<a class="btn" data-bind="click: addScript" data-icon="✚" data-i18n="SETTINGS_FILTERS/BUTTON_ADD_SCRIPT"></a>
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,6 @@ exports.jsLint = jsLint;
|
||||||
exports.js = gulp.series(
|
exports.js = gulp.series(
|
||||||
jsClean,
|
jsClean,
|
||||||
jsLint,
|
jsLint,
|
||||||
gulp.parallel(jsBoot, jsServiceWorker, jsOpenPGP, jsLibs/*, jsSieve*/, jsApp, jsAdmin),
|
gulp.parallel(jsBoot, jsServiceWorker, jsOpenPGP, jsLibs, jsSieve, jsApp, jsAdmin),
|
||||||
jsMin
|
jsMin
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue