Merge branch 'master' into UserMailTemplates

# Conflicts:
#	dev/App/User.js
#	dev/Common/Enums.js
#	dev/Screen/User/Settings.js
#	dev/Settings/Admin/General.js
#	snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php
#	snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Admin.php
#	snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php
This commit is contained in:
djmaze 2022-03-15 15:00:14 +01:00
commit f7f56b3789
794 changed files with 96444 additions and 74188 deletions

View file

@ -2,10 +2,9 @@ import { getNotification } from 'Common/Translator';
import Remote from 'Remote/User/Fetch';
import { decorateKoCommands } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
class AccountPopupView extends AbstractViewPopup {
export class AccountPopupView extends AbstractViewPopup {
constructor() {
super('Account');
@ -26,45 +25,42 @@ class AccountPopupView extends AbstractViewPopup {
this.email.subscribe(() => this.emailError(false));
this.password.subscribe(() => this.passwordError(false));
decorateKoCommands(this, {
addAccountCommand: self => !self.submitRequest()
});
}
addAccountCommand() {
this.emailError(!this.email().trim());
this.passwordError(!this.password().trim());
if (this.emailError() || this.passwordError()) {
return false;
submitForm() {
if (!this.submitRequest()) {
const email = this.email().trim(), pass = this.password();
this.emailError(!email);
this.passwordError(!pass);
if (!this.emailError() && pass) {
this.submitRequest(true);
Remote.request('AccountSetup', (iError, data) => {
this.submitRequest(false);
if (iError) {
this.submitError(getNotification(iError));
this.submitErrorAdditional((data && data.ErrorMessageAdditional) || '');
} else {
rl.app.accountsAndIdentities();
this.close();
}
}, {
Email: email,
Password: pass,
New: this.isNew() ? 1 : 0
}
);
}
}
this.submitRequest(true);
Remote.accountSetup(
(iError, data) => {
this.submitRequest(false);
if (iError) {
this.submitError(getNotification(iError));
this.submitErrorAdditional((data && data.ErrorMessageAdditional) || '');
} else {
rl.app.accountsAndIdentities();
this.cancelCommand();
}
},
this.email(),
this.password(),
this.isNew()
);
return true;
}
clearPopup() {
this.isNew(true);
this.email('');
onShow(account) {
if (account && account.isAdditional()) {
this.isNew(false);
this.email(account.email);
} else {
this.isNew(true);
this.email('');
}
this.password('');
this.emailError(false);
@ -74,14 +70,4 @@ class AccountPopupView extends AbstractViewPopup {
this.submitError('');
this.submitErrorAdditional('');
}
onShow(account) {
this.clearPopup();
if (account && account.canBeEdit()) {
this.isNew(false);
this.email(account.email);
}
}
}
export { AccountPopupView, AccountPopupView as default };

View file

@ -1,96 +0,0 @@
import { PgpUserStore } from 'Stores/User/Pgp';
import { decorateKoCommands } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
class AddOpenPgpKeyPopupView extends AbstractViewPopup {
constructor() {
super('AddOpenPgpKey');
this.addObservables({
key: '',
keyError: false,
keyErrorMessage: ''
});
this.key.subscribe(() => {
this.keyError(false);
this.keyErrorMessage('');
});
decorateKoCommands(this, {
addOpenPgpKeyCommand: 1
});
}
addOpenPgpKeyCommand() {
// eslint-disable-next-line max-len
const reg = /[-]{3,6}BEGIN[\s]PGP[\s](PRIVATE|PUBLIC)[\s]KEY[\s]BLOCK[-]{3,6}[\s\S]+?[-]{3,6}END[\s]PGP[\s](PRIVATE|PUBLIC)[\s]KEY[\s]BLOCK[-]{3,6}/gi,
openpgpKeyring = PgpUserStore.openpgpKeyring;
let keyTrimmed = this.key().trim();
if (/[\n]/.test(keyTrimmed)) {
keyTrimmed = keyTrimmed.replace(/[\r]+/g, '').replace(/[\n]{2,}/g, '\n\n');
}
this.keyError(!keyTrimmed);
this.keyErrorMessage('');
if (!openpgpKeyring || this.keyError()) {
return false;
}
let match = null,
count = 30,
done = false;
do {
match = reg.exec(keyTrimmed);
if (match && 0 < count) {
if (match[0] && match[1] && match[2] && match[1] === match[2]) {
let err = null;
if ('PRIVATE' === match[1]) {
err = openpgpKeyring.privateKeys.importKey(match[0]);
} else if ('PUBLIC' === match[1]) {
err = openpgpKeyring.publicKeys.importKey(match[0]);
}
if (err) {
this.keyError(true);
this.keyErrorMessage(err && err[0] ? '' + err[0] : '');
console.log(err);
}
}
--count;
done = false;
} else {
done = true;
}
} while (!done);
openpgpKeyring.store();
rl.app.reloadOpenPgpKeys();
if (this.keyError()) {
return false;
}
this.cancelCommand();
return true;
}
clearPopup() {
this.key('');
this.keyError(false);
this.keyErrorMessage('');
}
onShow() {
this.clearPopup();
}
}
export { AddOpenPgpKeyPopupView, AddOpenPgpKeyPopupView as default };

View file

@ -1,13 +1,13 @@
import ko from 'ko';
import { koComputable } from 'External/ko';
import { i18n, trigger as translatorTrigger } from 'Common/Translator';
import { MessageUserStore } from 'Stores/User/Message';
import { MessagelistUserStore } from 'Stores/User/Messagelist';
import { decorateKoCommands } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
import { FolderUserStore } from 'Stores/User/Folder';
class AdvancedSearchPopupView extends AbstractViewPopup {
export class AdvancedSearchPopupView extends AbstractViewPopup {
constructor() {
super('AdvancedSearch');
@ -17,15 +17,18 @@ class AdvancedSearchPopupView extends AbstractViewPopup {
subject: '',
text: '',
selectedDateValue: -1,
selectedTreeValue: '',
hasAttachment: false,
starred: false,
unseen: false
});
let prefix = 'SEARCH/LABEL_ADV_DATE_';
this.selectedDates = ko.computed(() => {
this.showMultisearch = koComputable(() => FolderUserStore.hasCapability('MULTISEARCH'));
this.selectedDates = koComputable(() => {
translatorTrigger();
let prefix = 'SEARCH/LABEL_ADV_DATE_';
return [
{ id: -1, name: i18n(prefix + 'ALL') },
{ id: 3, name: i18n(prefix + '3_DAYS') },
@ -37,18 +40,24 @@ class AdvancedSearchPopupView extends AbstractViewPopup {
];
});
decorateKoCommands(this, {
searchCommand: 1
this.selectedTree = koComputable(() => {
translatorTrigger();
let prefix = 'SEARCH/LABEL_ADV_SUBFOLDERS_';
return [
{ id: '', name: i18n(prefix + 'NONE') },
{ id: 'subtree-one', name: i18n(prefix + 'SUBTREE_ONE') },
{ id: 'subtree', name: i18n(prefix + 'SUBTREE') }
];
});
}
searchCommand() {
submitForm() {
const search = this.buildSearchString();
if (search) {
MessageUserStore.mainMessageListSearch(search);
MessagelistUserStore.mainSearch(search);
}
this.cancelCommand();
this.close();
}
parseSearchStringValue(search) {
@ -69,68 +78,38 @@ class AdvancedSearchPopupView extends AbstractViewPopup {
});
}
buildSearchStringValue(value) {
if (value.includes(' ')) {
value = '"' + value + '"';
}
return value;
}
buildSearchString() {
const result = [],
from_ = this.from().trim(),
to = this.to().trim(),
subject = this.subject().trim(),
text = this.text().trim(),
isPart = [],
hasPart = [];
if (from_) {
result.push('from:' + this.buildSearchStringValue(from_));
}
if (to) {
result.push('to:' + this.buildSearchStringValue(to));
}
if (subject) {
result.push('subject:' + this.buildSearchStringValue(subject));
}
if (this.hasAttachment()) {
hasPart.push('attachment');
}
if (this.unseen()) {
isPart.push('unseen');
}
if (this.starred()) {
isPart.push('flagged');
}
if (hasPart.length) {
result.push('has:' + hasPart.join(','));
}
if (isPart.length) {
result.push('is:' + isPart.join(','));
}
const
data = new FormData(),
append = (key, value) => value.length && data.append(key, value);
append('from', this.from().trim());
append('to', this.to().trim());
append('subject', this.subject().trim());
append('text', this.text().trim());
append('in', this.selectedTreeValue());
if (-1 < this.selectedDateValue()) {
let d = new Date();
d.setDate(d.getDate() - this.selectedDateValue());
result.push('date:' + d.format('Y.m.d') + '/');
append('date', d.format('Y.m.d') + '/');
}
if (text) {
result.push('text:' + this.buildSearchStringValue(text));
let result = new URLSearchParams(data).toString();
if (this.hasAttachment()) {
result += '&attachment';
}
if (this.unseen()) {
result += '&unseen';
}
if (this.starred()) {
result += '&flagged';
}
return result.join(' ').trim();
return result.replace(/^&+/, '');
}
clearPopup() {
onShow(search) {
this.from('');
this.to('');
this.subject('');
@ -140,12 +119,7 @@ class AdvancedSearchPopupView extends AbstractViewPopup {
this.hasAttachment(false);
this.starred(false);
this.unseen(false);
}
onShow(search) {
this.clearPopup();
this.parseSearchStringValue(search);
}
}
export { AdvancedSearchPopupView, AdvancedSearchPopupView as default };

View file

@ -1,84 +1,68 @@
import { Scope } from 'Common/Enums';
import { i18n } from 'Common/Translator';
import { isFunction } from 'Common/Utils';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
class AskPopupView extends AbstractViewPopup {
export class AskPopupView extends AbstractViewPopup {
constructor() {
super('Ask');
this.addObservables({
askDesc: '',
yesButton: '',
noButton: ''
noButton: '',
passphrase: '',
askPass: false
});
this.fYesAction = null;
this.fNoAction = null;
this.bFocusYesOnShow = true;
this.bDisabeCloseOnEsc = true;
}
clearPopup() {
this.askDesc('');
this.yesButton(i18n('POPUPS_ASK/BUTTON_YES'));
this.noButton(i18n('POPUPS_ASK/BUTTON_NO'));
this.fYesAction = null;
this.fNoAction = null;
this.focusOnShow = true;
}
yesClick() {
this.cancelCommand();
this.close();
isFunction(this.fYesAction) && this.fYesAction.call(null);
isFunction(this.fYesAction) && this.fYesAction();
}
noClick() {
this.cancelCommand();
this.close();
isFunction(this.fNoAction) && this.fNoAction.call(null);
isFunction(this.fNoAction) && this.fNoAction();
}
/**
* @param {string} sAskDesc
* @param {Function=} fYesFunc
* @param {Function=} fNoFunc
* @param {string=} sYesButton
* @param {string=} sNoButton
* @param {boolean=} bFocusYesOnShow = true
* @param {boolean=} focusOnShow = true
* @returns {void}
*/
onShow(askDesc, fYesFunc = null, fNoFunc = null, yesButton = '', noButton = '', isFocusYesOnShow = true) {
this.clearPopup();
this.fYesAction = fYesFunc || null;
this.fNoAction = fNoFunc || null;
this.askDesc(askDesc || '');
if (yesButton) {
this.yesButton(yesButton);
}
if (noButton) {
this.noButton(noButton);
}
this.bFocusYesOnShow = !!isFocusYesOnShow;
onShow(sAskDesc, fYesFunc = null, fNoFunc = null, focusOnShow = true, askPass = false, btnText = '') {
this.askDesc(sAskDesc || '');
this.askPass(askPass);
this.passphrase('');
this.yesButton(i18n(btnText || 'POPUPS_ASK/BUTTON_YES'));
this.noButton(i18n(askPass ? 'GLOBAL/CANCEL' : 'POPUPS_ASK/BUTTON_NO'));
this.fYesAction = fYesFunc;
this.fNoAction = fNoFunc;
this.focusOnShow = focusOnShow ? (askPass ? 'input[type="password"]' : '.buttonYes') : '';
}
onShowWithDelay() {
if (this.bFocusYesOnShow) {
this.querySelector('.buttonYes').focus();
}
afterShow() {
this.focusOnShow && this.querySelector(this.focusOnShow).focus();
}
onClose() {
this.noClick();
return false;
}
onBuild() {
// shortcuts.add('tab', 'shift', Scope.Ask, () => {
shortcuts.add('tab,arrowright,arrowleft', '', Scope.Ask, () => {
// shortcuts.add('tab', 'shift', 'Ask', () => {
shortcuts.add('tab,arrowright,arrowleft', '', 'Ask', () => {
let btn = this.querySelector('.buttonYes');
if (btn.matches(':focus')) {
btn = this.querySelector('.buttonNo');
@ -86,12 +70,18 @@ class AskPopupView extends AbstractViewPopup {
btn.focus();
return false;
});
shortcuts.add('escape', '', Scope.Ask, () => {
this.noClick();
return false;
});
}
}
export { AskPopupView, AskPopupView as default };
AskPopupView.password = function(sAskDesc, btnText) {
return new Promise(resolve => {
this.showModal([
sAskDesc,
() => resolve(this.__vm.passphrase()),
() => resolve(null),
true,
true,
btnText
]);
});
}

File diff suppressed because it is too large Load diff

View file

@ -1,385 +0,0 @@
import ko from 'ko';
import { pString, defaultOptionsAfterRender } from 'Common/Utils';
import { Scope } from 'Common/Enums';
import { i18n } from 'Common/Translator';
import { PgpUserStore } from 'Stores/User/Pgp';
import { EmailModel } from 'Model/Email';
import { decorateKoCommands } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
const KEY_NAME_SUBSTR = -8,
i18nPGP = (key, params) => i18n('PGP_NOTIFICATIONS/' + key, params);
class ComposeOpenPgpPopupView extends AbstractViewPopup {
constructor() {
super('ComposeOpenPgp');
this.publicKeysOptionsCaption = i18nPGP('ADD_A_PUBLICK_KEY');
this.privateKeysOptionsCaption = i18nPGP('SELECT_A_PRIVATE_KEY');
this.addObservables({
notification: '',
sign: false,
encrypt: false,
password: '',
text: '',
selectedPrivateKey: null,
selectedPublicKey: null,
signKey: null,
submitRequest: false
});
this.encryptKeys = ko.observableArray();
this.addComputables({
encryptKeysView: () => this.encryptKeys.map(oKey => (oKey ? oKey.key : null)).filter(v => v),
privateKeysOptions: () => {
const opts = PgpUserStore.openpgpkeysPrivate().map(oKey => {
if (this.signKey() && this.signKey().key.id === oKey.id) {
return null;
}
return oKey.users.map(user => ({
id: oKey.guid,
name: '(' + oKey.id.substr(KEY_NAME_SUBSTR).toUpperCase() + ') ' + user,
key: oKey
}));
});
return opts.flat().filter(v => v);
},
publicKeysOptions: () => {
const opts = PgpUserStore.openpgpkeysPublic().map(oKey => {
if (this.encryptKeysView().includes(oKey)) {
return null;
}
return oKey.users.map(user => ({
id: oKey.guid,
name: '(' + oKey.id.substr(KEY_NAME_SUBSTR).toUpperCase() + ') ' + user,
key: oKey
}));
});
return opts.flat().filter(v => v);
}
});
this.resultCallback = null;
this.selectedPrivateKey.subscribe((value) => {
if (value) {
this.selectCommand();
this.updateCommand();
}
});
this.selectedPublicKey.subscribe((value) => {
if (value) {
this.addCommand();
}
});
this.defaultOptionsAfterRender = defaultOptionsAfterRender;
this.deletePublickKey = this.deletePublickKey.bind(this);
decorateKoCommands(this, {
doCommand: self => !self.submitRequest() && (self.sign() || self.encrypt()),
selectCommand: 1,
addCommand: 1,
updateCommand: 1,
});
}
doCommand() {
let result = true,
privateKey = null,
aPublicKeys = [];
this.submitRequest(true);
if (result && this.sign()) {
if (!this.signKey()) {
this.notification(i18nPGP('NO_PRIVATE_KEY_FOUND'));
result = false;
} else if (!this.signKey().key) {
this.notification(
i18nPGP('NO_PRIVATE_KEY_FOUND_FOR', {
EMAIL: this.signKey().email
})
);
result = false;
}
if (result) {
const privateKeys = this.signKey().key.getNativeKeys();
privateKey = privateKeys[0] || null;
try {
if (privateKey) {
privateKey.decrypt(pString(this.password()));
}
} catch (e) {
privateKey = null;
}
if (!privateKey) {
this.notification(i18nPGP('NO_PRIVATE_KEY_FOUND'));
result = false;
}
}
}
if (result && this.encrypt()) {
if (this.encryptKeys.length) {
aPublicKeys = [];
this.encryptKeys.forEach(oKey => {
if (oKey && oKey.key) {
aPublicKeys = aPublicKeys.concat(oKey.key.getNativeKeys().flat(Infinity).filter(v => v));
} else if (oKey && oKey.email) {
this.notification(
i18nPGP('NO_PUBLIC_KEYS_FOUND_FOR', {
EMAIL: oKey.email
})
);
result = false;
}
});
if (result && (!aPublicKeys.length || this.encryptKeys.length !== aPublicKeys.length)) {
result = false;
}
} else {
this.notification(i18nPGP('NO_PUBLIC_KEYS_FOUND'));
result = false;
}
}
if (result && this.resultCallback) {
setTimeout(() => {
let pgpPromise = null;
try {
if (aPublicKeys.length) {
if (privateKey) {
pgpPromise = PgpUserStore.openpgp.encrypt({
data: this.text(),
publicKeys: aPublicKeys,
privateKeys: [privateKey]
});
} else {
pgpPromise = PgpUserStore.openpgp.encrypt({
data: this.text(),
publicKeys: aPublicKeys
});
}
} else if (privateKey) {
pgpPromise = PgpUserStore.openpgp.sign({
data: this.text(),
privateKeys: [privateKey]
});
}
} catch (e) {
console.log(e);
this.notification(
i18nPGP('PGP_ERROR', {
ERROR: '' + e
})
);
}
if (pgpPromise) {
try {
pgpPromise
.then((mData) => {
this.resultCallback(mData.data);
this.cancelCommand();
})
.catch((e) => {
this.notification(
i18nPGP('PGP_ERROR', {
ERROR: '' + e
})
);
});
} catch (e) {
this.notification(
i18nPGP('PGP_ERROR', {ERROR: '' + e})
);
}
}
this.submitRequest(false);
}, 20);
} else {
this.submitRequest(false);
}
return result;
}
selectCommand() {
const keyId = this.selectedPrivateKey(),
option = keyId ? this.privateKeysOptions().find(item => item && keyId === item.id) : null;
if (option) {
this.signKey({
empty: !option.key,
selected: ko.observable(!!option.key),
users: option.key.users,
hash: option.key.id.substr(KEY_NAME_SUBSTR).toUpperCase(),
key: option.key
});
}
}
addCommand() {
const keyId = this.selectedPublicKey(),
option = keyId ? this.publicKeysOptions().find(item => item && keyId === item.id) : null;
if (option) {
this.encryptKeys.push({
empty: !option.key,
selected: ko.observable(!!option.key),
removable: ko.observable(!this.sign() || !this.signKey() || this.signKey().key.id !== option.key.id),
users: option.key.users,
hash: option.key.id.substr(KEY_NAME_SUBSTR).toUpperCase(),
key: option.key
});
}
}
updateCommand() {
this.encryptKeys.forEach(oKey =>
oKey.removable(!this.sign() || !this.signKey() || this.signKey().key.id !== oKey.key.id)
);
}
deletePublickKey(publicKey) {
this.encryptKeys.remove(publicKey);
}
clearPopup() {
this.notification('');
this.sign(false);
this.encrypt(false);
this.password('');
this.signKey(null);
this.encryptKeys([]);
this.text('');
this.resultCallback = null;
}
onBuild() {
// shortcuts.add('tab', 'shift', Scope.ComposeOpenPgp, () => {
shortcuts.add('tab', '', Scope.ComposeOpenPgp, () => {
let btn = this.querySelector('.inputPassword');
if (btn.matches(':focus')) {
btn = this.querySelector('.buttonDo');
}
btn.focus();
return false;
});
}
onHideWithDelay() {
this.clearPopup();
}
onShowWithDelay() {
this.querySelector(this.sign() ? '.inputPassword' : '.buttonDo').focus();
}
onShow(fCallback, sText, identity, sTo, sCc, sBcc) {
this.clearPopup();
let rec = [],
emailLine = '';
const email = new EmailModel();
this.resultCallback = fCallback;
if (sTo) {
rec.push(sTo);
}
if (sCc) {
rec.push(sCc);
}
if (sBcc) {
rec.push(sBcc);
}
rec = rec.join(', ').split(',');
rec = rec.map(value => {
email.clear();
email.parse(value.trim());
return email.email || false;
}).filter(v => v);
if (identity && identity.email()) {
emailLine = identity.email();
rec.unshift(emailLine);
const keys = PgpUserStore.findAllPrivateKeysByEmailNotNative(emailLine);
if (keys && keys[0]) {
this.signKey({
users: keys[0].users || [emailLine],
hash: keys[0].id.substr(KEY_NAME_SUBSTR).toUpperCase(),
key: keys[0]
});
}
}
if (this.signKey()) {
this.sign(true);
}
if (rec.length) {
this.encryptKeys(
rec.map(recEmail => {
const keys = PgpUserStore.findAllPublicKeysByEmailNotNative(recEmail);
return keys
? keys.map(publicKey => ({
empty: !publicKey,
selected: ko.observable(!!publicKey),
removable: ko.observable(
!this.sign() || !this.signKey() || this.signKey().key.id !== publicKey.id
),
users: publicKey ? publicKey.users || [recEmail] : [recEmail],
hash: publicKey ? publicKey.id.substr(KEY_NAME_SUBSTR).toUpperCase() : '',
key: publicKey
}))
: [];
}).flat().validUnique(encryptKey => encryptKey.hash)
);
if (this.encryptKeys.length) {
this.encrypt(true);
}
}
this.text(sText);
}
}
export { ComposeOpenPgpPopupView, ComposeOpenPgpPopupView as default };

View file

@ -1,14 +1,10 @@
import ko from 'ko';
import {
SaveSettingsStep,
Scope
} from 'Common/Enums';
import { koArrayWithDestroy } from 'External/ko';
import { SaveSettingsStep } from 'Common/Enums';
import { ComposeType } from 'Common/EnumsUser';
import { registerShortcut } from 'Common/Globals';
import { arrayLength, pInt } from 'Common/Utils';
import { delegateRunOnDestroy, computedPaginatorHelper, showMessageComposer } from 'Common/UtilsUser';
import { download, computedPaginatorHelper, showMessageComposer } from 'Common/UtilsUser';
import { Selector } from 'Common/Selector';
import { serverRequestRaw, serverRequest } from 'Common/Links';
@ -23,14 +19,18 @@ import { EmailModel } from 'Model/Email';
import { ContactModel } from 'Model/Contact';
import { ContactPropertyModel, ContactPropertyType } from 'Model/ContactProperty';
import { decorateKoCommands, hideScreenPopup } from 'Knoin/Knoin';
import { decorateKoCommands, showScreenPopup } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
const CONTACTS_PER_PAGE = 50,
import { AskPopupView } from 'View/Popup/Ask';
const
CONTACTS_PER_PAGE = 50,
ScopeContacts = 'Contacts',
propertyIsMail = prop => prop.isType(ContactPropertyType.Email),
propertyIsName = prop => prop.isType(ContactPropertyType.FirstName) || prop.isType(ContactPropertyType.LastName);
class ContactsPopupView extends AbstractViewPopup {
export class ContactsPopupView extends AbstractViewPopup {
constructor() {
super('Contacts');
@ -66,7 +66,7 @@ class ContactsPopupView extends AbstractViewPopup {
this.contacts = ContactUserStore;
this.viewProperties = ko.observableArray();
this.viewProperties = koArrayWithDestroy();
this.useCheckboxesInList = SettingsUserStore.useCheckboxesInList;
@ -136,17 +136,16 @@ class ContactsPopupView extends AbstractViewPopup {
});
decorateKoCommands(this, {
newCommand: 1,
// close: self => !self.watchDirty(),
deleteCommand: self => 0 < self.contactsCheckedOrSelected().length,
newMessageCommand: self => 0 < self.contactsCheckedOrSelected().length,
clearCommand: 1,
saveCommand: self => !self.viewSaving() && !self.viewReadOnly()
&& (self.contactHasValidName() || self.viewProperties.find(prop => propertyIsMail(prop) && prop.isValid())),
syncCommand: self => !self.contacts.syncing() && !self.contacts.importing()
});
}
newCommand() {
newContact() {
this.populateViewContact(null);
this.currentContact(null);
}
@ -183,7 +182,7 @@ class ContactsPopupView extends AbstractViewPopup {
if (arrayLength(aE)) {
this.bBackToCompose = false;
hideScreenPopup(ContactsPopupView);
this.close();
switch (this.sLastComposeFocusedField) {
case 'cc':
@ -207,7 +206,7 @@ class ContactsPopupView extends AbstractViewPopup {
return true;
}
clearCommand() {
clearSearch() {
this.search('');
}
@ -217,7 +216,7 @@ class ContactsPopupView extends AbstractViewPopup {
const requestUid = Jua.randomId();
Remote.contactSave(
Remote.request('ContactSave',
(iError, oData) => {
let res = false;
this.viewSaving(false);
@ -243,15 +242,16 @@ class ContactsPopupView extends AbstractViewPopup {
this.watchDirty(false);
setTimeout(() => this.viewSaveTrigger(SaveSettingsStep.Idle), 1000);
}
},
requestUid,
this.viewID(),
this.viewProperties.map(oItem => oItem.toJSON())
}, {
RequestUid: requestUid,
Uid: this.viewID(),
Properties: this.viewProperties.map(oItem => oItem.toJSON())
}
);
}
syncCommand() {
rl.app.contactsSync(iError => {
ContactUserStore.sync(iError => {
iError && alert(getNotification(iError));
this.reloadContactList(true);
@ -316,11 +316,11 @@ class ContactsPopupView extends AbstractViewPopup {
}
exportVcf() {
rl.app.download(serverRequestRaw('ContactsVcf'));
download(serverRequestRaw('ContactsVcf'), 'contacts.vcf');
}
exportCsv() {
rl.app.download(serverRequestRaw('ContactsCsv'));
download(serverRequestRaw('ContactsCsv'), 'contacts.csv');
}
removeCheckedOrSelectedContactsFromList() {
@ -345,31 +345,30 @@ class ContactsPopupView extends AbstractViewPopup {
}
setTimeout(() => {
contacts.forEach(contact => {
ContactUserStore.remove(contact);
delegateRunOnDestroy(contact);
});
contacts.forEach(contact => ContactUserStore.remove(contact));
}, 500);
}
}
deleteSelectedContacts() {
if (this.contactsCheckedOrSelected().length) {
Remote.contactsDelete((iError, oData) => {
if (500 < (!iError && oData && oData.Time ? pInt(oData.Time) : 0)) {
this.reloadContactList(this.bDropPageAfterDelete);
} else {
setTimeout(() => this.reloadContactList(this.bDropPageAfterDelete), 500);
Remote.request('ContactsDelete',
(iError, oData) => {
if (500 < (!iError && oData && oData.Time ? pInt(oData.Time) : 0)) {
this.reloadContactList(this.bDropPageAfterDelete);
} else {
setTimeout(() => this.reloadContactList(this.bDropPageAfterDelete), 500);
}
}, {
Uids: this.contactsCheckedOrSelectedUids().join(',')
}
}, this.contactsCheckedOrSelectedUids());
);
this.removeCheckedOrSelectedContactsFromList();
}
}
removeProperty(oProp) {
this.viewProperties.remove(oProp);
delegateRunOnDestroy(oProp);
}
/**
@ -393,7 +392,6 @@ class ContactsPopupView extends AbstractViewPopup {
this.viewID(id);
// delegateRunOnDestroy(this.viewProperties());
// this.viewProperties([]);
this.viewProperties(contact.properties);
@ -415,7 +413,7 @@ class ContactsPopupView extends AbstractViewPopup {
}
ContactUserStore.loading(true);
Remote.contacts(
Remote.request('Contacts',
(iError, data) => {
let count = 0,
list = [];
@ -432,27 +430,31 @@ class ContactsPopupView extends AbstractViewPopup {
this.contactsCount(count);
delegateRunOnDestroy(ContactUserStore());
ContactUserStore(list);
ContactUserStore.loading(false);
this.viewClearSearch(!!this.search());
},
offset,
CONTACTS_PER_PAGE,
this.search()
{
Offset: offset,
Limit: CONTACTS_PER_PAGE,
Search: this.search()
},
null,
'',
['Contacts']
);
}
onBuild(dom) {
this.selector.init(dom.querySelector('.b-list-content'), Scope.Contacts);
this.selector.init(dom.querySelector('.b-list-content'), ScopeContacts);
shortcuts.add('delete', '', Scope.Contacts, () => {
registerShortcut('delete', '', ScopeContacts, () => {
this.deleteCommand();
return false;
});
shortcuts.add('c,w', '', Scope.Contacts, () => {
registerShortcut('c,w', '', ScopeContacts, () => {
this.newMessageCommand();
return false;
});
@ -460,7 +462,7 @@ class ContactsPopupView extends AbstractViewPopup {
const self = this;
dom.addEventListener('click', event => {
let el = event.target.closestWithin('.e-paginator .e-page', dom);
let el = event.target.closestWithin('.e-paginator a', dom);
if (el && ko.dataFor(el)) {
self.contactsPage(pInt(ko.dataFor(el).value));
self.reloadContactList();
@ -491,23 +493,28 @@ class ContactsPopupView extends AbstractViewPopup {
}
}
onClose() {
if (this.watchDirty() && AskPopupView.hidden()) {
showScreenPopup(AskPopupView, [
i18n('POPUPS_ASK/DESC_WANT_CLOSE_THIS_WINDOW'),
() => this.close()
]);
return false;
}
}
onShow(bBackToCompose, sLastComposeFocusedField) {
this.bBackToCompose = !!bBackToCompose;
this.sLastComposeFocusedField = sLastComposeFocusedField;
rl.route.off();
this.reloadContactList(true);
}
onHide() {
rl.route.on();
this.currentContact(null);
this.emptySelection(true);
this.search('');
this.contactsCount(0);
delegateRunOnDestroy(ContactUserStore());
ContactUserStore([]);
this.sLastComposeFocusedField = '';
@ -519,5 +526,3 @@ class ContactsPopupView extends AbstractViewPopup {
}
}
}
export { ContactsPopupView, ContactsPopupView as default };

View file

@ -1,4 +1,4 @@
import { pInt, pString } from 'Common/Utils';
import { pInt, forEachObjectEntry } from 'Common/Utils';
import { i18n, getNotification } from 'Common/Translator';
import Remote from 'Remote/Admin/Fetch';
@ -8,7 +8,26 @@ import { AbstractViewPopup } from 'Knoin/AbstractViews';
import { DomainAdminStore } from 'Stores/Admin/Domain';
class DomainPopupView extends AbstractViewPopup {
const domainToParams = oDomain => ({
Name: oDomain.name(),
IncHost: oDomain.imapHost(),
IncPort: oDomain.imapPort(),
IncSecure: oDomain.imapSecure(),
UseSieve: oDomain.useSieve() ? 1 : 0,
SieveHost: oDomain.sieveHost(),
SievePort: oDomain.sievePort(),
SieveSecure: oDomain.sieveSecure(),
OutHost: oDomain.smtpHost(),
OutPort: oDomain.smtpPort(),
OutSecure: oDomain.smtpSecure(),
OutAuth: oDomain.smtpAuth() ? 1 : 0,
OutUsePhpMail: oDomain.smtpPhpMail() ? 1 : 0
});
export class DomainPopupView extends AbstractViewPopup {
constructor() {
super('Domain');
@ -27,9 +46,9 @@ class DomainPopupView extends AbstractViewPopup {
testingSieveErrorDesc: '',
testingSmtpErrorDesc: '',
imapServerFocus: false,
sieveServerFocus: false,
smtpServerFocus: false,
imapHostFocus: false,
sieveHostFocus: false,
smtpHostFocus: false,
});
this.addComputables({
@ -42,7 +61,7 @@ class DomainPopupView extends AbstractViewPopup {
if (this.edit()) {
result = i18n('POPUPS_DOMAIN/TITLE_EDIT_DOMAIN', { NAME: name });
if (aliasName) {
result += ' ' + aliasName;
result += ' ' + aliasName;
}
} else {
result = name
@ -64,10 +83,10 @@ class DomainPopupView extends AbstractViewPopup {
return (
this.name() &&
this.imapServer() &&
this.imapHost() &&
this.imapPort() &&
(useSieve ? this.sieveServer() && this.sievePort() : true) &&
((this.smtpServer() && this.smtpPort()) || usePhpMail)
(useSieve ? this.sieveHost() && this.sievePort() : true) &&
((this.smtpHost() && this.smtpPort()) || usePhpMail)
);
},
@ -81,28 +100,28 @@ class DomainPopupView extends AbstractViewPopup {
testingSmtpError: value => value || this.testingSmtpErrorDesc(''),
// smart form improvements
imapServerFocus: value =>
value && this.name() && !this.imapServer() && this.imapServer(this.name().replace(/[.]?[*][.]?/g, '')),
imapHostFocus: value =>
value && this.name() && !this.imapHost() && this.imapHost(this.name().replace(/[.]?[*][.]?/g, '')),
sieveServerFocus: value =>
value && this.imapServer() && !this.sieveServer() && this.sieveServer(this.imapServer()),
sieveHostFocus: value =>
value && this.imapHost() && !this.sieveHost() && this.sieveHost(this.imapHost()),
smtpServerFocus: value => value && this.imapServer() && !this.smtpServer()
&& this.smtpServer(this.imapServer().replace(/imap/gi, 'smtp')),
smtpHostFocus: value => value && this.imapHost() && !this.smtpHost()
&& this.smtpHost(this.imapHost().replace(/imap/gi, 'smtp')),
imapSecure: value => {
if (this.enableSmartPorts()) {
const port = pInt(this.imapPort());
switch (pString(value)) {
case '0':
case '2':
switch (pInt(value)) {
case 0:
case 2:
if (993 === port) {
this.imapPort('143');
this.imapPort(143);
}
break;
case '1':
case 1:
if (143 === port) {
this.imapPort('993');
this.imapPort(993);
}
break;
// no default
@ -113,20 +132,20 @@ class DomainPopupView extends AbstractViewPopup {
smtpSecure: value => {
if (this.enableSmartPorts()) {
const port = pInt(this.smtpPort());
switch (pString(value)) {
case '0':
switch (pInt(value)) {
case 0:
if (465 === port || 587 === port) {
this.smtpPort('25');
this.smtpPort(25);
}
break;
case '1':
case 1:
if (25 === port || 587 === port) {
this.smtpPort('465');
this.smtpPort(465);
}
break;
case '2':
case 2:
if (25 === port || 465 === port) {
this.smtpPort('587');
this.smtpPort(587);
}
break;
// no default
@ -143,20 +162,26 @@ class DomainPopupView extends AbstractViewPopup {
createOrAddCommand() {
this.saving(true);
Remote.createOrUpdateDomain(
Remote.request('AdminDomainSave',
this.onDomainCreateOrSaveResponse.bind(this),
this
Object.assign(domainToParams(this), {
Create: this.edit() ? 0 : 1,
IncShortLogin: this.imapShortLogin() ? 1 : 0,
OutShortLogin: this.smtpShortLogin() ? 1 : 0,
OutSetSender: this.smtpSetSender() ? 1 : 0,
WhiteList: this.whiteList()
})
);
}
testConnectionCommand() {
this.testingDone(false);
this.testingImapError(false);
this.testingSieveError(false);
this.testingSmtpError(false);
this.clearTesting(false);
this.testing(true);
Remote.testConnectionForDomain(
Remote.request('AdminDomainTest',
(iError, oData) => {
this.testing(false);
if (iError) {
@ -185,7 +210,7 @@ class DomainPopupView extends AbstractViewPopup {
}
}
},
this
domainToParams(this)
);
}
@ -195,7 +220,7 @@ class DomainPopupView extends AbstractViewPopup {
this.savingError(getNotification(iError));
} else {
DomainAdminStore.fetch();
this.closeCommand();
this.close();
}
}
@ -209,56 +234,36 @@ class DomainPopupView extends AbstractViewPopup {
onShow(oDomain) {
this.saving(false);
this.clearTesting();
this.clearForm();
if (oDomain) {
this.enableSmartPorts(false);
this.edit(true);
this.name(oDomain.Name);
this.imapServer(oDomain.IncHost);
this.imapPort('' + pInt(oDomain.IncPort));
this.imapSecure(oDomain.IncSecure);
this.imapShortLogin(!!oDomain.IncShortLogin);
this.useSieve(!!oDomain.UseSieve);
this.sieveServer(oDomain.SieveHost);
this.sievePort('' + pInt(oDomain.SievePort));
this.sieveSecure(oDomain.SieveSecure);
this.smtpServer(oDomain.OutHost);
this.smtpPort('' + pInt(oDomain.OutPort));
this.smtpSecure(oDomain.OutSecure);
this.smtpShortLogin(!!oDomain.OutShortLogin);
this.smtpAuth(!!oDomain.OutAuth);
this.smtpSetSender(!!oDomain.OutSetSender);
this.smtpPhpMail(!!oDomain.OutUsePhpMail);
this.whiteList(oDomain.WhiteList);
this.aliasName(oDomain.AliasName);
forEachObjectEntry(oDomain, (key, value) => this[key] && this[key](value));
this.enableSmartPorts(true);
}
}
getDefaults() {
return {
enableSmartPorts: false,
savingError: '',
name: '',
imapServer: '',
imapPort: '143',
imapHost: '',
imapPort: 143,
imapSecure: 0,
imapShortLogin: false,
useSieve: false,
sieveServer: '',
sievePort: '4190',
sieveHost: '',
sievePort: 4190,
sieveSecure: 0,
smtpServer: '',
smtpPort: '25',
smtpHost: '',
smtpPort: 25,
smtpSecure: 0,
smtpShortLogin: false,
smtpAuth: true,
@ -266,17 +271,13 @@ class DomainPopupView extends AbstractViewPopup {
smtpPhpMail: false,
whiteList: '',
aliasName: '',
enableSmartPorts: false
aliasName: ''
};
}
clearForm() {
this.edit(false);
Object.entries(this.getDefaults()).forEach(([key, value]) => this[key](value));
forEachObjectEntry(this.getDefaults(), (key, value) => this[key](value));
this.enableSmartPorts(true);
}
}
export { DomainPopupView, DomainPopupView as default };

View file

@ -7,7 +7,7 @@ import Remote from 'Remote/Admin/Fetch';
import { decorateKoCommands } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
class DomainAliasPopupView extends AbstractViewPopup {
export class DomainAliasPopupView extends AbstractViewPopup {
constructor() {
super('DomainAlias');
@ -35,15 +35,19 @@ class DomainAliasPopupView extends AbstractViewPopup {
createCommand() {
this.saving(true);
Remote.createDomainAlias(iError => {
this.saving(false);
if (iError) {
this.savingError(getNotification(iError));
} else {
DomainAdminStore.fetch();
this.closeCommand();
}
}, this.name(), this.alias());
Remote.request('AdminDomainAliasSave',
iError => {
this.saving(false);
if (iError) {
this.savingError(getNotification(iError));
} else {
DomainAdminStore.fetch();
this.close();
}
}, {
Name: this.name(),
Alias: this.alias()
});
}
onShow() {
@ -59,5 +63,3 @@ class DomainAliasPopupView extends AbstractViewPopup {
this.alias('');
}
}
export { DomainAliasPopupView, DomainAliasPopupView as default };

View file

@ -1,179 +0,0 @@
import ko from 'ko';
import { FilterAction } from 'Model/Filter';
import { FilterConditionField, FilterConditionType } from 'Model/FilterCondition';
import { defaultOptionsAfterRender } from 'Common/Utils';
import { i18n, initOnStartOrLangChange } from 'Common/Translator';
import { FolderUserStore } from 'Stores/User/Folder';
import { SieveUserStore } from 'Stores/User/Sieve';
import { decorateKoCommands } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
import { folderListOptionsBuilder } from 'Common/UtilsUser';
class FilterPopupView extends AbstractViewPopup {
constructor() {
super('Filter');
this.addObservables({
isNew: true,
filter: null,
allowMarkAsRead: false,
selectedFolderValue: ''
});
this.fTrueCallback = null;
this.defaultOptionsAfterRender = defaultOptionsAfterRender;
this.folderSelectList = ko.computed(() =>
folderListOptionsBuilder(
[FolderUserStore.sieveAllowFileintoInbox ? '' : 'INBOX'],
[['', '']],
item => item ? item.localName() : ''
)
);
this.selectedFolderValue.subscribe(() => this.filter() && this.filter().actionValueError(false));
['actionTypeOptions','fieldOptions','typeOptions','typeOptionsSize','typeOptionsBody'].forEach(
key => this[key] = ko.observableArray()
);
initOnStartOrLangChange(this.populateOptions.bind(this));
SieveUserStore.capa.subscribe(this.populateOptions, this);
decorateKoCommands(this, {
saveFilterCommand: 1
});
}
saveFilterCommand() {
if (this.filter()) {
if (FilterAction.MoveTo === this.filter().actionType()) {
this.filter().actionValue(this.selectedFolderValue());
}
if (!this.filter().verify()) {
return false;
}
this.fTrueCallback && this.fTrueCallback(this.filter());
this.modalVisibility() && this.closeCommand();
}
return true;
}
populateOptions() {
this.actionTypeOptions([]);
let i18nFilter = key => i18n('POPUPS_FILTER/SELECT_' + key);
this.fieldOptions([
{ id: FilterConditionField.From, name: i18n('GLOBAL/FROM') },
{ id: FilterConditionField.Recipient, name: i18nFilter('FIELD_RECIPIENTS') },
{ id: FilterConditionField.Subject, name: i18n('GLOBAL/SUBJECT') },
{ id: FilterConditionField.Size, name: i18nFilter('FIELD_SIZE') },
{ id: FilterConditionField.Header, name: i18nFilter('FIELD_HEADER') }
]);
this.typeOptions([
{ id: FilterConditionType.Contains, name: i18nFilter('TYPE_CONTAINS') },
{ id: FilterConditionType.NotContains, name: i18nFilter('TYPE_NOT_CONTAINS') },
{ id: FilterConditionType.EqualTo, name: i18nFilter('TYPE_EQUAL_TO') },
{ id: FilterConditionType.NotEqualTo, name: i18nFilter('TYPE_NOT_EQUAL_TO') }
]);
// this.actionTypeOptions.push({id: FilterAction.None,
// name: i18n('GLOBAL/NONE')});
const modules = SieveUserStore.capa;
if (modules) {
if (modules.includes('imap4flags')) {
this.allowMarkAsRead(true);
}
if (modules.includes('fileinto')) {
this.actionTypeOptions.push({
id: FilterAction.MoveTo,
name: i18nFilter('ACTION_MOVE_TO')
});
this.actionTypeOptions.push({
id: FilterAction.Forward,
name: i18nFilter('ACTION_FORWARD_TO')
});
}
if (modules.includes('reject')) {
this.actionTypeOptions.push({ id: FilterAction.Reject, name: i18nFilter('ACTION_REJECT') });
}
if (modules.includes('vacation')) {
this.actionTypeOptions.push({
id: FilterAction.Vacation,
name: i18nFilter('ACTION_VACATION_MESSAGE')
});
}
if (modules.includes('body')) {
this.fieldOptions.push({ id: FilterConditionField.Body, name: i18nFilter('FIELD_BODY') });
}
if (modules.includes('regex')) {
this.typeOptions.push({ id: FilterConditionType.Regex, name: 'Regex' });
}
}
this.actionTypeOptions.push({ id: FilterAction.Discard, name: i18nFilter('ACTION_DISCARD') });
this.typeOptionsSize([
{ id: FilterConditionType.Over, name: i18nFilter('TYPE_OVER') },
{ id: FilterConditionType.Under, name: i18nFilter('TYPE_UNDER') }
]);
this.typeOptionsBody([
{ id: FilterConditionType.Text, name: i18nFilter('TYPE_TEXT') },
{ id: FilterConditionType.Raw, name: i18nFilter('TYPE_RAW') }
]);
}
removeCondition(oConditionToDelete) {
if (this.filter()) {
this.filter().removeCondition(oConditionToDelete);
}
}
clearPopup() {
this.isNew(true);
this.fTrueCallback = null;
this.filter(null);
}
onShow(oFilter, fTrueCallback, bEdit) {
this.clearPopup();
this.fTrueCallback = fTrueCallback;
this.filter(oFilter);
if (oFilter) {
this.selectedFolderValue(oFilter.actionValue());
}
this.isNew(!bEdit);
if (!bEdit && oFilter) {
oFilter.nameFocused(true);
}
}
onShowWithDelay() {
this.isNew() && this.filter() && this.filter().nameFocused(true);
}
}
export { FilterPopupView, FilterPopupView as default };

View file

@ -2,13 +2,14 @@ import { i18n, getNotification } from 'Common/Translator';
import { setFolderHash } from 'Common/Cache';
import { MessageUserStore } from 'Stores/User/Message';
import { MessagelistUserStore } from 'Stores/User/Messagelist';
import Remote from 'Remote/User/Fetch';
import { decorateKoCommands } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
class FolderClearPopupView extends AbstractViewPopup {
export class FolderClearPopupView extends AbstractViewPopup {
constructor() {
super('FolderClear');
@ -19,17 +20,11 @@ class FolderClearPopupView extends AbstractViewPopup {
});
this.addComputables({
folderFullNameForClear: () => {
dangerDescHtml: () => {
const folder = this.selectedFolder();
return folder ? folder.printableFullName() : '';
},
folderNameForClear: () => {
const folder = this.selectedFolder();
return folder ? folder.localName() : '';
},
dangerDescHtml: () => i18n('POPUPS_CLEAR_FOLDER/DANGER_DESC_HTML_1', { FOLDER: this.folderNameForClear() })
// return i18n('POPUPS_CLEAR_FOLDER/DANGER_DESC_HTML_1', { FOLDER: folder ? folder.fullName.replace(folder.delimiter, ' / ') : '' });
return i18n('POPUPS_CLEAR_FOLDER/DANGER_DESC_HTML_1', { FOLDER: folder ? folder.localName() : '' });
}
});
decorateKoCommands(this, {
@ -44,38 +39,31 @@ class FolderClearPopupView extends AbstractViewPopup {
const folderToClear = this.selectedFolder();
if (folderToClear) {
MessageUserStore.message(null);
MessageUserStore.list([]);
MessagelistUserStore([]);
this.clearingProcess(true);
folderToClear.messageCountAll(0);
folderToClear.messageCountUnread(0);
setFolderHash(folderToClear.fullNameRaw, '');
setFolderHash(folderToClear.fullName, '');
Remote.folderClear(iError => {
Remote.request('FolderClear', iError => {
this.clearingProcess(false);
if (iError) {
this.clearingError(getNotification(iError));
} else {
rl.app.reloadMessageList(true);
this.cancelCommand();
MessagelistUserStore.reload(true);
this.close();
}
}, folderToClear.fullNameRaw);
}, {
Folder: folderToClear.fullName
});
}
}
clearPopup() {
this.clearingProcess(false);
this.selectedFolder(null);
}
onShow(folder) {
this.clearPopup();
if (folder) {
this.selectedFolder(folder);
}
this.clearingProcess(false);
this.selectedFolder(folder || null);
}
}
export { FolderClearPopupView, FolderClearPopupView as default };

View file

@ -1,73 +1,86 @@
import ko from 'ko';
import { koComputable } from 'External/ko';
import { Notification } from 'Common/Enums';
import { UNUSED_OPTION_VALUE } from 'Common/Consts';
import { defaultOptionsAfterRender } from 'Common/Utils';
import { folderListOptionsBuilder } from 'Common/UtilsUser';
import { folderListOptionsBuilder, sortFolders } from 'Common/Folders';
import { getNotification } from 'Common/Translator';
import { FolderUserStore } from 'Stores/User/Folder';
import { SettingsUserStore } from 'Stores/User/Settings';
import Remote from 'Remote/User/Fetch';
import { decorateKoCommands } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
class FolderCreatePopupView extends AbstractViewPopup {
import { setFolder, getFolderFromCacheList } from 'Common/Cache';
import { FolderModel } from 'Model/FolderCollection';
export class FolderCreatePopupView extends AbstractViewPopup {
constructor() {
super('FolderCreate');
this.addObservables({
folderName: '',
folderSubscribe: SettingsUserStore.hideUnsubscribed(),
selectedParentValue: UNUSED_OPTION_VALUE
});
this.parentFolderSelectList = ko.computed(() =>
this.parentFolderSelectList = koComputable(() =>
folderListOptionsBuilder(
[],
[['', '']],
oItem =>
oItem ? (oItem.isSystemFolder() ? oItem.name() + ' ' + oItem.manageFolderSystemName() : oItem.name()) : '',
FolderUserStore.namespace
? item => FolderUserStore.namespace !== item.fullNameRaw.substr(0, FolderUserStore.namespace.length)
? item => FolderUserStore.namespace !== item.fullName.slice(0, FolderUserStore.namespace.length)
: null,
true
)
);
this.defaultOptionsAfterRender = defaultOptionsAfterRender;
decorateKoCommands(this, {
createFolderCommand: self => self.simpleFolderNameValidation(self.folderName())
});
}
createFolderCommand() {
let parentFolderName = this.selectedParentValue();
if (!parentFolderName && 1 < FolderUserStore.namespace.length) {
parentFolderName = FolderUserStore.namespace.substr(0, FolderUserStore.namespace.length - 1);
submitForm() {
if (/^[^\\/]+$/g.test(this.folderName())) {
let parentFolderName = this.selectedParentValue();
if (!parentFolderName && 1 < FolderUserStore.namespace.length) {
parentFolderName = FolderUserStore.namespace.slice(0, FolderUserStore.namespace.length - 1);
}
Remote.abort('Folders').post('FolderCreate', FolderUserStore.foldersCreating, {
Folder: this.folderName(),
Parent: parentFolderName,
Subscribe: this.folderSubscribe() ? 1 : 0
})
.then(
data => {
const folder = getFolderFromCacheList(parentFolderName),
subFolder = FolderModel.reviveFromJson(data.Result),
folders = (folder ? folder.subFolders : FolderUserStore.folderList);
setFolder(subFolder);
folders.push(subFolder);
sortFolders(folders);
/*
var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
console.log((folder ? folder.subFolders : FolderUserStore.folderList).sort(collator.compare));
*/
},
error => {
FolderUserStore.folderListError(
getNotification(error.code, '', Notification.CantCreateFolder)
+ '.\n' + error.message);
}
);
this.close();
}
rl.app.foldersPromisesActionHelper(
Remote.folderCreate(this.folderName(), parentFolderName),
Notification.CantCreateFolder
);
this.cancelCommand();
}
simpleFolderNameValidation(sName) {
return /^[^\\/]+$/g.test(sName);
}
clearPopup() {
this.folderName('');
this.selectedParentValue('');
}
onShow() {
this.clearPopup();
this.folderName('');
this.selectedParentValue('');
}
}
export { FolderCreatePopupView, FolderCreatePopupView as default };

View file

@ -1,41 +1,34 @@
import ko from 'ko';
import { koComputable, addSubscribablesTo } from 'External/ko';
import { SetSystemFoldersNotification } from 'Common/EnumsUser';
import { UNUSED_OPTION_VALUE } from 'Common/Consts';
import { defaultOptionsAfterRender, addSubscribablesTo } from 'Common/Utils';
import { folderListOptionsBuilder } from 'Common/UtilsUser';
import { initOnStartOrLangChange, i18n } from 'Common/Translator';
import { defaultOptionsAfterRender } from 'Common/Utils';
import { folderListOptionsBuilder } from 'Common/Folders';
import { i18n } from 'Common/Translator';
import { FolderUserStore } from 'Stores/User/Folder';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
class FolderSystemPopupView extends AbstractViewPopup {
export class FolderSystemPopupView extends AbstractViewPopup {
constructor() {
super('FolderSystem');
this.sChooseOnText = '';
this.sUnuseText = '';
initOnStartOrLangChange(() => {
this.sChooseOnText = i18n('POPUPS_SYSTEM_FOLDERS/SELECT_CHOOSE_ONE');
this.sUnuseText = i18n('POPUPS_SYSTEM_FOLDERS/SELECT_UNUSE_NAME');
});
this.notification = ko.observable('');
this.folderSelectList = ko.computed(() =>
this.folderSelectList = koComputable(() =>
folderListOptionsBuilder(
FolderUserStore.folderListSystemNames(),
[
['', this.sChooseOnText],
[UNUSED_OPTION_VALUE, this.sUnuseText]
['', i18n('POPUPS_SYSTEM_FOLDERS/SELECT_CHOOSE_ONE')],
[UNUSED_OPTION_VALUE, i18n('POPUPS_SYSTEM_FOLDERS/SELECT_UNUSE_NAME')]
]
)
);
this.sentFolder = FolderUserStore.sentFolder;
this.draftFolder = FolderUserStore.draftFolder;
this.draftsFolder = FolderUserStore.draftsFolder;
this.spamFolder = FolderUserStore.spamFolder;
this.trashFolder = FolderUserStore.trashFolder;
this.archiveFolder = FolderUserStore.archiveFolder;
@ -44,7 +37,7 @@ class FolderSystemPopupView extends AbstractViewPopup {
addSubscribablesTo(FolderUserStore, {
sentFolder: fSaveSystemFolders,
draftFolder: fSaveSystemFolders,
draftsFolder: fSaveSystemFolders,
spamFolder: fSaveSystemFolders,
trashFolder: fSaveSystemFolders,
archiveFolder: fSaveSystemFolders
@ -80,5 +73,3 @@ class FolderSystemPopupView extends AbstractViewPopup {
this.notification(notification);
}
}
export { FolderSystemPopupView, FolderSystemPopupView as default };

View file

@ -2,12 +2,11 @@ import { getNotification } from 'Common/Translator';
import Remote from 'Remote/User/Fetch';
import { decorateKoCommands } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
const reEmail = /^[^@\s]+@[^@\s]+$/;
class IdentityPopupView extends AbstractViewPopup {
export class IdentityPopupView extends AbstractViewPopup {
constructor() {
super('Identity');
@ -60,60 +59,57 @@ class IdentityPopupView extends AbstractViewPopup {
this.replyTo.valueHasMutated();
this.bcc.valueHasMutated();
*/
decorateKoCommands(this, {
addOrEditIdentityCommand: self => !self.submitRequest()
});
}
addOrEditIdentityCommand() {
if (this.signature && this.signature.__fetchEditorValue) {
this.signature.__fetchEditorValue();
}
if (!this.emailHasError()) {
this.emailHasError(!this.email().trim());
}
if (this.emailHasError()) {
if (!this.owner()) {
this.emailFocused(true);
submitForm() {
if (!this.submitRequest()) {
if (this.signature && this.signature.__fetchEditorValue) {
this.signature.__fetchEditorValue();
}
return false;
}
if (!this.emailHasError()) {
this.emailHasError(!this.email().trim());
}
if (this.replyToHasError()) {
this.replyToFocused(true);
return false;
}
if (this.bccHasError()) {
this.bccFocused(true);
return false;
}
this.submitRequest(true);
Remote.identityUpdate(
iError => {
this.submitRequest(false);
if (iError) {
this.submitError(getNotification(iError));
} else {
rl.app.accountsAndIdentities();
this.cancelCommand();
if (this.emailHasError()) {
if (!this.owner()) {
this.emailFocused(true);
}
},
this.id,
this.email(),
this.name(),
this.replyTo(),
this.bcc(),
this.signature(),
this.signatureInsertBefore()
);
return true;
return;
}
if (this.replyToHasError()) {
this.replyToFocused(true);
return;
}
if (this.bccHasError()) {
this.bccFocused(true);
return;
}
this.submitRequest(true);
Remote.request('IdentityUpdate', iError => {
this.submitRequest(false);
if (iError) {
this.submitError(getNotification(iError));
} else {
rl.app.accountsAndIdentities();
this.close();
}
}, {
Id: this.id,
Email: this.email(),
Name: this.name(),
ReplyTo: this.replyTo(),
Bcc: this.bcc(),
Signature: this.signature(),
SignatureInsertBefore: this.signatureInsertBefore() ? 1 : 0
}
);
}
}
clearPopup() {
@ -162,13 +158,11 @@ class IdentityPopupView extends AbstractViewPopup {
}
}
onShowWithDelay() {
afterShow() {
this.owner() || this.emailFocused(true);
}
onHideWithDelay() {
afterHide() {
this.clearPopup();
}
}
export { IdentityPopupView, IdentityPopupView as default };

View file

@ -1,5 +1,4 @@
import { Scope } from 'Common/Enums';
import { addShortcut } from 'Common/Globals';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
export class KeyboardShortcutsHelpPopupView extends AbstractViewPopup {
@ -12,9 +11,9 @@ export class KeyboardShortcutsHelpPopupView extends AbstractViewPopup {
const tabs = dom.querySelectorAll('.tabs input'),
last = tabs.length - 1;
// shortcuts.add('tab', 'shift',
shortcuts.add('tab,arrowleft,arrowright', '',
Scope.KeyboardShortcutsHelp,
// addShortcut('tab', 'shift',
addShortcut('tab,arrowleft,arrowright', '',
'KeyboardShortcutsHelp',
event => {
let next = 0;
tabs.forEach((node, index) => {

View file

@ -1,10 +1,11 @@
import ko from 'ko';
import { koComputable } from 'External/ko';
import { convertLangName } from 'Common/Translator';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
class LanguagesPopupView extends AbstractViewPopup {
export class LanguagesPopupView extends AbstractViewPopup {
constructor() {
super('Languages');
@ -13,7 +14,7 @@ class LanguagesPopupView extends AbstractViewPopup {
this.langs = ko.observableArray();
this.languages = ko.computed(() => {
this.languages = koComputable(() => {
const userLanguage = this.userLanguage();
return this.langs.map(language => ({
key: language,
@ -35,7 +36,7 @@ class LanguagesPopupView extends AbstractViewPopup {
this.languages().forEach(item => item.selected(item.key === currentLang));
}
onBeforeShow() {
beforeShow() {
this.fLang = null;
this.userLanguage('');
@ -51,8 +52,6 @@ class LanguagesPopupView extends AbstractViewPopup {
changeLanguage(lang) {
this.fLang && this.fLang(lang);
this.cancelCommand();
this.close();
}
}
export { LanguagesPopupView, LanguagesPopupView as default };

View file

@ -1,121 +0,0 @@
import ko from 'ko';
import { pString } from 'Common/Utils';
import { Scope } from 'Common/Enums';
import { decorateKoCommands } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
class MessageOpenPgpPopupView extends AbstractViewPopup {
constructor() {
super('MessageOpenPgp');
this.addObservables({
notification: '',
selectedKey: null,
password: '',
submitRequest: false
});
this.privateKeys = ko.observableArray();
this.resultCallback = null;
decorateKoCommands(this, {
doCommand: self => !self.submitRequest()
});
}
doCommand() {
this.submitRequest(true);
setTimeout(() => {
let privateKey = null;
try {
if (this.resultCallback && this.selectedKey()) {
const privateKeys = this.selectedKey().getNativeKeys();
privateKey = privateKeys && privateKeys[0] ? privateKeys[0] : null;
if (privateKey) {
try {
if (!privateKey.decrypt(pString(this.password()))) {
console.log('Error: Private key cannot be decrypted');
privateKey = null;
}
} catch (e) {
console.log(e);
privateKey = null;
}
} else {
console.log('Error: Private key cannot be found');
}
}
} catch (e) {
console.log(e);
privateKey = null;
}
this.submitRequest(false);
this.cancelCommand();
this.resultCallback(privateKey);
}, 100);
}
clearPopup() {
this.notification('');
this.password('');
this.selectedKey(false);
this.submitRequest(false);
this.resultCallback = null;
this.privateKeys([]);
}
onBuild(oDom) {
// shortcuts.add('tab', 'shift', Scope.MessageOpenPgp, () => {
shortcuts.add('tab', '', Scope.MessageOpenPgp, () => {
let btn = this.querySelector('.inputPassword');
if (btn.matches(':focus')) {
btn = this.querySelector('.buttonDo');
}
btn.focus();
return false;
});
const self = this;
oDom.addEventListener('click', event => {
const el = event.target.closestWithin('.key-list__item', oDom);
if (el) {
oDom.querySelectorAll('.key-list__item .key-list__item__radio').forEach(node =>
node.textContent = el === node ? '⦿' : '○'
);
self.selectedKey(ko.dataFor(el));
// this.querySelector('.inputPassword').focus();
}
});
}
onHideWithDelay() {
this.clearPopup();
}
onShow(fCallback, privateKeys) {
this.clearPopup();
this.resultCallback = fCallback;
this.privateKeys(privateKeys);
if (this.viewModelDom) {
const el = this.viewModelDom.querySelector('.key-list__item');
el && el.click();
}
}
}
export { MessageOpenPgpPopupView, MessageOpenPgpPopupView as default };

View file

@ -1,105 +0,0 @@
import { pInt } from 'Common/Utils';
import { PgpUserStore } from 'Stores/User/Pgp';
import { decorateKoCommands } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
class NewOpenPgpKeyPopupView extends AbstractViewPopup {
constructor() {
super('NewOpenPgpKey');
this.addObservables({
email: '',
emailError: false,
name: '',
password: '',
keyBitLength: 4096,
submitRequest: false,
submitError: ''
});
this.email.subscribe(() => this.emailError(false));
decorateKoCommands(this, {
generateOpenPgpKeyCommand: 1
});
}
generateOpenPgpKeyCommand() {
const userId = {},
openpgpKeyring = PgpUserStore.openpgpKeyring;
this.emailError(!this.email().trim());
if (!openpgpKeyring || this.emailError()) {
return false;
}
userId.email = this.email();
if (this.name()) {
userId.name = this.name();
}
this.submitRequest(true);
this.submitError('');
setTimeout(() => {
try {
PgpUserStore.openpgp
.generateKey({
userIds: [userId],
numBits: pInt(this.keyBitLength()),
passphrase: this.password().trim()
})
.then((keyPair) => {
this.submitRequest(false);
if (keyPair && keyPair.privateKeyArmored) {
openpgpKeyring.privateKeys.importKey(keyPair.privateKeyArmored);
openpgpKeyring.publicKeys.importKey(keyPair.publicKeyArmored);
openpgpKeyring.store();
rl.app.reloadOpenPgpKeys();
this.cancelCommand();
}
})
.catch((e) => {
this.submitRequest(false);
this.showError(e);
});
} catch (e) {
this.submitRequest(false);
this.showError(e);
}
}, 100);
return true;
}
showError(e) {
console.log(e);
if (e && e.message) {
this.submitError(e.message);
}
}
clearPopup() {
this.name('');
this.password('');
this.email('');
this.emailError(false);
this.keyBitLength(4096);
this.submitError('');
}
onShow() {
this.clearPopup();
}
}
export { NewOpenPgpKeyPopupView, NewOpenPgpKeyPopupView as default };

View file

@ -0,0 +1,109 @@
//import { pInt } from 'Common/Utils';
import { GnuPGUserStore } from 'Stores/User/GnuPG';
import { OpenPGPUserStore } from 'Stores/User/OpenPGP';
import { IdentityUserStore } from 'Stores/User/Identity';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
import { SettingsCapa } from 'Common/Globals';
export class OpenPgpGeneratePopupView extends AbstractViewPopup {
constructor() {
super('OpenPgpGenerate');
this.identities = IdentityUserStore;
this.addObservables({
email: '',
emailError: false,
name: '',
password: '',
keyType: 'ECC',
submitRequest: false,
submitError: '',
backupPublicKey: true,
backupPrivateKey: false,
saveGnuPGPublic: true,
saveGnuPGPrivate: false
});
this.canGnuPG = SettingsCapa('GnuPG');
this.email.subscribe(() => this.emailError(false));
}
submitForm() {
const type = this.keyType().toLowerCase(),
userId = {
name: this.name(),
email: this.email()
},
cfg = {
type: type,
userIDs: [userId],
passphrase: this.password().trim()
// format: 'armored' // output key format, defaults to 'armored' (other options: 'binary' or 'object')
}
/*
if ('ecc' === type) {
cfg.curve = 'curve25519';
} else {
cfg.rsaBits = pInt(this.keyBitLength());
}
*/
this.emailError(!this.email().trim());
if (this.emailError()) {
return;
}
this.submitRequest(true);
this.submitError('');
openpgp.generateKey(cfg).then(keyPair => {
if (keyPair) {
const fn = () => {
this.submitRequest(false);
this.close();
};
OpenPGPUserStore.storeKeyPair(keyPair);
keyPair.onServer = (this.backupPublicKey() ? 1 : 0) + (this.backupPrivateKey() ? 2 : 0);
keyPair.inGnuPG = (this.saveGnuPGPublic() ? 1 : 0) + (this.saveGnuPGPrivate() ? 2 : 0);
if (keyPair.onServer || keyPair.inGnuPG) {
if (!this.backupPrivateKey() && !this.saveGnuPGPrivate()) {
delete keyPair.privateKey;
}
GnuPGUserStore.storeKeyPair(keyPair, fn);
} else {
fn();
}
}
})
.catch((e) => {
this.submitRequest(false);
this.showError(e);
});
}
showError(e) {
console.log(e);
if (e && e.message) {
this.submitError(e.message);
}
}
onShow() {
this.name(''/*IdentityUserStore()[0].name()*/);
this.password('');
this.email(''/*IdentityUserStore()[0].email()*/);
this.emailError(false);
this.submitError('');
}
}

View file

@ -0,0 +1,74 @@
import { GnuPGUserStore } from 'Stores/User/GnuPG';
import { OpenPGPUserStore } from 'Stores/User/OpenPGP';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
export class OpenPgpImportPopupView extends AbstractViewPopup {
constructor() {
super('OpenPgpImport');
this.addObservables({
key: '',
keyError: false,
keyErrorMessage: '',
saveGnuPG: true,
saveServer: false
});
this.canGnuPG = GnuPGUserStore.isSupported();
this.key.subscribe(() => {
this.keyError(false);
this.keyErrorMessage('');
});
}
submitForm() {
let keyTrimmed = this.key().trim();
if (/\n/.test(keyTrimmed)) {
keyTrimmed = keyTrimmed.replace(/\r+/g, '').replace(/\n{2,}/g, '\n\n');
}
this.keyError(!keyTrimmed);
this.keyErrorMessage('');
if (!keyTrimmed) {
return;
}
let match = null,
count = 30,
done = false;
// eslint-disable-next-line max-len
const reg = /[-]{3,6}BEGIN[\s]PGP[\s](PRIVATE|PUBLIC)[\s]KEY[\s]BLOCK[-]{3,6}[\s\S]+?[-]{3,6}END[\s]PGP[\s](PRIVATE|PUBLIC)[\s]KEY[\s]BLOCK[-]{3,6}/gi;
do {
match = reg.exec(keyTrimmed);
if (match && 0 < count) {
if (match[0] && match[1] && match[2] && match[1] === match[2]) {
this.saveGnuPG() && GnuPGUserStore.isSupported() && GnuPGUserStore.importKey(this.key());
OpenPGPUserStore.isSupported() && OpenPGPUserStore.importKey(this.key());
}
--count;
done = false;
} else {
done = true;
}
} while (!done);
if (this.keyError()) {
return;
}
this.close();
}
onShow() {
this.key('');
this.keyError(false);
this.keyErrorMessage('');
}
}

View file

@ -0,0 +1,126 @@
import { doc, addShortcut } from 'Common/Globals';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
export class OpenPgpKeyPopupView extends AbstractViewPopup {
constructor() {
super('OpenPgpKey');
this.addObservables({
key: '',
keyDom: null
});
}
selectKey() {
const el = this.keyDom();
if (el) {
let sel = getSelection(),
range = doc.createRange();
sel.removeAllRanges();
range.selectNodeContents(el);
sel.addRange(range);
}
if (navigator.clipboard) {
navigator.clipboard.writeText(this.key()).then(
() => console.log('Copied to clipboard'),
err => console.error(err)
);
}
}
onShow(openPgpKey) {
// TODO: show more info
this.key(openPgpKey ? openPgpKey.armor : '');
/*
this.key = key;
const aEmails = [];
if (key.users) {
key.users.forEach(user => user.userID.email && aEmails.push(user.userID.email));
}
this.id = key.getKeyID().toHex();
this.fingerprint = key.getFingerprint();
this.can_encrypt = !!key.getEncryptionKey();
this.can_sign = !!key.getSigningKey();
this.emails = aEmails;
this.armor = armor;
this.askDelete = ko.observable(false);
this.openForDeletion = ko.observable(null).askDeleteHelper();
key.id = key.subkeys[0].keyid;
key.fingerprint = key.subkeys[0].fingerprint;
key.uids.forEach(uid => uid.email && aEmails.push(uid.email));
key.emails = aEmails;
"disabled": false,
"expired": false,
"revoked": false,
"is_secret": true,
"can_sign": true,
"can_decrypt": true
"can_verify": true
"can_encrypt": true,
"uids": [
{
"name": "demo",
"comment": "",
"email": "demo@snappymail.eu",
"uid": "demo <demo@snappymail.eu>",
"revoked": false,
"invalid": false
}
],
"subkeys": [
{
"fingerprint": "2C223F20EA2ADB4CB68F81D95F3A5CDC09AD8AE3",
"keyid": "5F3A5CDC09AD8AE3",
"timestamp": 1643381672,
"expires": 0,
"is_secret": false,
"invalid": false,
"can_encrypt": false,
"can_sign": true,
"disabled": false,
"expired": false,
"revoked": false,
"can_certify": true,
"can_authenticate": false,
"is_qualified": false,
"is_de_vs": false,
"pubkey_algo": 303,
"length": 256,
"keygrip": "5A1A6C7310D0508C68E8E74F15068301E83FD1AE",
"is_cardkey": false,
"curve": "ed25519"
},
{
"fingerprint": "3CD720549D8833872C267D08F1230DCE2A561ADE",
"keyid": "F1230DCE2A561ADE",
"timestamp": 1643381672,
"expires": 0,
"is_secret": false,
"invalid": false,
"can_encrypt": true,
"can_sign": false,
"disabled": false,
"expired": false,
"revoked": false,
"can_certify": false,
"can_authenticate": false,
"is_qualified": false,
"is_de_vs": false,
"pubkey_algo": 302,
"length": 256,
"keygrip": "886921A7E06BE56F8E8C51797BB476BB26DF21BF",
"is_cardkey": false,
"curve": "cv25519"
}
]
*/
}
onBuild() {
addShortcut('a', 'meta', 'OpenPgpKey', () => {
this.selectKey();
return false;
});
}
}

View file

@ -1,16 +1,15 @@
import ko from 'ko';
import { Scope } from 'Common/Enums';
import { getNotification, i18n } from 'Common/Translator';
import { arrayLength } from 'Common/Utils';
import Remote from 'Remote/Admin/Fetch';
import { decorateKoCommands, isPopupVisible, showScreenPopup } from 'Knoin/Knoin';
import { decorateKoCommands, showScreenPopup } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
import { AskPopupView } from 'View/Popup/Ask';
class PluginPopupView extends AbstractViewPopup {
export class PluginPopupView extends AbstractViewPopup {
constructor() {
super('Plugin');
@ -21,17 +20,14 @@ class PluginPopupView extends AbstractViewPopup {
readme: ''
});
this.configures = ko.observableArray();
this.config = ko.observableArray();
this.addComputables({
hasReadme: () => !!this.readme(),
hasConfiguration: () => 0 < this.configures().length
hasConfiguration: () => 0 < this.config().length
});
this.bDisabeCloseOnEsc = true;
this.sDefaultScope = Scope.All;
this.tryToClosePopup = this.tryToClosePopup.debounce(200);
this.keyScope.scope = 'all';
decorateKoCommands(this, {
saveCommand: self => self.hasConfiguration()
@ -39,32 +35,40 @@ class PluginPopupView extends AbstractViewPopup {
}
saveCommand() {
const list = {
const oConfig = {
Id: this.id(),
Settings: {}
};
this.configures.forEach(oItem => {
let value = oItem.value();
},
setItem = item => {
let value = item.value();
if (false === value || true === value) {
value = value ? 1 : 0;
}
list.Settings[oItem.Name] = value;
oConfig.Settings[item.Name] = value;
};
this.config.forEach(oItem => {
if (7 == oItem.Type) {
// Group
oItem.config.forEach(oSubItem => setItem(oSubItem));
} else {
setItem(oItem);
}
});
this.saveError('');
Remote.pluginSettingsUpdate(iError =>
iError
Remote.request('AdminPluginSettingsUpdate',
iError => iError
? this.saveError(getNotification(iError))
: this.cancelCommand()
, list);
: this.close(),
oConfig);
}
onShow(oPlugin) {
this.id('');
this.name('');
this.readme('');
this.configures([]);
this.config([]);
if (oPlugin) {
this.id(oPlugin.Id);
@ -73,38 +77,30 @@ class PluginPopupView extends AbstractViewPopup {
const config = oPlugin.Config;
if (arrayLength(config)) {
this.configures(
config.map(item => ({
value: ko.observable(item[0]),
placeholder: ko.observable(item[6]),
Name: item[1],
Type: item[2],
Label: item[3],
Default: item[4],
Desc: item[5]
}))
this.config(
config.map(item => {
if (7 == item.Type) {
// Group
item.config.forEach(subItem => {
subItem.value = ko.observable(subItem.value);
});
} else {
item.value = ko.observable(item.value);
}
return item;
})
);
}
}
}
tryToClosePopup() {
if (!isPopupVisible(AskPopupView)) {
onClose() {
if (AskPopupView.hidden()) {
showScreenPopup(AskPopupView, [
i18n('POPUPS_ASK/DESC_WANT_CLOSE_THIS_WINDOW'),
() => this.modalVisibility() && this.cancelCommand()
() => this.close()
]);
}
}
onBuild() {
shortcuts.add('escape', '', Scope.All, () => {
if (this.modalVisibility()) {
this.tryToClosePopup();
return false;
}
});
return false;
}
}
export { PluginPopupView, PluginPopupView as default };

View file

@ -1,146 +0,0 @@
import ko from 'ko';
import { getNotification, i18nToNodes } from 'Common/Translator';
import { addObservablesTo } from 'Common/Utils';
import { delegateRunOnDestroy } from 'Common/UtilsUser';
import Remote from 'Remote/User/Fetch';
import { FilterModel } from 'Model/Filter';
import { SieveUserStore } from 'Stores/User/Sieve';
import { showScreenPopup/*, decorateKoCommands*/ } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
import { FilterPopupView } from 'View/Popup/Filter';
class SieveScriptPopupView extends AbstractViewPopup {
constructor() {
super('SieveScript');
addObservablesTo(this, {
saveError: false,
saveErrorText: '',
rawActive: false,
allowToggle: false,
script: null
});
this.sieveCapabilities = SieveUserStore.capa.join(' ');
this.saving = false;
this.filterForDeletion = ko.observable(null).deleteAccessHelper();
/*
decorateKoCommands(this, {
saveScriptCommand: 1
});
*/
}
saveScriptCommand() {
let self = this,
script = self.script();
if (!self.saving/* && script.hasChanges()*/) {
if (!script.verify()) {
return false;
}
if (!script.exists() && SieveUserStore.scripts.find(item => item.name() === script.name())) {
script.nameError(true);
return false;
}
self.saving = true;
self.saveError(false);
if (self.allowToggle()) {
script.body(script.filtersToRaw());
}
Remote.filtersScriptSave(
(iError, data) => {
self.saving = false;
if (iError) {
self.saveError(true);
self.saveErrorText((data && data.ErrorMessageAdditional) || getNotification(iError));
} else {
script.exists() || SieveUserStore.scripts.push(script);
script.exists(true);
script.hasChanges(false);
}
},
script
);
}
return true;
}
deleteFilter(filter) {
this.script().filters.remove(filter);
delegateRunOnDestroy(filter);
}
addFilter() {
/* this = SieveScriptModel */
const filter = new FilterModel();
filter.generateID();
showScreenPopup(FilterPopupView, [
filter,
() => {
this.filters.push(filter);
},
false
]);
}
editFilter(filter) {
const clonedFilter = filter.cloneSelf();
showScreenPopup(FilterPopupView, [
clonedFilter,
() => {
const script = this.script(),
filters = script.filters(),
index = filters.indexOf(filter);
if (-1 < index) {
delegateRunOnDestroy(filters[index]);
filters[index] = clonedFilter;
script.filters(filters);
}
},
true
]);
}
toggleFiltersRaw() {
if (!this.rawActive()) {
let script = this.script(),
changed = script.hasChanges();
script.body(script.filtersToRaw());
script.hasChanges(changed);
}
this.rawActive(!this.rawActive());
}
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) {
this.script(oScript);
this.rawActive(!oScript.allowFilters());
this.allowToggle(oScript.allowFilters());
this.saveError(false);
}
onShowWithDelay() {
// Sometimes not everything is translated, try again
i18nToNodes(this.viewModelDom);
}
}
export { SieveScriptPopupView, SieveScriptPopupView as default };

View file

@ -1,46 +0,0 @@
import { Scope } from 'Common/Enums';
import { doc } from 'Common/Globals';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
class ViewOpenPgpKeyPopupView extends AbstractViewPopup {
constructor() {
super('ViewOpenPgpKey');
this.addObservables({
key: '',
keyDom: null
});
}
clearPopup() {
this.key('');
}
selectKey() {
const el = this.keyDom();
if (el) {
let sel = getSelection(),
range = doc.createRange();
sel.removeAllRanges();
range.selectNodeContents(el);
sel.addRange(range);
}
}
onShow(openPgpKey) {
this.clearPopup();
if (openPgpKey) {
this.key(openPgpKey.armor);
}
}
onBuild() {
shortcuts.add('a', 'meta', Scope.ViewOpenPgpKey, () => {
this.selectKey();
return false;
});
}
}
export { ViewOpenPgpKeyPopupView, ViewOpenPgpKeyPopupView as default };