diff --git a/dev/App/Abstract.js b/dev/App/Abstract.js index 5e100c251..487ff90c6 100644 --- a/dev/App/Abstract.js +++ b/dev/App/Abstract.js @@ -2,13 +2,11 @@ import ko from 'ko'; import { doc, - $htmlCL, elementById, leftPanelDisabled, Settings } from 'Common/Globals'; -import { KeyState } from 'Common/Enums'; import { logoutLink } from 'Common/Links'; import { i18nToNodes, initOnStartOrLangChange } from 'Common/Translator'; @@ -27,17 +25,6 @@ export class AbstractApp { */ constructor(Remote) { this.Remote = Remote; - - const refresh = (()=>dispatchEvent(new CustomEvent('rl.auto-logout-refresh'))).debounce(5000), - fn = (ev=>{ - $htmlCL.toggle('rl-ctrl-key-pressed', ev.ctrlKey); - refresh(); - }).debounce(500); - -// doc.addEventListener('touchstart', fn, {passive:true}); - ['mousedown','keydown','keyup'/*,'mousemove'*/].forEach(t => doc.addEventListener(t, fn)); - - shortcuts.add('escape,enter', '', KeyState.All, () => rl.Dropdowns.detectVisibility()); } remote() { @@ -110,7 +97,7 @@ export class AbstractApp { register('Select', SelectComponent); register('TextArea', TextAreaComponent); register('CheckboxSimple', CheckboxComponent, 'CheckboxComponent'); - if (/*ThemeStore.isMobile() || */!Settings.app('materialDesign')) { + if (!Settings.app('materialDesign')) { register('Checkbox', CheckboxComponent); } else { register('Checkbox', CheckboxMaterialDesignComponent, 'CheckboxMaterialDesignComponent'); diff --git a/dev/App/Admin.js b/dev/App/Admin.js index 83aa47385..73e34a4ff 100644 --- a/dev/App/Admin.js +++ b/dev/App/Admin.js @@ -3,11 +3,11 @@ import ko from 'ko'; import { StorageResultType } from 'Common/Enums'; -import AppStore from 'Stores/Admin/App'; -import CapaStore from 'Stores/Admin/Capa'; -import DomainStore from 'Stores/Admin/Domain'; -import PluginStore from 'Stores/Admin/Plugin'; -import PackageStore from 'Stores/Admin/Package'; +import { AppAdminStore } from 'Stores/Admin/App'; +import { CapaAdminStore } from 'Stores/Admin/Capa'; +import { DomainAdminStore } from 'Stores/Admin/Domain'; +import { PluginAdminStore } from 'Stores/Admin/Plugin'; +import { PackageAdminStore } from 'Stores/Admin/Package'; import Remote from 'Remote/Admin/Fetch'; import { SettingsAdminScreen } from 'Screen/Admin/Settings'; @@ -26,11 +26,11 @@ class AdminApp extends AbstractApp { } reloadDomainList() { - DomainStore.domains.loading(true); + DomainAdminStore.loading(true); Remote.domainList((result, data) => { - DomainStore.domains.loading(false); + DomainAdminStore.loading(false); if (StorageResultType.Success === result && data && data.Result) { - DomainStore.domains( + DomainAdminStore( Object.entries(data.Result).map(([name, [enabled, alias]]) => ({ name: name, disabled: ko.observable(!enabled), @@ -43,11 +43,11 @@ class AdminApp extends AbstractApp { } reloadPluginList() { - PluginStore.plugins.loading(true); + PluginAdminStore.loading(true); Remote.pluginList((result, data) => { - PluginStore.plugins.loading(false); + PluginAdminStore.loading(false); if (StorageResultType.Success === result && data && data.Result) { - PluginStore.plugins( + PluginAdminStore( data.Result.map(item => ({ name: item.Name, disabled: ko.observable(!item.Enabled), @@ -59,18 +59,16 @@ class AdminApp extends AbstractApp { } reloadPackagesList() { - PackageStore.packages.loading(true); - PackageStore.packagesReal(true); + PackageAdminStore.loading(true); Remote.packagesList((result, data) => { - PackageStore.packages.loading(false); + PackageAdminStore.loading(false); if (StorageResultType.Success === result && data && data.Result) { - PackageStore.packagesReal(!!data.Result.Real); - PackageStore.packagesMainUpdatable(!!data.Result.MainUpdatable); + PackageAdminStore.real(!!data.Result.Real); let list = []; const loading = {}; - PackageStore.packages.forEach(item => { + PackageAdminStore.forEach(item => { if (item && item.loading()) { loading[item.file] = item; } @@ -86,9 +84,9 @@ class AdminApp extends AbstractApp { }).filter(v => v); } - PackageStore.packages(list); + PackageAdminStore(list); } else { - PackageStore.packagesReal(false); + PackageAdminStore.real(false); } }); } @@ -100,8 +98,8 @@ class AdminApp extends AbstractApp { bootstart() { super.bootstart(); - AppStore.populate(); - CapaStore.populate(); + AppAdminStore.populate(); + CapaAdminStore.populate(); this.hideLoading(); diff --git a/dev/App/User.js b/dev/App/User.js index ff81aef90..aa12d6f78 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -6,7 +6,8 @@ import { isPosNumeric, delegateRunOnDestroy, mailToHelper } from 'Common/UtilsUs import { Capa, StorageResultType, - Notification + Notification, + KeyState } from 'Common/Enums'; import { @@ -51,7 +52,7 @@ import SettingsStore from 'Stores/User/Settings'; import NotificationStore from 'Stores/User/Notification'; import AccountStore from 'Stores/User/Account'; import ContactStore from 'Stores/User/Contact'; -import IdentityStore from 'Stores/User/Identity'; +import { IdentityUserStore } from 'Stores/User/Identity'; import TemplateStore from 'Stores/User/Template'; import FolderStore from 'Stores/User/Folder'; import PgpStore from 'Stores/User/Pgp'; @@ -124,6 +125,17 @@ class AppUser extends AbstractApp { } }, 1000); } + + const refresh = (()=>dispatchEvent(new CustomEvent('rl.auto-logout-refresh'))).debounce(5000), + fn = (ev=>{ + $htmlCL.toggle('rl-ctrl-key-pressed', ev.ctrlKey); + refresh(); + }).debounce(500); + +// doc.addEventListener('touchstart', fn, {passive:true}); + ['mousedown','keydown','keyup'/*,'mousemove'*/].forEach(t => doc.addEventListener(t, fn)); + + shortcuts.add('escape,enter', '', KeyState.All, () => rl.Dropdowns.detectVisibility()); } remote() { @@ -480,11 +492,11 @@ class AppUser extends AbstractApp { accountsAndIdentities() { AccountStore.accounts.loading(true); - IdentityStore.identities.loading(true); + IdentityUserStore.loading(true); Remote.accountsAndIdentities((sResult, oData) => { AccountStore.accounts.loading(false); - IdentityStore.identities.loading(false); + IdentityUserStore.loading(false); if (StorageResultType.Success === sResult && oData.Result) { const counts = {}, @@ -506,9 +518,9 @@ class AppUser extends AbstractApp { } if (Array.isArray(oData.Result.Identities)) { - delegateRunOnDestroy(IdentityStore.identities()); + delegateRunOnDestroy(IdentityUserStore()); - IdentityStore.identities( + IdentityUserStore( oData.Result.Identities.map(identityData => { const id = pString(identityData.Id), email = pString(identityData.Email), diff --git a/dev/Common/File.js b/dev/Common/File.js index d2673a0eb..0d8b6c6a7 100644 --- a/dev/Common/File.js +++ b/dev/Common/File.js @@ -6,7 +6,6 @@ const app = 'application/', msOffice = app+'vnd.openxmlformats-officedocument.', openDoc = app+'vnd.oasis.opendocument.', - font = app+'x-font-', sizes = ['B', 'KiB', 'MiB', 'GiB', 'TiB'], exts = { @@ -54,10 +53,6 @@ const 'bz2': app+'x-bzip2', 'deb': app+'x-debian-package', - // fonts - 'psf': font+'linux-psf', - 'ttc': font+'ttf', - // audio 'mp3': 'audio/mpeg', 'wav': 'audio/x-wav', @@ -124,7 +119,7 @@ export const FileType = { Archive: 'archive' }; -export const File = { +export const FileInfo = { /** * @param {string} fileName * @returns {string} @@ -151,8 +146,8 @@ export const File = { return 'audio/'+ext; if (/^(h26[134]|jpgv|mp4|webm)$/.test(ext)) return 'video/'+ext; - if (/^(otf|pcf|snf|ttf)$/.test(ext)) - return font+ext; + if (/^(otf|sfnt|ttf|woff2?)$/.test(ext)) + return 'font/'+ext; if (/^(png|jpeg|gif|tiff|webp)$/.test(ext)) return 'image/'+ext; @@ -282,7 +277,7 @@ export const File = { return result; }, - getIconClass: (ext, mime) => File.getTypeIconClass(File.getType(ext, mime)), + getIconClass: (ext, mime) => FileInfo.getTypeIconClass(FileInfo.getType(ext, mime)), /** * @param {string} sFileType @@ -291,7 +286,7 @@ export const File = { getCombinedIconClass: data => { if (Array.isNotEmpty(data)) { let icons = data - .map(item => item ? File.getIconClass(File.getExtension(item[0]), item[1])[0] : '') + .map(item => item ? FileInfo.getIconClass(FileInfo.getExtension(item[0]), item[1])[0] : '') .validUnique(); return (icons && 1 === icons.length && 'icon-file' !== icons[0]) diff --git a/dev/External/User/ko.js b/dev/External/User/ko.js index 51c775bd3..8f80d431b 100644 --- a/dev/External/User/ko.js +++ b/dev/External/User/ko.js @@ -230,3 +230,29 @@ ko.bindingHandlers.onEsc = { ko.utils.domNodeDisposal.addDisposeCallback(element, () => element.removeEventListener('keyup', fn)); } }; + +ko.bindingHandlers.registerBootstrapDropdown = { + init: element => { + rl.Dropdowns.register(element); + element.ddBtn = new BSN.Dropdown(element.querySelector('[data-toggle="dropdown"]')); + } +}; + +ko.bindingHandlers.openDropdownTrigger = { + update: (element, fValueAccessor) => { + if (ko.unwrap(fValueAccessor())) { + const el = element.ddBtn; + el.open || el.toggle(); +// el.focus(); + + rl.Dropdowns.detectVisibility(); + fValueAccessor()(false); + } + } +}; + +ko.bindingHandlers.dropdownCloser = { + init: element => element.closest('.dropdown').addEventListener('click', event => + event.target.closestWithin('.e-item', element) && element.ddBtn.toggle() + ) +}; diff --git a/dev/External/ko.js b/dev/External/ko.js index 8f3cd269d..a2009786d 100644 --- a/dev/External/ko.js +++ b/dev/External/ko.js @@ -1,5 +1,5 @@ import { i18n, i18nToNodes, trigger } from 'Common/Translator'; -import { doc, createElement, dropdownVisibility } from 'Common/Globals'; +import { doc, createElement } from 'Common/Globals'; import { SaveSettingsStep } from 'Common/Enums'; const @@ -9,27 +9,11 @@ const ko.bindingHandlers.tooltip = { init: (element, fValueAccessor) => { const sValue = koValue(fValueAccessor()); - - if ('off' === element.dataset.tooltipI18n) { - element.title = sValue; - } else { - element.title = i18n(sValue); - trigger.subscribe(() => - element.title = i18n(sValue) - ); - dropdownVisibility.subscribe(() => - element.title = i18n(sValue) - ); - } + element.title = i18n(sValue); + trigger.subscribe(() => element.title = i18n(sValue)); }, - update: (element, fValueAccessor) => { - const sValue = koValue(fValueAccessor()); - if (sValue) { - element.title = 'off' === element.dataset.tooltipI18n ? sValue : i18n(sValue); - } else { - element.title = ''; - } - } + update: (element, fValueAccessor) => + element.title = i18n(koValue(fValueAccessor())) }; ko.bindingHandlers.tooltipErrorTip = { @@ -46,32 +30,6 @@ ko.bindingHandlers.tooltipErrorTip = { } }; -ko.bindingHandlers.registerBootstrapDropdown = { - init: element => { - rl.Dropdowns.register(element); - element.ddBtn = new BSN.Dropdown(element.querySelector('[data-toggle="dropdown"]')); - } -}; - -ko.bindingHandlers.openDropdownTrigger = { - update: (element, fValueAccessor) => { - if (ko.unwrap(fValueAccessor())) { - const el = element.ddBtn; - el.open || el.toggle(); -// el.focus(); - - rl.Dropdowns.detectVisibility(); - fValueAccessor()(false); - } - } -}; - -ko.bindingHandlers.dropdownCloser = { - init: element => element.closest('.dropdown').addEventListener('click', event => - event.target.closestWithin('.e-item', element) && element.ddBtn.toggle() - ) -}; - ko.bindingHandlers.onEnter = { init: (element, fValueAccessor, fAllBindingsAccessor, viewModel) => { let fn = event => { @@ -230,17 +188,6 @@ ko.extenders.limitedList = (target, limitedList) => { return result; }; -ko.extenders.reversible = (target) => { - let value = target(); - - target.commit = () => value = target(); - - target.reverse = () => target(value); - - target.commitedValue = () => value; - return target; -}; - ko.extenders.toggleSubscribeProperty = (target, options) => { const prop = options[1]; if (prop) { diff --git a/dev/Model/Attachment.js b/dev/Model/Attachment.js index e68f467c3..08bbb0591 100644 --- a/dev/Model/Attachment.js +++ b/dev/Model/Attachment.js @@ -1,6 +1,6 @@ import ko from 'ko'; -import { File, FileType } from 'Common/File'; +import { FileInfo, FileType } from 'Common/File'; import { attachmentDownload, serverRequestRaw @@ -44,11 +44,11 @@ export class AttachmentModel extends AbstractModel { static reviveFromJson(json) { const attachment = super.reviveFromJson(json); if (attachment) { - attachment.friendlySize = File.friendlySize(json.EstimatedSize); + attachment.friendlySize = FileInfo.friendlySize(json.EstimatedSize); attachment.cidWithoutTags = attachment.cid.replace(/^<+/, '').replace(/>+$/, ''); - attachment.fileNameExt = File.getExtension(attachment.fileName); - attachment.fileType = File.getType(attachment.fileNameExt, attachment.mimeType); + attachment.fileNameExt = FileInfo.getExtension(attachment.fileName); + attachment.fileType = FileInfo.getType(attachment.fileNameExt, attachment.mimeType); } return attachment; } @@ -211,13 +211,13 @@ export class AttachmentModel extends AbstractModel { * @returns {string} */ iconClass() { - return File.getTypeIconClass(this.fileType)[0]; + return FileInfo.getTypeIconClass(this.fileType)[0]; } /** * @returns {string} */ iconText() { - return File.getTypeIconClass(this.fileType)[1]; + return FileInfo.getTypeIconClass(this.fileType)[1]; } } diff --git a/dev/Model/ComposeAttachment.js b/dev/Model/ComposeAttachment.js index 112cda8cc..6ee36dade 100644 --- a/dev/Model/ComposeAttachment.js +++ b/dev/Model/ComposeAttachment.js @@ -1,5 +1,5 @@ import { pInt } from 'Common/Utils'; -import { File } from 'Common/File'; +import { FileInfo } from 'Common/File'; import { AbstractModel } from 'Knoin/AbstractModel'; @@ -51,11 +51,11 @@ export class ComposeAttachmentModel extends AbstractModel { friendlySize: () => { const localSize = this.size(); - return null === localSize ? '' : File.friendlySize(localSize); + return null === localSize ? '' : FileInfo.friendlySize(localSize); }, - mimeType: () => File.getContentType(this.fileName()), - fileExt: () => File.getExtension(this.fileName()) + mimeType: () => FileInfo.getContentType(this.fileName()), + fileExt: () => FileInfo.getExtension(this.fileName()) }); } @@ -96,13 +96,13 @@ export class ComposeAttachmentModel extends AbstractModel { * @returns {string} */ iconClass() { - return File.getIconClass(this.fileExt(), this.mimeType())[0]; + return FileInfo.getIconClass(this.fileExt(), this.mimeType())[0]; } /** * @returns {string} */ iconText() { - return File.getIconClass(this.fileExt(), this.mimeType())[1]; + return FileInfo.getIconClass(this.fileExt(), this.mimeType())[1]; } } diff --git a/dev/Model/Message.js b/dev/Model/Message.js index d693eae9c..703f97cd1 100644 --- a/dev/Model/Message.js +++ b/dev/Model/Message.js @@ -10,7 +10,7 @@ import { serverRequestRaw } from 'Common/Links'; import FolderStore from 'Stores/User/Folder'; -import { File } from 'Common/File'; +import { FileInfo } from 'Common/File'; import { AttachmentCollectionModel } from 'Model/AttachmentCollection'; import { EmailCollectionModel } from 'Model/EmailCollection'; import { AbstractModel } from 'Knoin/AbstractModel'; @@ -85,7 +85,7 @@ export class MessageModel extends AbstractModel { this.threads = ko.observableArray(); this.addComputables({ - attachmentIconClass: () => File.getCombinedIconClass(this.hasAttachments() ? this.attachmentsSpecData() : []), + attachmentIconClass: () => FileInfo.getCombinedIconClass(this.hasAttachments() ? this.attachmentsSpecData() : []), threadsLen: () => this.threads.length, isImportant: () => MessagePriority.High === this.priority(), }); @@ -169,7 +169,7 @@ export class MessageModel extends AbstractModel { * @returns {string} */ friendlySize() { - return File.friendlySize(this.size()); + return FileInfo.friendlySize(this.size()); } computeSenderEmail() { diff --git a/dev/Screen/AbstractSettings.js b/dev/Screen/AbstractSettings.js index a2464b1bb..04c2bc4da 100644 --- a/dev/Screen/AbstractSettings.js +++ b/dev/Screen/AbstractSettings.js @@ -26,9 +26,7 @@ export class AbstractSettingsScreen extends AbstractScreen { * @param {Function=} fCallback */ setupSettings(fCallback = null) { - if (fCallback) { - fCallback(); - } + fCallback && fCallback(); } onRoute(subName) { diff --git a/dev/Screen/Admin/Settings.js b/dev/Screen/Admin/Settings.js index b606461e7..31a1f64bd 100644 --- a/dev/Screen/Admin/Settings.js +++ b/dev/Screen/Admin/Settings.js @@ -52,9 +52,7 @@ export class SettingsAdminScreen extends AbstractSettingsScreen { runSettingsViewModelHooks(true); - if (fCallback) { - fCallback(); - } + fCallback && fCallback(); } onShow() { diff --git a/dev/Screen/User/Settings.js b/dev/Screen/User/Settings.js index ec4fc097b..75bf6c026 100644 --- a/dev/Screen/User/Settings.js +++ b/dev/Screen/User/Settings.js @@ -38,9 +38,7 @@ export class SettingsUserScreen extends AbstractSettingsScreen { */ setupSettings(fCallback = null) { if (!Settings.capa(Capa.Settings)) { - if (fCallback) { - fCallback(); - } + fCallback && fCallback(); return false; } @@ -93,9 +91,7 @@ export class SettingsUserScreen extends AbstractSettingsScreen { runSettingsViewModelHooks(false); - if (fCallback) { - fCallback(); - } + fCallback && fCallback(); return true; } diff --git a/dev/Settings/Admin/Domains.js b/dev/Settings/Admin/Domains.js index e99c67b11..6afc986ce 100644 --- a/dev/Settings/Admin/Domains.js +++ b/dev/Settings/Admin/Domains.js @@ -3,7 +3,7 @@ import ko from 'ko'; import { StorageResultType } from 'Common/Enums'; import { showScreenPopup } from 'Knoin/Knoin'; -import DomainStore from 'Stores/Admin/Domain'; +import { DomainAdminStore } from 'Stores/Admin/Domain'; import Remote from 'Remote/Admin/Fetch'; import { DomainPopupView } from 'View/Popup/Domain'; @@ -11,9 +11,9 @@ import { DomainAliasPopupView } from 'View/Popup/DomainAlias'; export class DomainsAdminSettings { constructor() { - this.domains = DomainStore.domains; + this.domains = DomainAdminStore; - this.visibility = ko.computed(() => (this.domains.loading() ? 'visible' : 'hidden')); + this.visibility = ko.computed(() => (DomainAdminStore.loading() ? 'visible' : 'hidden')); this.domainForDeletion = ko.observable(null).deleteAccessHelper(); @@ -30,7 +30,7 @@ export class DomainsAdminSettings { } deleteDomain(domain) { - this.domains.remove(domain); + DomainAdminStore.remove(domain); Remote.domainDelete(this.onDomainListChangeRequest, domain.name); } diff --git a/dev/Settings/Admin/General.js b/dev/Settings/Admin/General.js index 60814e76d..6bcf71a83 100644 --- a/dev/Settings/Admin/General.js +++ b/dev/Settings/Admin/General.js @@ -16,8 +16,8 @@ import Remote from 'Remote/Admin/Fetch'; import { ThemeStore } from 'Stores/Theme'; import { LanguageStore } from 'Stores/Language'; -import AppAdminStore from 'Stores/Admin/App'; -import CapaAdminStore from 'Stores/Admin/Capa'; +import { AppAdminStore } from 'Stores/Admin/App'; +import { CapaAdminStore } from 'Stores/Admin/Capa'; import LanguagesPopupView from 'View/Popup/Languages'; const settingsGet = rl.settings.get; @@ -31,7 +31,7 @@ export class GeneralAdminSettings { this.languagesAdmin = ko.observableArray(Array.isArray(aLanguagesAdmin) ? aLanguagesAdmin : []); this.languageAdmin = ko .observable(settingsGet('LanguageAdmin')) - .extend({ limitedList: this.languagesAdmin, reversible: true }); + .extend({ limitedList: this.languagesAdmin }); this.theme = ThemeStore.theme; this.themes = ThemeStore.themes; diff --git a/dev/Settings/Admin/Packages.js b/dev/Settings/Admin/Packages.js index 31a264069..e7fa4fab1 100644 --- a/dev/Settings/Admin/Packages.js +++ b/dev/Settings/Admin/Packages.js @@ -3,28 +3,26 @@ import ko from 'ko'; import { StorageResultType, Notification } from 'Common/Enums'; import { getNotification } from 'Common/Translator'; -import PackageStore from 'Stores/Admin/Package'; +import { PackageAdminStore } from 'Stores/Admin/Package'; import Remote from 'Remote/Admin/Fetch'; export class PackagesAdminSettings { constructor() { this.packagesError = ko.observable(''); - this.packages = PackageStore.packages; - this.packagesReal = PackageStore.packagesReal; - this.packagesMainUpdatable = PackageStore.packagesMainUpdatable; + this.packages = PackageAdminStore; this.packagesCurrent = ko.computed(() => - this.packages.filter(item => item && item.installed && !item.compare) + PackageAdminStore.filter(item => item && item.installed && !item.compare) ); this.packagesAvailableForUpdate = ko.computed(() => - this.packages.filter(item => item && item.installed && !!item.compare) + PackageAdminStore.filter(item => item && item.installed && !!item.compare) ); this.packagesAvailableForInstallation = ko.computed(() => - this.packages.filter(item => item && !item.installed) + PackageAdminStore.filter(item => item && !item.installed) ); - this.visibility = ko.computed(() => (PackageStore.packages.loading() ? 'visible' : 'hidden')); + this.visibility = ko.computed(() => (PackageAdminStore.loading() ? 'visible' : 'hidden')); } onShow() { @@ -47,7 +45,7 @@ export class PackagesAdminSettings { } } - this.packages.forEach(item => { + PackageAdminStore.forEach(item => { if (item && packageToRequest && item.loading && item.loading() && packageToRequest.file === item.file) { packageToRequest.loading(false); item.loading(false); diff --git a/dev/Settings/Admin/Plugins.js b/dev/Settings/Admin/Plugins.js index deb382290..2630b95e7 100644 --- a/dev/Settings/Admin/Plugins.js +++ b/dev/Settings/Admin/Plugins.js @@ -5,7 +5,7 @@ import { getNotification } from 'Common/Translator'; import { showScreenPopup } from 'Knoin/Knoin'; -import PluginStore from 'Stores/Admin/Plugin'; +import { PluginAdminStore } from 'Stores/Admin/Plugin'; import Remote from 'Remote/Admin/Fetch'; @@ -15,10 +15,10 @@ export class PluginsAdminSettings { constructor() { this.enabledPlugins = ko.observable(!!rl.settings.get('EnabledPlugins')); - this.plugins = PluginStore.plugins; - this.pluginsError = PluginStore.plugins.error; + this.plugins = PluginAdminStore; + this.pluginsError = PluginAdminStore.error; - this.visibility = ko.computed(() => (PluginStore.plugins.loading() ? 'visible' : 'hidden')); + this.visibility = ko.computed(() => (PluginAdminStore.loading() ? 'visible' : 'hidden')); this.onPluginLoadRequest = this.onPluginLoadRequest.bind(this); this.onPluginDisableRequest = this.onPluginDisableRequest.bind(this); @@ -50,7 +50,7 @@ export class PluginsAdminSettings { } onShow() { - PluginStore.plugins.error(''); + PluginAdminStore.error(''); rl.app.reloadPluginList(); } @@ -64,9 +64,9 @@ export class PluginsAdminSettings { if (StorageResultType.Success === result && data) { if (!data.Result && data.ErrorCode) { if (Notification.UnsupportedPluginPackage === data.ErrorCode && data.ErrorMessage && data.ErrorMessage) { - PluginStore.plugins.error(data.ErrorMessage); + PluginAdminStore.error(data.ErrorMessage); } else { - PluginStore.plugins.error(getNotification(data.ErrorCode)); + PluginAdminStore.error(getNotification(data.ErrorCode)); } } } diff --git a/dev/Settings/Admin/Security.js b/dev/Settings/Admin/Security.js index 8abcb48c7..76e5454ff 100644 --- a/dev/Settings/Admin/Security.js +++ b/dev/Settings/Admin/Security.js @@ -2,8 +2,8 @@ import ko from 'ko'; import { StorageResultType } from 'Common/Enums'; -import AppAdminStore from 'Stores/Admin/App'; -import CapaAdminStore from 'Stores/Admin/Capa'; +import { AppAdminStore } from 'Stores/Admin/App'; +import { CapaAdminStore } from 'Stores/Admin/Capa'; import Remote from 'Remote/Admin/Fetch'; diff --git a/dev/Settings/User/Accounts.js b/dev/Settings/User/Accounts.js index f8b12975e..81417a8a8 100644 --- a/dev/Settings/User/Accounts.js +++ b/dev/Settings/User/Accounts.js @@ -3,7 +3,7 @@ import ko from 'ko'; import { Capa, StorageResultType } from 'Common/Enums'; import AccountStore from 'Stores/User/Account'; -import IdentityStore from 'Stores/User/Identity'; +import { IdentityUserStore } from 'Stores/User/Identity'; import Remote from 'Remote/User/Fetch'; import { showScreenPopup } from 'Knoin/Knoin'; @@ -17,7 +17,7 @@ export class AccountsUserSettings { this.allowIdentities = rl.settings.capa(Capa.Identities); this.accounts = AccountStore.accounts; - this.identities = IdentityStore.identities; + this.identities = IdentityUserStore; this.accountForDeletion = ko.observable(null).deleteAccessHelper(); this.identityForDeletion = ko.observable(null).deleteAccessHelper(); @@ -72,14 +72,14 @@ export class AccountsUserSettings { this.identityForDeletion(null); if (identityToRemove) { - IdentityStore.identities.remove(oIdentity => identityToRemove === oIdentity); + IdentityUserStore.remove(oIdentity => identityToRemove === oIdentity); Remote.identityDelete(() => rl.app.accountsAndIdentities(), identityToRemove.id()); } } } accountsAndIdentitiesAfterMove() { - Remote.accountsAndIdentitiesSortOrder(null, AccountStore.getEmailAddresses(), IdentityStore.getIDS()); + Remote.accountsAndIdentitiesSortOrder(null, AccountStore.getEmailAddresses(), IdentityUserStore.getIDS()); } onBuild(oDom) { diff --git a/dev/Settings/User/Filters.js b/dev/Settings/User/Filters.js index 73cc2171a..75aadac10 100644 --- a/dev/Settings/User/Filters.js +++ b/dev/Settings/User/Filters.js @@ -4,7 +4,7 @@ import { delegateRunOnDestroy } from 'Common/UtilsUser'; import { StorageResultType, Notification } from 'Common/Enums'; import { getNotification } from 'Common/Translator'; -import SieveStore from 'Stores/User/Sieve'; +import { SieveUserStore } from 'Stores/User/Sieve'; import Remote from 'Remote/User/Fetch'; import { SieveScriptModel } from 'Model/SieveScript'; @@ -15,7 +15,7 @@ import { SieveScriptPopupView } from 'View/Popup/SieveScript'; export class FiltersUserSettings { constructor() { - this.scripts = SieveStore.scripts; + this.scripts = SieveUserStore.scripts; this.loading = ko.observable(false).extend({ debounce: 200 }); ko.addObservablesTo(this, { @@ -41,7 +41,7 @@ export class FiltersUserSettings { this.scripts([]); if (StorageResultType.Success === result && data && data.Result) { - SieveStore.capa(data.Result.Capa); + SieveUserStore.capa(data.Result.Capa); /* this.scripts( data.Result.Scripts.map(aItem => SieveScriptModel.reviveFromJson(aItem)).filter(v => v) @@ -52,7 +52,7 @@ export class FiltersUserSettings { value && this.scripts.push(value) }); } else { - SieveStore.capa([]); + SieveUserStore.capa([]); this.setError( data && data.ErrorCode ? getNotification(data.ErrorCode) : getNotification(Notification.CantGetFilters) ); diff --git a/dev/Settings/User/General.js b/dev/Settings/User/General.js index ee72b4389..48e8806dc 100644 --- a/dev/Settings/User/General.js +++ b/dev/Settings/User/General.js @@ -14,7 +14,7 @@ import { showScreenPopup } from 'Knoin/Knoin'; import AppStore from 'Stores/User/App'; import { LanguageStore } from 'Stores/Language'; import SettingsStore from 'Stores/User/Settings'; -import IdentityStore from 'Stores/User/Identity'; +import { IdentityUserStore } from 'Stores/User/Identity'; import NotificationStore from 'Stores/User/Notification'; import MessageStore from 'Stores/User/Message'; @@ -54,7 +54,7 @@ export class GeneralUserSettings { layoutTrigger: SaveSettingsStep.Idle }); - this.identities = IdentityStore.identities; + this.identities = IdentityUserStore; this.identityMain = ko.computed(() => { const list = this.identities(); diff --git a/dev/Settings/User/Themes.js b/dev/Settings/User/Themes.js index de64da382..23495f88c 100644 --- a/dev/Settings/User/Themes.js +++ b/dev/Settings/User/Themes.js @@ -17,8 +17,8 @@ export class ThemesUserSettings { this.themesObjects = ko.observableArray(); this.background = {}; - this.background.name = ThemeStore.themeBackgroundName; - this.background.hash = ThemeStore.themeBackgroundHash; + this.background.name = ThemeStore.userBackgroundName; + this.background.hash = ThemeStore.userBackgroundHash; this.background.uploaderButton = ko.observable(null); this.background.loading = ko.observable(false); this.background.error = ko.observable(''); diff --git a/dev/Stores/Admin/App.js b/dev/Stores/Admin/App.js index 918b70a88..6f89a18bf 100644 --- a/dev/Stores/Admin/App.js +++ b/dev/Stores/Admin/App.js @@ -1,20 +1,14 @@ import ko from 'ko'; -class AppAdminStore { - constructor() { - this.weakPassword = ko.observable(false); +export const AppAdminStore = { + weakPassword: ko.observable(false), + dataFolderAccess: ko.observable(false), - this.dataFolderAccess = ko.observable(false); - } - - populate() { + populate: function() { this.weakPassword(!!rl.settings.get('WeakPassword')); /* - if (settingsGet('Auth')) { - fetch('./data/VERSION?' + Math.random()).then(() => this.dataFolderAccess(true)); - } + rl.settings.get('WeakPassword') + && fetch('./data/VERSION?' + Math.random()).then(() => this.dataFolderAccess(true)); */ } -} - -export default new AppAdminStore(); +}; diff --git a/dev/Stores/Admin/Capa.js b/dev/Stores/Admin/Capa.js index ce4f4edfb..ea1abbd41 100644 --- a/dev/Stores/Admin/Capa.js +++ b/dev/Stores/Admin/Capa.js @@ -1,24 +1,8 @@ import ko from 'ko'; import { Capa } from 'Common/Enums'; -class CapaAdminStore { - constructor() { - ko.addObservablesTo(this, { - additionalAccounts: false, - identities: false, - attachmentThumbnails: false, - sieve: false, - filters: false, - themes: true, - userBackground: false, - openPGP: false, - twoFactorAuth: false, - twoFactorAuthForce: false, - templates: false - }); - } - - populate() { +export const CapaAdminStore = { + populate: function() { let capa = rl.settings.capa; this.additionalAccounts(capa(Capa.AdditionalAccounts)); this.identities(capa(Capa.Identities)); @@ -32,6 +16,18 @@ class CapaAdminStore { this.twoFactorAuthForce(capa(Capa.TwoFactorForce)); this.templates(capa(Capa.Templates)); } -} +}; -export default new CapaAdminStore(); +ko.addObservablesTo(CapaAdminStore, { + additionalAccounts: false, + identities: false, + attachmentThumbnails: false, + sieve: false, + filters: false, + themes: true, + userBackground: false, + openPGP: false, + twoFactorAuth: false, + twoFactorAuthForce: false, + templates: false +}); diff --git a/dev/Stores/Admin/Domain.js b/dev/Stores/Admin/Domain.js index ffdbe767c..8e44f53d8 100644 --- a/dev/Stores/Admin/Domain.js +++ b/dev/Stores/Admin/Domain.js @@ -1,11 +1,5 @@ import ko from 'ko'; -class DomainAdminStore { - constructor() { - this.domains = ko.observableArray(); - this.domains.loading = ko.observable(false).extend({ 'throttle': 100 }); - this.domainsWithoutAliases = ko.computed(() => this.domains.filter(item => item && !item.alias)); - } -} +export const DomainAdminStore = ko.observableArray(); -export default new DomainAdminStore(); +DomainAdminStore.loading = ko.observable(false).extend({ 'throttle': 100 }); diff --git a/dev/Stores/Admin/Package.js b/dev/Stores/Admin/Package.js index c4618df7e..74fefdb22 100644 --- a/dev/Stores/Admin/Package.js +++ b/dev/Stores/Admin/Package.js @@ -1,15 +1,7 @@ import ko from 'ko'; -class PackageAdminStore { - constructor() { - this.packages = ko.observableArray(); - this.packages.loading = ko.observable(false).extend({ debounce: 100 }); +export const PackageAdminStore = ko.observableArray(); - ko.addObservablesTo(this, { - packagesReal: true, - packagesMainUpdatable: true - }); - } -} +PackageAdminStore.real = ko.observable(true); -export default new PackageAdminStore(); +PackageAdminStore.loading = ko.observable(false).extend({ debounce: 100 }); diff --git a/dev/Stores/Admin/Plugin.js b/dev/Stores/Admin/Plugin.js index 09dc17055..d72bf12bc 100644 --- a/dev/Stores/Admin/Plugin.js +++ b/dev/Stores/Admin/Plugin.js @@ -1,11 +1,5 @@ import ko from 'ko'; -class PluginAdminStore { - constructor() { - this.plugins = ko.observableArray(); - this.plugins.loading = ko.observable(false).extend({ debounce: 100 }); - this.plugins.error = ko.observable(''); - } -} - -export default new PluginAdminStore(); +export const PluginAdminStore = ko.observableArray(); +PluginAdminStore.loading = ko.observable(false).extend({ debounce: 100 }); +PluginAdminStore.error = ko.observable(''); diff --git a/dev/Stores/Language.js b/dev/Stores/Language.js index ed7e96421..4e1c6e90b 100644 --- a/dev/Stores/Language.js +++ b/dev/Stores/Language.js @@ -14,4 +14,4 @@ export const LanguageStore = { } LanguageStore.language = ko.observable('') - .extend({ limitedList: LanguageStore.languages, reversible: true }); + .extend({ limitedList: LanguageStore.languages }); diff --git a/dev/Stores/Theme.js b/dev/Stores/Theme.js index 2eb2dddfd..ee3a0fee7 100644 --- a/dev/Stores/Theme.js +++ b/dev/Stores/Theme.js @@ -3,8 +3,9 @@ import { $htmlCL } from 'Common/Globals'; export const ThemeStore = { themes: ko.observableArray(), - themeBackgroundName: ko.observable(''), - themeBackgroundHash: ko.observable(''), + userBackgroundName: ko.observable(''), + userBackgroundHash: ko.observable(''), + isMobile: ko.observable($htmlCL.contains('rl-mobile')), populate: function(){ const Settings = rl.settings, @@ -13,13 +14,12 @@ export const ThemeStore = { this.themes(Array.isArray(themes) ? themes : []); this.theme(Settings.get('Theme')); if (!this.isMobile()) { - this.themeBackgroundName(Settings.get('UserBackgroundName')); - this.themeBackgroundHash(Settings.get('UserBackgroundHash')); + this.userBackgroundName(Settings.get('UserBackgroundName')); + this.userBackgroundHash(Settings.get('UserBackgroundHash')); } } }; ThemeStore.theme = ko.observable('').extend({ limitedList: ThemeStore.themes }); -ThemeStore.isMobile = ko.observable($htmlCL.contains('rl-mobile')); ThemeStore.isMobile.subscribe(value => $htmlCL.toggle('rl-mobile', value)); diff --git a/dev/Stores/User/Account.js b/dev/Stores/User/Account.js index 54d3ec057..d3cb97e29 100644 --- a/dev/Stores/User/Account.js +++ b/dev/Stores/User/Account.js @@ -30,13 +30,6 @@ class AccountUserStore { this.email(rl.settings.get('Email')); this.parentEmail(rl.settings.get('ParentEmail')); } - - /** - * @returns {boolean} - */ - isRootAccount() { - return !this.parentEmail(); - } } export default new AccountUserStore(); diff --git a/dev/Stores/User/Identity.js b/dev/Stores/User/Identity.js index 657e41c69..4e0f3cbf1 100644 --- a/dev/Stores/User/Identity.js +++ b/dev/Stores/User/Identity.js @@ -1,12 +1,7 @@ import ko from 'ko'; -class IdentityUserStore { - constructor() { - this.identities = ko.observableArray(); - this.identities.loading = ko.observable(false).extend({ debounce: 100 }); +export const IdentityUserStore = ko.observableArray(); - this.getIDS = () => this.identities.map(item => (item ? item.id() : null)).filter(value => null !== value); - } -} - -export default new IdentityUserStore(); +IdentityUserStore.getIDS = () => IdentityUserStore.map(item => (item ? item.id() : null)) + .filter(value => null !== value); +IdentityUserStore.loading = ko.observable(false).extend({ debounce: 100 }); diff --git a/dev/Stores/User/Sieve.js b/dev/Stores/User/Sieve.js index 5e72c0da2..0c9d50d71 100644 --- a/dev/Stores/User/Sieve.js +++ b/dev/Stores/User/Sieve.js @@ -1,12 +1,8 @@ import ko from 'ko'; -class SieveUserStore { - constructor() { - // capabilities - this.capa = ko.observableArray(); - // Sieve scripts SieveScriptModel - this.scripts = ko.observableArray(); - } +export const SieveUserStore = { + // capabilities + capa: ko.observableArray(), + // Sieve scripts SieveScriptModel + scripts: ko.observableArray() } - -export default new SieveUserStore(); diff --git a/dev/View/Admin/Settings/Pane.js b/dev/View/Admin/Settings/Pane.js index 361eb97a8..36c398818 100644 --- a/dev/View/Admin/Settings/Pane.js +++ b/dev/View/Admin/Settings/Pane.js @@ -2,9 +2,9 @@ import ko from 'ko'; import Remote from 'Remote/Admin/Fetch'; -import DomainStore from 'Stores/Admin/Domain'; -import PluginStore from 'Stores/Admin/Plugin'; -import PackageStore from 'Stores/Admin/Package'; +import { DomainAdminStore } from 'Stores/Admin/Domain'; +import { PluginAdminStore } from 'Stores/Admin/Plugin'; +import { PackageAdminStore } from 'Stores/Admin/Package'; import { AbstractViewRight } from 'Knoin/AbstractViews'; @@ -19,9 +19,9 @@ class PaneSettingsAdminView extends AbstractViewRight { this.leftPanelDisabled = leftPanelDisabled; this.adminManLoadingVisibility = ko - .computed(() => (DomainStore.domains.loading() - || PluginStore.plugins.loading() - || PackageStore.packages.loading()) ? 'visible' : 'hidden') + .computed(() => (DomainAdminStore.loading() + || PluginAdminStore.loading() + || PackageAdminStore.loading()) ? 'visible' : 'hidden') .extend({ rateLimit: 300 }); } diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index bd38d079a..d185e0e05 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -27,7 +27,7 @@ import { Settings } from 'Common/Globals'; import AppStore from 'Stores/User/App'; import SettingsStore from 'Stores/User/Settings'; -import IdentityStore from 'Stores/User/Identity'; +import { IdentityUserStore } from 'Stores/User/Identity'; import AccountStore from 'Stores/User/Account'; import FolderStore from 'Stores/User/Folder'; import PgpStore from 'Stores/User/Pgp'; @@ -136,7 +136,7 @@ class ComposePopupView extends AbstractViewPopup { this.capaOpenPGP = PgpStore.capaOpenPGP; - this.identities = IdentityStore.identities; + this.identities = IdentityUserStore; this.addObservables({ identitiesDropdownTrigger: false, @@ -258,7 +258,7 @@ class ComposePopupView extends AbstractViewPopup { isDraftFolderMessage: () => this.draftFolder() && this.draftUid(), identitiesOptions: () => - IdentityStore.identities.map(item => ({ + IdentityUserStore.map(item => ({ 'item': item, 'optValue': item.id(), 'optText': item.formattedName() @@ -553,7 +553,7 @@ class ComposePopupView extends AbstractViewPopup { findIdentityByMessage(composeType, message) { let resultIndex = 1000, resultIdentity = null; - const identities = IdentityStore.identities(), + const identities = IdentityUserStore(), identitiesCache = {}, fEachHelper = (item) => { if (item && item.email && identitiesCache[item.email]) { diff --git a/dev/View/Popup/Domain.js b/dev/View/Popup/Domain.js index 9b2e4c830..3161f021c 100644 --- a/dev/View/Popup/Domain.js +++ b/dev/View/Popup/Domain.js @@ -2,7 +2,7 @@ import { StorageResultType, Notification } from 'Common/Enums'; import { pInt, pString } from 'Common/Utils'; import { i18n } from 'Common/Translator'; -import CapaAdminStore from 'Stores/Admin/Capa'; +import { CapaAdminStore } from 'Stores/Admin/Capa'; import Remote from 'Remote/Admin/Fetch'; diff --git a/dev/View/Popup/DomainAlias.js b/dev/View/Popup/DomainAlias.js index bf4cbbd91..557669e61 100644 --- a/dev/View/Popup/DomainAlias.js +++ b/dev/View/Popup/DomainAlias.js @@ -3,7 +3,7 @@ import ko from 'ko'; import { StorageResultType, Notification } from 'Common/Enums'; import { i18n } from 'Common/Translator'; -import DomainStore from 'Stores/Admin/Domain'; +import { DomainAdminStore } from 'Stores/Admin/Domain'; import Remote from 'Remote/Admin/Fetch'; @@ -23,7 +23,7 @@ class DomainAliasPopupView extends AbstractViewPopup { alias: '' }); - this.domains = DomainStore.domainsWithoutAliases; + this.domains = ko.computed(() => DomainAdminStore.filter(item => item && !item.alias)); this.domainsOptions = ko.computed(() => this.domains().map(item => ({ optValue: item.name, optText: item.name }))); diff --git a/dev/View/Popup/Filter.js b/dev/View/Popup/Filter.js index 4ef7c69e4..81c3a3651 100644 --- a/dev/View/Popup/Filter.js +++ b/dev/View/Popup/Filter.js @@ -6,7 +6,7 @@ import { defaultOptionsAfterRender } from 'Common/Utils'; import { i18n, initOnStartOrLangChange } from 'Common/Translator'; import FolderStore from 'Stores/User/Folder'; -import SieveStore from 'Stores/User/Sieve'; +import { SieveUserStore } from 'Stores/User/Sieve'; import { command } from 'Knoin/Knoin'; import { AbstractViewPopup } from 'Knoin/AbstractViews'; @@ -35,7 +35,7 @@ class FilterPopupView extends AbstractViewPopup { initOnStartOrLangChange(this.populateOptions.bind(this)); - SieveStore.capa.subscribe(this.populateOptions, this); + SieveUserStore.capa.subscribe(this.populateOptions, this); } @command() @@ -79,7 +79,7 @@ class FilterPopupView extends AbstractViewPopup { // this.actionTypeOptions.push({'id': FilterAction.None, // 'name': i18n('GLOBAL/NONE')}); - const modules = SieveStore.capa; + const modules = SieveUserStore.capa; if (modules) { if (modules.includes('imap4flags')) { this.allowMarkAsRead(true); diff --git a/dev/View/Popup/SieveScript.js b/dev/View/Popup/SieveScript.js index 7bbf91a7d..412b90709 100644 --- a/dev/View/Popup/SieveScript.js +++ b/dev/View/Popup/SieveScript.js @@ -7,7 +7,7 @@ import { i18nToNodes } from 'Common/Translator'; import Remote from 'Remote/User/Fetch'; import { FilterModel } from 'Model/Filter'; -import SieveStore from 'Stores/User/Sieve'; +import { SieveUserStore } from 'Stores/User/Sieve'; import { showScreenPopup/*, command*/ } from 'Knoin/Knoin'; import { AbstractViewPopup } from 'Knoin/AbstractViews'; @@ -26,7 +26,7 @@ class SieveScriptPopupView extends AbstractViewPopup { script: null }); - this.sieveCapabilities = SieveStore.capa.join(' '); + this.sieveCapabilities = SieveUserStore.capa.join(' '); this.saving = false; this.filterForDeletion = ko.observable(null).deleteAccessHelper(); @@ -41,7 +41,7 @@ class SieveScriptPopupView extends AbstractViewPopup { return false; } - if (!script.exists() && SieveStore.scripts.find(item => item.name() === script.name())) { + if (!script.exists() && SieveUserStore.scripts.find(item => item.name() === script.name())) { script.nameError(true); return false; } @@ -58,7 +58,7 @@ class SieveScriptPopupView extends AbstractViewPopup { self.saving = false; if (StorageResultType.Success === result && data && data.Result) { - script.exists() || SieveStore.scripts.push(script); + script.exists() || SieveUserStore.scripts.push(script); script.exists(true); script.hasChanges(false); } else { diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index 2fe058024..4747748af 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -18,7 +18,7 @@ import { UNUSED_OPTION_VALUE } from 'Common/Consts'; import { doc, leftPanelDisabled, moveAction, Settings } from 'Common/Globals'; import { computedPaginatorHelper } from 'Common/UtilsUser'; -import { File } from 'Common/File'; +import { FileInfo } from 'Common/File'; import { mailBox, serverRequest } from 'Common/Links'; import { Selector } from 'Common/Selector'; @@ -882,9 +882,9 @@ export class MessageListMailBoxUserView extends AbstractViewRight { quotaTooltip() { return i18n('MESSAGE_LIST/QUOTA_SIZE', { - 'SIZE': File.friendlySize(this.userUsageSize()), + 'SIZE': FileInfo.friendlySize(this.userUsageSize()), 'PROC': this.userUsageProc(), - 'LIMIT': File.friendlySize(this.userQuota()) + 'LIMIT': FileInfo.friendlySize(this.userQuota()) }); } diff --git a/snappymail/v/0.0.0/app/localization/admin/nl_NL.yml b/snappymail/v/0.0.0/app/localization/admin/nl_NL.yml index cc727f7c8..1be52136c 100644 --- a/snappymail/v/0.0.0/app/localization/admin/nl_NL.yml +++ b/snappymail/v/0.0.0/app/localization/admin/nl_NL.yml @@ -13,7 +13,7 @@ nl_NL: LABEL_DOMAINS_NAME: "Domeinen" LABEL_SECURITY_NAME: "Beveiliging" LABEL_PLUGINS_NAME: "Invoegtoepassingen" - LABEL_PACKAGES_NAME: "Paketten" + LABEL_PACKAGES_NAME: "Pakketten" LABEL_ABOUT_NAME: "Over" TAB_GENERAL: LEGEND_INTERFACE: "Interface" diff --git a/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsPackages.html b/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsPackages.html index 1ff50b2ac..9e69d13a2 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsPackages.html +++ b/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsPackages.html @@ -1,6 +1,6 @@