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,7 +2,7 @@ import ko from 'ko';
import { pString } from 'Common/Utils';
import { settings } from 'Common/Links';
import { doc, elementById } from 'Common/Globals';
import { createElement, elementById } from 'Common/Globals';
import { AbstractScreen } from 'Knoin/AbstractScreen';
@ -18,52 +18,38 @@ export class AbstractSettingsScreen extends AbstractScreen {
this.menu = ko.observableArray();
this.oCurrentSubScreen = null;
this.setupSettings();
}
/**
* @param {Function=} fCallback
*/
setupSettings(fCallback = null) {
fCallback && fCallback();
}
onRoute(subName) {
let settingsScreen = null,
RoutedSettingsViewModel = null,
viewModelDom = null;
RoutedSettingsViewModel = VIEW_MODELS.find(
SettingsViewModel =>
SettingsViewModel && SettingsViewModel.__rlSettingsData && subName === SettingsViewModel.__rlSettingsData.Route
);
viewModelDom = null,
RoutedSettingsViewModel = VIEW_MODELS.find(
SettingsViewModel => subName === SettingsViewModel.__rlSettingsData.route
);
if (RoutedSettingsViewModel) {
if (RoutedSettingsViewModel.__builded && RoutedSettingsViewModel.__vm) {
if (RoutedSettingsViewModel.__vm) {
settingsScreen = RoutedSettingsViewModel.__vm;
} else {
const vmPlace = elementById('rl-settings-subscreen');
if (vmPlace) {
settingsScreen = new RoutedSettingsViewModel();
viewModelDom = Element.fromHTML('<div class="rl-settings-view-model" hidden=""></div>');
viewModelDom = createElement('div',{
id: 'V-Settings-' + RoutedSettingsViewModel.name.replace(/(User|Admin)Settings/,''),
hidden: ''
})
vmPlace.append(viewModelDom);
settingsScreen = new RoutedSettingsViewModel();
settingsScreen.viewModelDom = viewModelDom;
settingsScreen.__rlSettingsData = RoutedSettingsViewModel.__rlSettingsData;
RoutedSettingsViewModel.__dom = viewModelDom;
RoutedSettingsViewModel.__builded = true;
RoutedSettingsViewModel.__vm = settingsScreen;
const tmpl = { name: RoutedSettingsViewModel.__rlSettingsData.Template };
ko.applyBindingAccessorsToNode(
viewModelDom,
{
i18nInit: true,
template: () => tmpl
template: () => ({ name: RoutedSettingsViewModel.__rlSettingsData.template })
},
settingsScreen
);
@ -75,74 +61,55 @@ export class AbstractSettingsScreen extends AbstractScreen {
}
if (settingsScreen) {
const o = this;
setTimeout(() => {
// hide
if (o.oCurrentSubScreen) {
o.oCurrentSubScreen.onHide && o.oCurrentSubScreen.onHide();
o.oCurrentSubScreen.viewModelDom.hidden = true;
}
this.onHide();
// --
o.oCurrentSubScreen = settingsScreen;
this.oCurrentSubScreen = settingsScreen;
// show
if (o.oCurrentSubScreen) {
o.oCurrentSubScreen.onBeforeShow && o.oCurrentSubScreen.onBeforeShow();
o.oCurrentSubScreen.viewModelDom.hidden = false;
o.oCurrentSubScreen.onShow && o.oCurrentSubScreen.onShow();
settingsScreen.beforeShow && settingsScreen.beforeShow();
settingsScreen.viewModelDom.hidden = false;
settingsScreen.onShow && settingsScreen.onShow();
o.menu.forEach(item => {
item.selected(
settingsScreen &&
settingsScreen.__rlSettingsData &&
item.route === settingsScreen.__rlSettingsData.Route
);
});
this.menu.forEach(item => {
item.selected(
item.route === RoutedSettingsViewModel.__rlSettingsData.route
);
});
doc.querySelector('#rl-content .b-settings .b-content').scrollTop = 0;
}
(elementById('rl-settings-subscreen') || {}).scrollTop = 0;
// --
}, 1);
}
} else {
rl.route.setHash(settings(), false, true);
hasher.replaceHash(settings());
}
}
onHide() {
if (this.oCurrentSubScreen && this.oCurrentSubScreen.viewModelDom) {
this.oCurrentSubScreen.onHide && this.oCurrentSubScreen.onHide();
this.oCurrentSubScreen.viewModelDom.hidden = true;
let subScreen = this.oCurrentSubScreen;
if (subScreen) {
subScreen.onHide && subScreen.onHide();
subScreen.viewModelDom.hidden = true;
}
}
onBuild() {
VIEW_MODELS.forEach(SettingsViewModel => {
if (
SettingsViewModel &&
SettingsViewModel.__rlSettingsData
) {
this.menu.push({
route: SettingsViewModel.__rlSettingsData.Route,
label: SettingsViewModel.__rlSettingsData.Label,
selected: ko.observable(false)
});
}
});
VIEW_MODELS.forEach(SettingsViewModel => this.menu.push(SettingsViewModel.__rlSettingsData));
}
routes() {
const DefaultViewModel = VIEW_MODELS.find(
SettingsViewModel =>
SettingsViewModel && SettingsViewModel.__rlSettingsData && SettingsViewModel.__rlSettingsData.IsDefault
SettingsViewModel => SettingsViewModel.__rlSettingsData.isDefault
),
defaultRoute =
DefaultViewModel && DefaultViewModel.__rlSettingsData ? DefaultViewModel.__rlSettingsData.Route : 'general',
DefaultViewModel ? DefaultViewModel.__rlSettingsData.route : 'general',
rules = {
subname: /^(.*)$/,
normalize_: (rquest, vals) => {
vals.subname = undefined === vals.subname ? defaultRoute : pString(vals.subname);
vals.subname = null == vals.subname ? defaultRoute : pString(vals.subname);
return [vals.subname];
}
};
@ -164,11 +131,13 @@ export class AbstractSettingsScreen extends AbstractScreen {
* @returns {void}
*/
export function settingsAddViewModel(SettingsViewModelClass, template, labelName, route, isDefault = false) {
let name = SettingsViewModelClass.name.replace(/(User|Admin)Settings/, '');
SettingsViewModelClass.__rlSettingsData = {
Label: labelName,
Template: template,
Route: route,
IsDefault: !!isDefault
label: labelName || 'SETTINGS_LABELS/LABEL_' + name.toUpperCase() + '_NAME',
route: route || name.toLowerCase(),
selected: ko.observable(false),
template: template || SettingsViewModelClass.name,
isDefault: !!isDefault
};
VIEW_MODELS.push(SettingsViewModelClass);

View file

@ -1,10 +1,10 @@
import { AbstractScreen } from 'Knoin/AbstractScreen';
import { LoginAdminView } from 'View/Admin/Login';
import { AdminLoginView } from 'View/Admin/Login';
export class LoginAdminScreen extends AbstractScreen {
constructor() {
super('login', [LoginAdminView]);
super('login', [AdminLoginView]);
}
onShow() {

View file

@ -2,14 +2,15 @@ import { runSettingsViewModelHooks } from 'Common/Plugins';
import { AbstractSettingsScreen, settingsAddViewModel } from 'Screen/AbstractSettings';
import { GeneralAdminSettings } from 'Settings/Admin/General';
import { DomainsAdminSettings } from 'Settings/Admin/Domains';
import { LoginAdminSettings } from 'Settings/Admin/Login';
import { ContactsAdminSettings } from 'Settings/Admin/Contacts';
import { SecurityAdminSettings } from 'Settings/Admin/Security';
import { PackagesAdminSettings } from 'Settings/Admin/Packages';
import { AboutAdminSettings } from 'Settings/Admin/About';
import { BrandingAdminSettings } from 'Settings/Admin/Branding';
import { AdminSettingsGeneral } from 'Settings/Admin/General';
import { AdminSettingsDomains } from 'Settings/Admin/Domains';
import { AdminSettingsLogin } from 'Settings/Admin/Login';
import { AdminSettingsContacts } from 'Settings/Admin/Contacts';
import { AdminSettingsSecurity } from 'Settings/Admin/Security';
import { AdminSettingsPackages } from 'Settings/Admin/Packages';
import { AdminSettingsAbout } from 'Settings/Admin/About';
import { AdminSettingsBranding } from 'Settings/Admin/Branding';
import { AdminSettingsConfig } from 'Settings/Admin/Config';
import { MenuSettingsAdminView } from 'View/Admin/Settings/Menu';
import { PaneSettingsAdminView } from 'View/Admin/Settings/Pane';
@ -17,40 +18,22 @@ import { PaneSettingsAdminView } from 'View/Admin/Settings/Pane';
export class SettingsAdminScreen extends AbstractSettingsScreen {
constructor() {
super([MenuSettingsAdminView, PaneSettingsAdminView]);
}
/**
* @param {Function=} fCallback = null
*/
setupSettings(fCallback = null) {
settingsAddViewModel(
GeneralAdminSettings,
'AdminSettingsGeneral',
'TABS_LABELS/LABEL_GENERAL_NAME',
'general',
true
);
[
[DomainsAdminSettings, 'Domains'],
[LoginAdminSettings, 'Login'],
[BrandingAdminSettings, 'Branding'],
[ContactsAdminSettings, 'Contacts'],
[SecurityAdminSettings, 'Security'],
[PackagesAdminSettings, 'Packages'],
[AboutAdminSettings, 'About'],
].forEach(item =>
settingsAddViewModel(
item[0],
'AdminSettings'+item[1],
'TABS_LABELS/LABEL_'+item[1].toUpperCase()+'_NAME',
item[1].toLowerCase()
)
AdminSettingsGeneral,
AdminSettingsDomains,
AdminSettingsLogin,
AdminSettingsBranding,
AdminSettingsContacts,
AdminSettingsSecurity,
AdminSettingsPackages,
AdminSettingsConfig,
AdminSettingsAbout
].forEach((item, index) =>
settingsAddViewModel(item, 0, 0, 0, 0 === index)
);
runSettingsViewModelHooks(true);
fCallback && fCallback();
}
onShow() {

View file

@ -1,29 +1,32 @@
import { Scope } from 'Common/Enums';
import { doc, leftPanelDisabled, moveAction, Settings } from 'Common/Globals';
import { Layout, ClientSideKeyNameMessageListSize } from 'Common/EnumsUser';
import { doc, leftPanelDisabled, moveAction, Settings, elementById } from 'Common/Globals';
import { pString, pInt } from 'Common/Utils';
import { getFolderFromCacheList, getFolderFullNameRaw, getFolderInboxName } from 'Common/Cache';
import { setLayoutResizer } from 'Common/UtilsUser';
import { getFolderFromCacheList, getFolderFullName, getFolderInboxName } from 'Common/Cache';
import { i18n } from 'Common/Translator';
import { SettingsUserStore } from 'Stores/User/Settings';
import { AppUserStore } from 'Stores/User/App';
import { AccountUserStore } from 'Stores/User/Account';
import { FolderUserStore } from 'Stores/User/Folder';
import { MessageUserStore } from 'Stores/User/Message';
import { MessagelistUserStore } from 'Stores/User/Messagelist';
import { ThemeStore } from 'Stores/Theme';
import { SystemDropDownMailBoxUserView } from 'View/User/MailBox/SystemDropDown';
import { FolderListMailBoxUserView } from 'View/User/MailBox/FolderList';
import { MessageListMailBoxUserView } from 'View/User/MailBox/MessageList';
import { MessageViewMailBoxUserView } from 'View/User/MailBox/MessageView';
import { SystemDropDownUserView } from 'View/User/SystemDropDown';
import { MailFolderList } from 'View/User/MailBox/FolderList';
import { MailMessageList } from 'View/User/MailBox/MessageList';
import { MailMessageView } from 'View/User/MailBox/MessageView';
import { AbstractScreen } from 'Knoin/AbstractScreen';
export class MailBoxUserScreen extends AbstractScreen {
constructor() {
super('mailbox', [
SystemDropDownMailBoxUserView,
FolderListMailBoxUserView,
MessageListMailBoxUserView,
MessageViewMailBoxUserView
SystemDropDownUserView,
MailFolderList,
MailMessageList,
MailMessageView
]);
}
@ -48,7 +51,7 @@ export class MailBoxUserScreen extends AbstractScreen {
onShow() {
this.updateWindowTitle();
AppUserStore.focusedState(Scope.None);
AppUserStore.focusedState('none');
AppUserStore.focusedState(Scope.MessageList);
ThemeStore.isMobile() && leftPanelDisabled(true);
@ -61,17 +64,17 @@ export class MailBoxUserScreen extends AbstractScreen {
* @returns {void}
*/
onRoute(folderHash, page, search) {
const folder = getFolderFromCacheList(getFolderFullNameRaw(folderHash.replace(/~([\d]+)$/, '')));
const folder = getFolderFromCacheList(getFolderFullName(folderHash.replace(/~([\d]+)$/, '')));
if (folder) {
let threadUid = folderHash.replace(/^.+~(\d+)$/, '$1');
FolderUserStore.currentFolder(folder);
MessageUserStore.listPage(1 > page ? 1 : page);
MessageUserStore.listSearch(search);
MessageUserStore.listThreadUid((folderHash === threadUid) ? 0 : pInt(threadUid));
MessagelistUserStore.page(1 > page ? 1 : page);
MessagelistUserStore.listSearch(search);
MessagelistUserStore.threadUid((folderHash === threadUid) ? 0 : pInt(threadUid));
rl.app.reloadMessageList();
MessagelistUserStore.reload();
}
}
@ -79,27 +82,41 @@ export class MailBoxUserScreen extends AbstractScreen {
* @returns {void}
*/
onStart() {
if (!this.__started) {
super.onStart();
super.onStart();
addEventListener('mailbox.inbox-unread-count', e => {
FolderUserStore.foldersInboxUnreadCount(e.detail);
const email = AccountUserStore.email();
AccountUserStore.accounts.forEach(item =>
item && email === item.email && item.count(e.detail)
);
this.updateWindowTitle();
});
}
addEventListener('mailbox.inbox-unread-count', e => {
FolderUserStore.foldersInboxUnreadCount(e.detail);
/* // Disabled in SystemDropDown.html
const email = AccountUserStore.email();
AccountUserStore.accounts.forEach(item =>
item && email === item.email && item.count(e.detail)
);
*/
this.updateWindowTitle();
});
}
/**
* @returns {void}
*/
onBuild() {
setTimeout(() => rl.app.initHorizontalLayoutResizer(), 1);
setTimeout(() => {
// initMailboxLayoutResizer
const top = elementById('V-MailMessageList'),
bottom = elementById('V-MailMessageView'),
fToggle = () => {
let layout = SettingsUserStore.layout();
setLayoutResizer(top, bottom, ClientSideKeyNameMessageListSize,
(ThemeStore.isMobile() || Layout.NoPreview === layout)
? 0
: (Layout.SidePreview === layout ? 'Width' : 'Height')
);
};
if (top && bottom) {
fToggle();
addEventListener('rl-layout', fToggle);
}
}, 1);
doc.addEventListener('click', event =>
event.target.closest('#rl-right') && moveAction(false)
@ -107,19 +124,23 @@ export class MailBoxUserScreen extends AbstractScreen {
}
/**
* Parse link as generated by mailBox()
* @returns {Array}
*/
routes() {
const
folder = (request, vals) => request ? decodeURI(pString(vals[0])) : getFolderInboxName(),
folder = (request, vals) => request ? pString(vals[0]) : getFolderInboxName(),
fNormS = (request, vals) => [folder(request, vals), request ? pInt(vals[1]) : 1, decodeURI(pString(vals[2]))];
return [
// Folder: INBOX | INBOX.sub | Sent | fullNameHash
[/^([^/]*)$/, { normalize_: fNormS }],
[/^([a-zA-Z0-9~]+)\/(.+)\/?$/, { normalize_: (request, vals) =>
// Search: {folder}/{string}
[/^([a-zA-Z0-9.~_-]+)\/(.+)\/?$/, { normalize_: (request, vals) =>
[folder(request, vals), 1, decodeURI(pString(vals[1]))]
}],
[/^([a-zA-Z0-9~]+)\/p([1-9][0-9]*)(?:\/(.+)\/?)?$/, { normalize_: fNormS }]
// Page: {folder}/p{int}(/{search})?
[/^([a-zA-Z0-9.~_-]+)\/p([1-9][0-9]*)(?:\/(.+))?$/, { normalize_: fNormS }]
];
}
}

View file

@ -1,5 +1,5 @@
import { Capa, Scope } from 'Common/Enums';
import { keyScope, leftPanelDisabled, Settings } from 'Common/Globals';
import { Scope } from 'Common/Enums';
import { keyScope, leftPanelDisabled, SettingsCapa } from 'Common/Globals';
import { runSettingsViewModelHooks } from 'Common/Plugins';
import { initOnStartOrLangChange, i18n } from 'Common/Translator';
@ -9,23 +9,59 @@ import { ThemeStore } from 'Stores/Theme';
import { AbstractSettingsScreen, settingsAddViewModel } from 'Screen/AbstractSettings';
import { GeneralUserSettings } from 'Settings/User/General';
import { ContactsUserSettings } from 'Settings/User/Contacts';
import { AccountsUserSettings } from 'Settings/User/Accounts';
import { FiltersUserSettings } from 'Settings/User/Filters';
import { SecurityUserSettings } from 'Settings/User/Security';
import { TemplatesUserSettings } from 'Settings/User/Templates';
import { FoldersUserSettings } from 'Settings/User/Folders';
import { ThemesUserSettings } from 'Settings/User/Themes';
import { OpenPgpUserSettings } from 'Settings/User/OpenPgp';
import { UserSettingsGeneral } from 'Settings/User/General';
import { UserSettingsContacts } from 'Settings/User/Contacts';
import { UserSettingsAccounts } from 'Settings/User/Accounts';
import { UserSettingsFilters } from 'Settings/User/Filters';
import { UserSettingsSecurity } from 'Settings/User/Security';
import { UserSettingsFolders } from 'Settings/User/Folders';
import { UserSettingsThemes } from 'Settings/User/Themes';
import { SystemDropDownSettingsUserView } from 'View/User/Settings/SystemDropDown';
import { MenuSettingsUserView } from 'View/User/Settings/Menu';
import { PaneSettingsUserView } from 'View/User/Settings/Pane';
import { UserSettingsTemplates } from 'Settings/User/Templates';
import { SystemDropDownUserView } from 'View/User/SystemDropDown';
import { SettingsMenuUserView } from 'View/User/Settings/Menu';
import { SettingsPaneUserView } from 'View/User/Settings/Pane';
export class SettingsUserScreen extends AbstractSettingsScreen {
constructor() {
super([SystemDropDownSettingsUserView, MenuSettingsUserView, PaneSettingsUserView]);
super([SystemDropDownUserView, SettingsMenuUserView, SettingsPaneUserView]);
const views = [
UserSettingsGeneral
];
if (AppUserStore.allowContacts()) {
views.push(UserSettingsContacts);
}
if (SettingsCapa('AdditionalAccounts') || SettingsCapa('Identities')) {
views.push(UserSettingsAccounts);
}
if (SettingsCapa('Sieve')) {
views.push(UserSettingsFilters);
}
if (SettingsCapa('AutoLogout') || SettingsCapa('OpenPGP') || SettingsCapa('GnuPG')) {
views.push(UserSettingsSecurity);
}
if (SettingsCapa('Templates')) {
views.push(UserSettingsTemplates);
}
views.push(UserSettingsFolders);
if (SettingsCapa('Themes')) {
views.push(UserSettingsThemes);
}
views.forEach((item, index) =>
settingsAddViewModel(item, item.name.replace('User', ''), 0, 0, 0 === index)
);
runSettingsViewModelHooks(false);
initOnStartOrLangChange(
() => this.sSettingsTitle = i18n('TITLES/SETTINGS'),
@ -33,67 +69,6 @@ export class SettingsUserScreen extends AbstractSettingsScreen {
);
}
/**
* @param {Function=} fCallback
*/
setupSettings(fCallback = null) {
if (!Settings.capa(Capa.Settings)) {
fCallback && fCallback();
return false;
}
settingsAddViewModel(GeneralUserSettings, 'SettingsGeneral', 'SETTINGS_LABELS/LABEL_GENERAL_NAME', 'general', true);
if (AppUserStore.allowContacts()) {
settingsAddViewModel(ContactsUserSettings, 'SettingsContacts', 'SETTINGS_LABELS/LABEL_CONTACTS_NAME', 'contacts');
}
if (Settings.capa(Capa.AdditionalAccounts) || Settings.capa(Capa.Identities)) {
settingsAddViewModel(
AccountsUserSettings,
'SettingsAccounts',
Settings.capa(Capa.AdditionalAccounts)
? 'SETTINGS_LABELS/LABEL_ACCOUNTS_NAME'
: 'SETTINGS_LABELS/LABEL_IDENTITIES_NAME',
'accounts'
);
}
if (Settings.capa(Capa.Sieve)) {
settingsAddViewModel(FiltersUserSettings, 'SettingsFilters', 'SETTINGS_LABELS/LABEL_FILTERS_NAME', 'filters');
}
if (Settings.capa(Capa.AutoLogout)) {
settingsAddViewModel(SecurityUserSettings, 'SettingsSecurity', 'SETTINGS_LABELS/LABEL_SECURITY_NAME', 'security');
}
if (Settings.capa(Capa.Templates)) {
settingsAddViewModel(
TemplatesUserSettings,
'SettingsTemplates',
'SETTINGS_LABELS/LABEL_TEMPLATES_NAME',
'templates'
);
}
settingsAddViewModel(FoldersUserSettings, 'SettingsFolders', 'SETTINGS_LABELS/LABEL_FOLDERS_NAME', 'folders');
if (Settings.capa(Capa.Themes)) {
settingsAddViewModel(ThemesUserSettings, 'SettingsThemes', 'SETTINGS_LABELS/LABEL_THEMES_NAME', 'themes');
}
if (Settings.capa(Capa.OpenPGP)) {
settingsAddViewModel(OpenPgpUserSettings, 'SettingsOpenPGP', 'OpenPGP', 'openpgp');
}
runSettingsViewModelHooks(false);
fCallback && fCallback();
return true;
}
onShow() {
this.setSettingsTitle();
keyScope(Scope.Settings);