From c46a28a8cf300b7d998d7307c965ad67df52e74c Mon Sep 17 00:00:00 2001 From: djmaze Date: Wed, 14 Jul 2021 12:56:37 +0200 Subject: [PATCH 01/26] Bugfix: Invalid command 'RewriteEngine' https://github.com/Mailu/Mailu/pull/1796#issuecomment-854958765 --- .htaccess | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.htaccess b/.htaccess index 0c2db6f7e..49e532bc1 100644 --- a/.htaccess +++ b/.htaccess @@ -1,6 +1,8 @@ -RewriteEngine On -# Redirect cPanel -RewriteRule cpsess.* https://%{HTTP_HOST}/ [L,R=301] + + RewriteEngine On + # Redirect cPanel + RewriteRule cpsess.* https://%{HTTP_HOST}/ [L,R=301] + ExpiresActive On From 6e41f19b0264af8bf1211daef46ba6a5395d2201 Mon Sep 17 00:00:00 2001 From: djmaze Date: Thu, 15 Jul 2021 07:52:34 +0200 Subject: [PATCH 02/26] Forgot to commit regarding APC => APCU Mentioned at https://github.com/the-djmaze/snappymail/issues/99#issuecomment-879835900 --- .../v/0.0.0/app/libraries/RainLoop/Config/AbstractConfig.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/AbstractConfig.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/AbstractConfig.php index 8333befa7..aeae28f1c 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/AbstractConfig.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/AbstractConfig.php @@ -41,7 +41,7 @@ abstract class AbstractConfig $this->sFileHeader = $sFileHeader; $this->aData = $this->defaultValues(); - $this->bUseApcCache = APP_USE_APC_CACHE && + $this->bUseApcCache = APP_USE_APCU_CACHE && \MailSo\Base\Utils::FunctionExistsAndEnabled(array('apcu_fetch', 'apcu_store')); } From bf9b47ce851f99b7913b3ab7e2d9260878a6bff1 Mon Sep 17 00:00:00 2001 From: djmaze Date: Thu, 15 Jul 2021 21:01:41 +0200 Subject: [PATCH 03/26] Added: compose window on mobile use Fullscreen API --- dev/Styles/Compose.less | 7 +++++++ dev/Styles/_BootstrapFix.less | 2 +- dev/View/Popup/Compose.js | 36 +++++++++++++++++++++++++++++------ 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/dev/Styles/Compose.less b/dev/Styles/Compose.less index 9caa115e8..c07114a90 100644 --- a/dev/Styles/Compose.less +++ b/dev/Styles/Compose.less @@ -144,6 +144,13 @@ } } +@media screen and (max-width: 999px) { + .b-compose.modal { + border: 0; + width: 100%; + } +} + .minimize-custom { border: 0 solid #333; border-bottom-width: 3px; diff --git a/dev/Styles/_BootstrapFix.less b/dev/Styles/_BootstrapFix.less index 460157a94..dad250ce5 100644 --- a/dev/Styles/_BootstrapFix.less +++ b/dev/Styles/_BootstrapFix.less @@ -61,7 +61,7 @@ label.inline, span.inline { @media screen and (max-width: 999px) { .modal { - width: calc(~'100% - 20px') !important; + width: calc(100% - 20px); } .form-horizontal { diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index 62736cf04..6fbbca82b 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -21,7 +21,7 @@ import { serverRequest } from 'Common/Links'; import { i18n, getNotification, getUploadErrorDescByCode } from 'Common/Translator'; import { timestampToString } from 'Common/Momentor'; import { MessageFlagsCache, setFolderHash } from 'Common/Cache'; -import { Settings, SettingsGet } from 'Common/Globals'; +import { doc, Settings, SettingsGet } from 'Common/Globals'; import { AppUserStore } from 'Stores/User/App'; import { SettingsUserStore } from 'Stores/User/Settings'; @@ -43,6 +43,8 @@ import { AskPopupView } from 'View/Popup/Ask'; import { ContactsPopupView } from 'View/Popup/Contacts'; import { ComposeOpenPgpPopupView } from 'View/Popup/ComposeOpenPgp'; +import { ThemeStore } from 'Stores/Theme'; + const /** * @param {string} prefix @@ -432,7 +434,7 @@ class ComposePopupView extends AbstractViewPopup { || getNotification(Notification.CantSendMessage)); } } else { - this.closeCommand && this.closeCommand(); + this.closeCommand(); } } this.reloadDraftFolder(); @@ -643,6 +645,8 @@ class ComposePopupView extends AbstractViewPopup { rl.route.on(); this.resizeObserver.disconnect(); + + (doc.fullscreenElement || doc.webkitFullscreenElement) === this.oContent && doc.exitFullscreen(); } editor(fOnInit) { @@ -739,6 +743,8 @@ class ComposePopupView extends AbstractViewPopup { } else { this.initOnShow(type, oMessageOrArray, aToEmails, aCcEmails, aBccEmails, sCustomSubject, sCustomPlainText); } + + ThemeStore.isMobile() && this.oContent.requestFullscreen && this.oContent.requestFullscreen(); } /** @@ -1062,14 +1068,12 @@ class ComposePopupView extends AbstractViewPopup { tryToClosePopup() { if (!isPopupVisible(AskPopupView) && this.modalVisibility()) { if (this.bSkipNextHide || (this.isEmptyForm() && !this.draftUid())) { - this.closeCommand && this.closeCommand(); + this.closeCommand(); } else { showScreenPopup(AskPopupView, [ i18n('POPUPS_ASK/DESC_WANT_CLOSE_THIS_WINDOW'), () => { - if (this.modalVisibility()) { - this.closeCommand && this.closeCommand(); - } + this.modalVisibility() && this.closeCommand(); } ]); } @@ -1135,6 +1139,26 @@ class ComposePopupView extends AbstractViewPopup { ro.els = [dom.querySelector('.textAreaParent'), dom.querySelector('.attachmentAreaParent')]; this.editor(editor => editor.modeWysiwyg()); + + // Fullscreen must be on app, else other popups fail + const el = doc.getElementById('rl-app'); + let event = 'fullscreenchange'; + if (!el.requestFullscreen && el.webkitRequestFullscreen) { + el.requestFullscreen = el.webkitRequestFullscreen; + event = 'webkit' + event; + } + if (el.requestFullscreen) { + if (!doc.exitFullscreen && doc.webkitExitFullscreen) { + doc.exitFullscreen = doc.webkitExitFullscreen; + } + this.oContent = el; + el.addEventListener(event, () => + ThemeStore.isMobile() + && this.modalVisibility() + && (doc.fullscreenElement || doc.webkitFullscreenElement) !== el + && this.skipCommand() + ); + } } /** From c46e190a3aab9b38f6b2d474693e0b4861e09a57 Mon Sep 17 00:00:00 2001 From: djmaze Date: Thu, 15 Jul 2021 21:02:00 +0200 Subject: [PATCH 04/26] Fix calc() in less files --- dev/Styles/FolderList.less | 2 +- dev/Styles/Languages.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/Styles/FolderList.less b/dev/Styles/FolderList.less index 921853ddd..42985de66 100644 --- a/dev/Styles/FolderList.less +++ b/dev/Styles/FolderList.less @@ -240,7 +240,7 @@ .btn { &.buttonCompose { - width: calc(~'100% - 85px'); + width: calc(100% - 85px); max-width: -moz-fit-content; max-width: -webkit-fit-content; max-width: fit-content; diff --git a/dev/Styles/Languages.less b/dev/Styles/Languages.less index ca88ccadf..e84676baf 100644 --- a/dev/Styles/Languages.less +++ b/dev/Styles/Languages.less @@ -40,6 +40,6 @@ @media screen and (max-width: 999px) { .b-languages-content .lang-item { - width: calc(~'100% - 40px'); + width: calc(100% - 40px); } } From d69743cee5570aa8d6d783c08e9ec201626d9096 Mon Sep 17 00:00:00 2001 From: djmaze Date: Fri, 16 Jul 2021 09:11:10 +0200 Subject: [PATCH 05/26] Cleanup closeCommand & cancelCommand because both are defined for popups --- dev/Knoin/AbstractViews.js | 10 +++++----- dev/View/Popup/AddOpenPgpKey.js | 2 +- dev/View/Popup/Filter.js | 2 +- dev/View/Popup/NewOpenPgpKey.js | 2 +- dev/View/Popup/Plugin.js | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dev/Knoin/AbstractViews.js b/dev/Knoin/AbstractViews.js index 42df779c0..4affd299e 100644 --- a/dev/Knoin/AbstractViews.js +++ b/dev/Knoin/AbstractViews.js @@ -38,9 +38,6 @@ class AbstractView { keyScope(this.sCurrentScope); } - cancelCommand() {} - closeCommand() {} - querySelector(selectors) { return this.viewModelDom.querySelector(selectors); } @@ -68,7 +65,10 @@ export class AbstractViewPopup extends AbstractView this.sDefaultScope = Scope[name]; } } - +/* + cancelCommand() {} + closeCommand() {} +*/ /** * @returns {void} */ @@ -76,7 +76,7 @@ export class AbstractViewPopup extends AbstractView addEventListener('keydown', event => { if (event && this.modalVisibility && this.modalVisibility()) { if (!this.bDisabeCloseOnEsc && 'Escape' == event.key) { - this.cancelCommand && this.cancelCommand(); + this.cancelCommand(); return false; } else if ('Backspace' == event.key && !inFocus()) { return false; diff --git a/dev/View/Popup/AddOpenPgpKey.js b/dev/View/Popup/AddOpenPgpKey.js index 1ed1b1238..b4bf0e733 100644 --- a/dev/View/Popup/AddOpenPgpKey.js +++ b/dev/View/Popup/AddOpenPgpKey.js @@ -78,7 +78,7 @@ class AddOpenPgpKeyPopupView extends AbstractViewPopup { return false; } - this.cancelCommand && this.cancelCommand(); + this.cancelCommand(); return true; } diff --git a/dev/View/Popup/Filter.js b/dev/View/Popup/Filter.js index 88da060d0..64b387f93 100644 --- a/dev/View/Popup/Filter.js +++ b/dev/View/Popup/Filter.js @@ -54,7 +54,7 @@ class FilterPopupView extends AbstractViewPopup { this.fTrueCallback && this.fTrueCallback(this.filter()); - this.modalVisibility() && this.closeCommand && this.closeCommand(); + this.modalVisibility() && this.closeCommand(); } return true; diff --git a/dev/View/Popup/NewOpenPgpKey.js b/dev/View/Popup/NewOpenPgpKey.js index efdabbb2d..98f57ac34 100644 --- a/dev/View/Popup/NewOpenPgpKey.js +++ b/dev/View/Popup/NewOpenPgpKey.js @@ -63,7 +63,7 @@ class NewOpenPgpKeyPopupView extends AbstractViewPopup { openpgpKeyring.store(); rl.app.reloadOpenPgpKeys(); - this.cancelCommand && this.cancelCommand(); + this.cancelCommand(); } }) .catch((e) => { diff --git a/dev/View/Popup/Plugin.js b/dev/View/Popup/Plugin.js index f1cb19fd0..12ce2e6f8 100644 --- a/dev/View/Popup/Plugin.js +++ b/dev/View/Popup/Plugin.js @@ -85,7 +85,7 @@ class PluginPopupView extends AbstractViewPopup { if (!isPopupVisible(AskPopupView)) { showScreenPopup(AskPopupView, [ i18n('POPUPS_ASK/DESC_WANT_CLOSE_THIS_WINDOW'), - () => this.modalVisibility() && this.cancelCommand && this.cancelCommand() + () => this.modalVisibility() && this.cancelCommand() ]); } } From 79945d45b46f57c530d123d887c7c2a0e100198d Mon Sep 17 00:00:00 2001 From: djmaze Date: Fri, 16 Jul 2021 13:57:04 +0200 Subject: [PATCH 06/26] Merge Admin Plugins and Packages into single management page --- dev/Remote/Admin/Fetch.js | 7 - dev/Screen/Admin/Settings.js | 2 - dev/Settings/Admin/Packages.js | 44 ++++- dev/Settings/Admin/Plugins.js | 63 ------- dev/Stores/Admin/Package.js | 23 ++- dev/Stores/Admin/Plugin.js | 24 --- dev/Styles/@Admin.less | 1 - dev/Styles/AdminPackages.less | 10 ++ .../app/libraries/RainLoop/Actions/Admin.php | 161 ++++++------------ .../v/0.0.0/app/localization/de-DE/admin.json | 8 +- .../v/0.0.0/app/localization/en/admin.json | 8 +- .../v/0.0.0/app/localization/es-ES/admin.json | 8 +- .../v/0.0.0/app/localization/fr-FR/admin.json | 8 +- .../v/0.0.0/app/localization/hu-HU/admin.json | 8 +- .../v/0.0.0/app/localization/nl-NL/admin.json | 8 +- .../v/0.0.0/app/localization/sv-SE/admin.json | 8 +- .../v/0.0.0/app/localization/zh-CN/admin.json | 8 +- .../Views/Admin/AdminSettingsPackages.html | 14 +- .../Admin/AdminSettingsPackagesTable.html | 8 +- .../Views/Admin/AdminSettingsPlugins.html | 43 ----- 20 files changed, 142 insertions(+), 322 deletions(-) delete mode 100644 dev/Settings/Admin/Plugins.js delete mode 100644 dev/Stores/Admin/Plugin.js delete mode 100644 snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsPlugins.html diff --git a/dev/Remote/Admin/Fetch.js b/dev/Remote/Admin/Fetch.js index 5d0922ac6..dc0ded270 100644 --- a/dev/Remote/Admin/Fetch.js +++ b/dev/Remote/Admin/Fetch.js @@ -38,13 +38,6 @@ class RemoteAdminFetch extends AbstractFetchRemote { }); } - /** - * @param {?Function} fCallback - */ - pluginList(fCallback) { - this.defaultRequest(fCallback, 'AdminPluginList'); - } - /** * @param {?Function} fCallback */ diff --git a/dev/Screen/Admin/Settings.js b/dev/Screen/Admin/Settings.js index 31a1f64bd..4b47b4db6 100644 --- a/dev/Screen/Admin/Settings.js +++ b/dev/Screen/Admin/Settings.js @@ -7,7 +7,6 @@ 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 { PluginsAdminSettings } from 'Settings/Admin/Plugins'; import { PackagesAdminSettings } from 'Settings/Admin/Packages'; import { AboutAdminSettings } from 'Settings/Admin/About'; import { BrandingAdminSettings } from 'Settings/Admin/Branding'; @@ -38,7 +37,6 @@ export class SettingsAdminScreen extends AbstractSettingsScreen { [BrandingAdminSettings, 'Branding'], [ContactsAdminSettings, 'Contacts'], [SecurityAdminSettings, 'Security'], - [PluginsAdminSettings, 'Plugins'], [PackagesAdminSettings, 'Packages'], [AboutAdminSettings, 'About'], ].forEach(item => diff --git a/dev/Settings/Admin/Packages.js b/dev/Settings/Admin/Packages.js index f2d04f6c1..6c1e9ab19 100644 --- a/dev/Settings/Admin/Packages.js +++ b/dev/Settings/Admin/Packages.js @@ -6,6 +6,10 @@ import { getNotification } from 'Common/Translator'; import { PackageAdminStore } from 'Stores/Admin/Package'; import Remote from 'Remote/Admin/Fetch'; +import { showScreenPopup } from 'Knoin/Knoin'; +import { PluginPopupView } from 'View/Popup/Plugin'; +import { SettingsGet } from 'Common/Globals'; + export class PackagesAdminSettings { constructor() { this.packagesError = ko.observable(''); @@ -13,24 +17,42 @@ export class PackagesAdminSettings { this.packages = PackageAdminStore; this.packagesCurrent = ko.computed(() => - PackageAdminStore.filter(item => item && item.installed && !item.compare) + PackageAdminStore.filter(item => item && item.installed && !item.canBeUpdated) ); this.packagesAvailableForUpdate = ko.computed(() => - PackageAdminStore.filter(item => item && item.installed && !!item.compare) + PackageAdminStore.filter(item => item && item.installed && !!item.canBeUpdated) ); this.packagesAvailableForInstallation = ko.computed(() => PackageAdminStore.filter(item => item && !item.installed) ); this.visibility = ko.computed(() => (PackageAdminStore.loading() ? 'visible' : 'hidden')); + + this.enabledPlugins = ko.observable(!!SettingsGet('EnabledPlugins')); + this.enabledPlugins.subscribe(value => + Remote.saveAdminConfig(null, { + EnabledPlugins: value ? 1 : 0 + }) + ); } onShow() { this.packagesError(''); } - onBuild() { + onBuild(oDom) { PackageAdminStore.fetch(); + + oDom.addEventListener('click', event => { + // configurePlugin + let el = event.target.closestWithin('.package-configure', oDom), + data = el ? ko.dataFor(el) : 0; + data && Remote.plugin((iError, data) => iError || showScreenPopup(PluginPopupView, [data.Result]), data.name) + // disablePlugin + el = event.target.closestWithin('.package-active', oDom); + data = el ? ko.dataFor(el) : 0; + data && this.disablePlugin(data); + }); } requestHelper(packageToRequest, install) { @@ -70,4 +92,20 @@ export class PackagesAdminSettings { Remote.packageInstall(this.requestHelper(packageToInstall, true), packageToInstall); } } + + disablePlugin(plugin) { + let b = !plugin.enabled(); + plugin.enabled(b); + Remote.pluginDisable((iError, data) => { + if (iError) { + this.packagesError( + (Notification.UnsupportedPluginPackage === iError && data && data.ErrorMessage) + ? data.ErrorMessage + : getNotification(iError) + ); + } +// PackageAdminStore.fetch(); + }, plugin.name, !b); + } + } diff --git a/dev/Settings/Admin/Plugins.js b/dev/Settings/Admin/Plugins.js deleted file mode 100644 index 6ea4f4e05..000000000 --- a/dev/Settings/Admin/Plugins.js +++ /dev/null @@ -1,63 +0,0 @@ -import ko from 'ko'; - -import { Notification } from 'Common/Enums'; -import { SettingsGet } from 'Common/Globals'; -import { getNotification } from 'Common/Translator'; - -import { showScreenPopup } from 'Knoin/Knoin'; - -import { PluginAdminStore } from 'Stores/Admin/Plugin'; - -import Remote from 'Remote/Admin/Fetch'; - -import { PluginPopupView } from 'View/Popup/Plugin'; - -export class PluginsAdminSettings -{ - constructor() { - this.enabledPlugins = ko.observable(!!SettingsGet('EnabledPlugins')); - - this.plugins = PluginAdminStore; - this.pluginsError = PluginAdminStore.error; - - this.enabledPlugins.subscribe(value => - Remote.saveAdminConfig(null, { - EnabledPlugins: value ? 1 : 0 - }) - ); - } - - disablePlugin(plugin) { - let b = !plugin.disabled(); - plugin.disabled(b); - Remote.pluginDisable((iError, data) => { - if (iError) { - PluginAdminStore.error( - (Notification.UnsupportedPluginPackage === iError && data && data.ErrorMessage) - ? data.ErrorMessage - : getNotification(iError) - ); - } - PluginAdminStore.fetch(); - }, plugin.name, b); - } - - configurePlugin(plugin) { - Remote.plugin((iError, data) => iError || showScreenPopup(PluginPopupView, [data.Result]), plugin.name); - } - - onBuild(oDom) { - oDom.addEventListener('click', event => { - let el = event.target.closestWithin('.configure-plugin-action', oDom); - el && ko.dataFor(el) && this.configurePlugin(ko.dataFor(el)); - - el = event.target.closestWithin('.disabled-plugin', oDom); - el && ko.dataFor(el) && this.disablePlugin(ko.dataFor(el)); - }); - } - - onShow() { - PluginAdminStore.error(''); - PluginAdminStore.fetch(); - } -} diff --git a/dev/Stores/Admin/Package.js b/dev/Stores/Admin/Package.js index 2753dea47..bc824527f 100644 --- a/dev/Stores/Admin/Package.js +++ b/dev/Stores/Admin/Package.js @@ -8,35 +8,34 @@ PackageAdminStore.real = ko.observable(true); PackageAdminStore.loading = ko.observable(false); +//PackageAdminStore.error = ko.observable(''); + PackageAdminStore.fetch = () => { PackageAdminStore.loading(true); Remote.packagesList((iError, data) => { PackageAdminStore.loading(false); - if (!iError) { + if (iError) { + PackageAdminStore.real(false); + } else { PackageAdminStore.real(!!data.Result.Real); - let list = []; const loading = {}; - PackageAdminStore.forEach(item => { if (item && item.loading()) { loading[item.file] = item; } }); + let list = []; if (isArray(data.Result.List)) { - list = data.Result.List.map(item => { - if (item) { - item.loading = ko.observable(loading[item.file] !== undefined); - return 'core' === item.type && !item.canBeInstalled ? null : item; - } - return null; - }).filter(v => v); + list = data.Result.List.filter(v => v).map(item => { + item.loading = ko.observable(loading[item.file] !== undefined); + item.enabled = ko.observable(item.enabled); + return item; + }); } PackageAdminStore(list); - } else { - PackageAdminStore.real(false); } }); }; diff --git a/dev/Stores/Admin/Plugin.js b/dev/Stores/Admin/Plugin.js deleted file mode 100644 index 6b4bc50e2..000000000 --- a/dev/Stores/Admin/Plugin.js +++ /dev/null @@ -1,24 +0,0 @@ -import ko from 'ko'; -import Remote from 'Remote/Admin/Fetch'; - -export const PluginAdminStore = ko.observableArray(); - -PluginAdminStore.loading = ko.observable(false); - -PluginAdminStore.error = ko.observable(''); - -PluginAdminStore.fetch = () => { - PluginAdminStore.loading(true); - Remote.pluginList((iError, data) => { - PluginAdminStore.loading(false); - if (!iError) { - PluginAdminStore( - data.Result.map(item => ({ - name: item.Name, - disabled: ko.observable(!item.Enabled), - configured: ko.observable(!!item.Configured) - })) - ); - } - }); -}; diff --git a/dev/Styles/@Admin.less b/dev/Styles/@Admin.less index 65c42afd6..c8eb37181 100644 --- a/dev/Styles/@Admin.less +++ b/dev/Styles/@Admin.less @@ -35,7 +35,6 @@ @import "AdminDomains.less"; @import "AdminDomain.less"; @import "AdminPackages.less"; -@import "AdminPlugins.less"; @import "AdminPlugin.less"; @import "AdminAbout.less"; diff --git a/dev/Styles/AdminPackages.less b/dev/Styles/AdminPackages.less index f56f29f7b..9793a6bbc 100644 --- a/dev/Styles/AdminPackages.less +++ b/dev/Styles/AdminPackages.less @@ -5,6 +5,11 @@ width: 650px; } + .disabled table, + table .disabled { + opacity: 0.5; + } + } .b-admin-packages-list-table { @@ -30,4 +35,9 @@ .package-actions { vertical-align: middle; } + + .package-configure, .package-active { + cursor: pointer; + margin-right: 1em; + } } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Admin.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Admin.php index 3ef545f6d..61dcf16aa 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Admin.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Admin.php @@ -540,103 +540,75 @@ trait Admin $bReal = \is_array($aRep) && 0 < \count($aRep); } - $aResult = array(); - if (\is_array($aRep)) - { - foreach ($aRep as $oItem) - { - if ($oItem && isset($oItem->type, $oItem->id, $oItem->name, - $oItem->version, $oItem->release, $oItem->file, $oItem->description)) - { - if (!empty($oItem->required) && APP_DEV_VERSION !== APP_VERSION && version_compare(APP_VERSION, $oItem->required, '<')) - { - continue; - } - - if (!empty($oItem->depricated) && APP_DEV_VERSION !== APP_VERSION && version_compare(APP_VERSION, $oItem->depricated, '>=')) - { - continue; - } - - if ('plugin' === $oItem->type) - { - $aResult[] = array( - 'type' => $oItem->type, - 'id' => $oItem->id, - 'name' => $oItem->name, - 'installed' => '', - 'version' => $oItem->version, - 'file' => $oItem->file, - 'release' => $oItem->release, - 'desc' => $oItem->description - ); - } - } - } - } - - return $aResult; + return \is_array($aRep) ? $aRep : []; } private function getRepositoryData(bool &$bReal, bool &$bRainLoopUpdatable) : array { $bRainLoopUpdatable = $this->rainLoopUpdatable(); - $aResult = $this->getRepositoryDataByUrl($this->snappyMailRepo(), $bReal); - - $aSub = array(); - foreach ($aResult as $aItem) - { - if ('plugin' === $aItem['type']) + $aResult = array(); + foreach ($this->getRepositoryDataByUrl($this->snappyMailRepo(), $bReal) as $oItem) { + if ($oItem && isset($oItem->type, $oItem->id, $oItem->name, + $oItem->version, $oItem->release, $oItem->file, $oItem->description)) { - $aSub[] = $aItem; - } - } - $aResult = $aSub; - unset($aSub); + if (!empty($oItem->required) && APP_DEV_VERSION !== APP_VERSION && version_compare(APP_VERSION, $oItem->required, '<')) { + continue; + } - $aInstalled = $this->Plugins()->InstalledPlugins(); - foreach ($aResult as &$aItem) - { - if ('plugin' === $aItem['type']) - { - foreach ($aInstalled as &$aSubItem) - { - if (\is_array($aSubItem) && isset($aSubItem[0]) && $aSubItem[0] === $aItem['id']) - { - $aSubItem[2] = true; - $aItem['installed'] = $aSubItem[1]; - } + if (!empty($oItem->depricated) && APP_DEV_VERSION !== APP_VERSION && version_compare(APP_VERSION, $oItem->depricated, '>=')) { + continue; + } + + if ('plugin' === $oItem->type) { + $aResult[$oItem->id] = array( + 'type' => $oItem->type, + 'id' => $oItem->id, + 'name' => $oItem->name, + 'installed' => '', + 'enabled' => true, + 'version' => $oItem->version, + 'file' => $oItem->file, + 'release' => $oItem->release, + 'desc' => $oItem->description, + 'canBeDeleted' => false, + 'canBeUpdated' => true + ); } } } - foreach ($aInstalled as $aSubItemSec) - { - if ($aSubItemSec && !isset($aSubItemSec[2])) - { - \array_push($aResult, array( - 'type' => 'plugin', - 'id' => $aSubItemSec[0], - 'name' => $aSubItemSec[0], - 'installed' => $aSubItemSec[1], - 'version' => '', - 'file' => '', - 'release' => '', - 'desc' => '' - )); + $aEnabledPlugins = \array_map('trim', + \explode(',', \strtolower($this->Config()->Get('plugins', 'enabled_list', ''))) + ); + + $aInstalled = $this->Plugins()->InstalledPlugins(); + foreach ($aInstalled as $aItem) { + if ($aItem) { + if (isset($aResult[$aItem[0]])) { + $aResult[$aItem[0]]['installed'] = $aItem[1]; + $aResult[$aItem[0]]['enabled'] = \in_array(\strtolower($aItem[0]), $aEnabledPlugins); + $aResult[$aItem[0]]['canBeDeleted'] = true; + $aResult[$aItem[0]]['canBeUpdated'] = \version_compare($aItem[1], $aResult[$aItem[0]]['version'], '<'); + } else { + \array_push($aResult, array( + 'type' => 'plugin', + 'id' => $aItem[0], + 'name' => $aItem[0], + 'installed' => $aItem[1], + 'enabled' => \in_array(\strtolower($aItem[0]), $aEnabledPlugins), + 'version' => '', + 'file' => '', + 'release' => '', + 'desc' => '', + 'canBeDeleted' => true, + 'canBeUpdated' => false + )); + } } } - foreach ($aResult as &$aItem) - { - $aItem['compare'] = \version_compare($aItem['installed'], $aItem['version'], '<'); - $aItem['canBeDeleted'] = '' !== $aItem['installed'] && 'plugin' === $aItem['type']; - $aItem['canBeUpdated'] = $aItem['compare']; - $aItem['canBeInstalled'] = true; - } - - return $aResult; + return \array_values($aResult); } public function DoAdminPackagesList() : array @@ -647,6 +619,8 @@ trait Admin $bRainLoopUpdatable = false; $aList = $this->getRepositoryData($bReal, $bRainLoopUpdatable); +// \uksort($aList, function($a, $b){return \strcasecmp($a['name'], $b['name']);}); + return $this->DefaultResponse(__FUNCTION__, array( 'Real' => $bReal, 'MainUpdatable' => $bRainLoopUpdatable, @@ -773,29 +747,6 @@ trait Admin ('plugin' !== $sType ? array('Reload' => true) : true) : false); } - public function DoAdminPluginList() : array - { - $this->IsAdminLoggined(); - - $aResult = array(); - - $sEnabledPlugins = $this->Config()->Get('plugins', 'enabled_list', ''); - $aEnabledPlugins = \explode(',', \strtolower($sEnabledPlugins)); - $aEnabledPlugins = \array_map('trim', $aEnabledPlugins); - - $aList = $this->Plugins()->InstalledPlugins(); - foreach ($aList as $aItem) - { - $aResult[] = array( - 'Name' => $aItem[0], - 'Enabled' => \in_array(\strtolower($aItem[0]), $aEnabledPlugins), - 'Configured' => false - ); - } - - return $this->DefaultResponse(__FUNCTION__, $aResult); - } - private function pluginEnable(string $sName, bool $bEnable = true) : bool { if (0 === \strlen($sName)) diff --git a/snappymail/v/0.0.0/app/localization/de-DE/admin.json b/snappymail/v/0.0.0/app/localization/de-DE/admin.json index 43a40c3ce..b972fe5d8 100644 --- a/snappymail/v/0.0.0/app/localization/de-DE/admin.json +++ b/snappymail/v/0.0.0/app/localization/de-DE/admin.json @@ -87,14 +87,8 @@ "LABEL_REQUIRE_VERIFICATION": "Verlangen, dass das verwendete SSL-Zertifikat beglaubigt ist (IMAP\/SMTP)", "LABEL_ALLOW_SELF_SIGNED": "Selbst-signierte Zertifikate erlauben" }, - "TAB_PLUGINS": { - "LEGEND_PLUGINS": "Plugins", - "LABEL_ENABLE_PLUGINS": "Plugins aktivieren", - "ALERT_NO_PLUGINS": "Es wurden noch keine Plugins installiert.", - "LINK_INSTALL_NEW": "Hier klicken, um Plugins zu installieren!", - "HINT_CLICK_NAME": "Klicken Sie auf den Namen, um das Plugin zu konfigurieren." - }, "TAB_PACKAGES": { + "LABEL_ENABLE_PLUGINS": "Plugins aktivieren", "LEGEND_AVAILABLE_FOR_UPDATE": "Zur Aktualisierung verfügbar", "LEGEND_AVAILABLE_FOR_INSTALLATION": "Zur Installation verfügbar", "LEGEND_INSTALLED_PACKAGES": "Installierte Pakete", diff --git a/snappymail/v/0.0.0/app/localization/en/admin.json b/snappymail/v/0.0.0/app/localization/en/admin.json index 78c38b368..2cfb1efe9 100644 --- a/snappymail/v/0.0.0/app/localization/en/admin.json +++ b/snappymail/v/0.0.0/app/localization/en/admin.json @@ -87,14 +87,8 @@ "LABEL_REQUIRE_VERIFICATION": "Require verification of SSL certificate used (IMAP\/SMTP)", "LABEL_ALLOW_SELF_SIGNED": "Allow self signed certificates" }, - "TAB_PLUGINS": { - "LEGEND_PLUGINS": "Plugins", - "LABEL_ENABLE_PLUGINS": "Enable plugins", - "ALERT_NO_PLUGINS": "No plugins have yet been installed.", - "LINK_INSTALL_NEW": "Click here to install new!", - "HINT_CLICK_NAME": "Click on the name to configure the plugin." - }, "TAB_PACKAGES": { + "LABEL_ENABLE_PLUGINS": "Enable plugins", "LEGEND_AVAILABLE_FOR_UPDATE": "Available for Update", "LEGEND_AVAILABLE_FOR_INSTALLATION": "Available for Installation", "LEGEND_INSTALLED_PACKAGES": "Installed Packages", diff --git a/snappymail/v/0.0.0/app/localization/es-ES/admin.json b/snappymail/v/0.0.0/app/localization/es-ES/admin.json index 0e0a96d39..bc6ddabab 100644 --- a/snappymail/v/0.0.0/app/localization/es-ES/admin.json +++ b/snappymail/v/0.0.0/app/localization/es-ES/admin.json @@ -87,14 +87,8 @@ "LABEL_REQUIRE_VERIFICATION": "Requerir verificación del certificado SSL (IMAP \/ SMTP)", "LABEL_ALLOW_SELF_SIGNED": "Permitir certificados auto-firmados" }, - "TAB_PLUGINS": { - "LEGEND_PLUGINS": "Complementos", - "LABEL_ENABLE_PLUGINS": "Activar Plugins", - "ALERT_NO_PLUGINS": "No hay plugins instalados.", - "LINK_INSTALL_NEW": "¡Haz click aquí para instalar uno nuevo!", - "HINT_CLICK_NAME": "Haz click en el nombre para configurar el plugin." - }, "TAB_PACKAGES": { + "LABEL_ENABLE_PLUGINS": "Activar Plugins", "LEGEND_AVAILABLE_FOR_UPDATE": "Actualización disponible", "LEGEND_AVAILABLE_FOR_INSTALLATION": "Instalación disponible", "LEGEND_INSTALLED_PACKAGES": "Paquetes instalados", diff --git a/snappymail/v/0.0.0/app/localization/fr-FR/admin.json b/snappymail/v/0.0.0/app/localization/fr-FR/admin.json index 1a5d60e24..30562df0d 100644 --- a/snappymail/v/0.0.0/app/localization/fr-FR/admin.json +++ b/snappymail/v/0.0.0/app/localization/fr-FR/admin.json @@ -87,14 +87,8 @@ "LABEL_REQUIRE_VERIFICATION": "S'assurer qu'un certificat SSL (IMAP\/SMTP) est utilisé", "LABEL_ALLOW_SELF_SIGNED": "Autoriser les certificats auto-signés" }, - "TAB_PLUGINS": { - "LEGEND_PLUGINS": "Plugins", - "LABEL_ENABLE_PLUGINS": "Activer les plugins", - "ALERT_NO_PLUGINS": "Aucun plugin n'a été installé.", - "LINK_INSTALL_NEW": "Cliquez ici pour installer un nouveau plugin !", - "HINT_CLICK_NAME": "Cliquez sur le nom pour configurer le plugin." - }, "TAB_PACKAGES": { + "LABEL_ENABLE_PLUGINS": "Activer les plugins", "LEGEND_AVAILABLE_FOR_UPDATE": "Disponible pour la mise à jour", "LEGEND_AVAILABLE_FOR_INSTALLATION": "Disponible pour l'installation", "LEGEND_INSTALLED_PACKAGES": "Paquets installés", diff --git a/snappymail/v/0.0.0/app/localization/hu-HU/admin.json b/snappymail/v/0.0.0/app/localization/hu-HU/admin.json index eb27e37c2..9580417d8 100644 --- a/snappymail/v/0.0.0/app/localization/hu-HU/admin.json +++ b/snappymail/v/0.0.0/app/localization/hu-HU/admin.json @@ -87,14 +87,8 @@ "LABEL_REQUIRE_VERIFICATION": "A használt (IMAP\/SMTP) tanúsítvány ellenőrzés megkövetelése", "LABEL_ALLOW_SELF_SIGNED": "Saját aláírt tanúsítványok engedélyezése" }, - "TAB_PLUGINS": { - "LEGEND_PLUGINS": "Beépülők", - "LABEL_ENABLE_PLUGINS": "Beépülők engedélyezése", - "ALERT_NO_PLUGINS": "Még nincsenek telepítve beépülők.", - "LINK_INSTALL_NEW": "Új telepítéséhez kattints ide!", - "HINT_CLICK_NAME": "A beépülő beállításához kattints a nevére." - }, "TAB_PACKAGES": { + "LABEL_ENABLE_PLUGINS": "Beépülők engedélyezése", "LEGEND_AVAILABLE_FOR_UPDATE": "Frissíthető", "LEGEND_AVAILABLE_FOR_INSTALLATION": "Telepíthető", "LEGEND_INSTALLED_PACKAGES": "Telepített csomagok", diff --git a/snappymail/v/0.0.0/app/localization/nl-NL/admin.json b/snappymail/v/0.0.0/app/localization/nl-NL/admin.json index 44009401b..39e987c33 100644 --- a/snappymail/v/0.0.0/app/localization/nl-NL/admin.json +++ b/snappymail/v/0.0.0/app/localization/nl-NL/admin.json @@ -87,14 +87,8 @@ "LABEL_REQUIRE_VERIFICATION": "Verificatie van SSL certificaten (IMAP\/SMTP)", "LABEL_ALLOW_SELF_SIGNED": "Sta zelf ondertekende certificaten toe" }, - "TAB_PLUGINS": { - "LEGEND_PLUGINS": "Invoegtoepassingen", - "LABEL_ENABLE_PLUGINS": "Invoegtoepassingen inschakelen", - "ALERT_NO_PLUGINS": "Er zijn nog geen invoegtoepassingen geïnstalleerd", - "LINK_INSTALL_NEW": "Klik hier om een invoegtoepassing te installeren!", - "HINT_CLICK_NAME": "Klik op de naam om de invoegtoepassing te configuren." - }, "TAB_PACKAGES": { + "LABEL_ENABLE_PLUGINS": "Invoegtoepassingen inschakelen", "LEGEND_AVAILABLE_FOR_UPDATE": "Update beschikbaar", "LEGEND_AVAILABLE_FOR_INSTALLATION": "Gereed voor installatie", "LEGEND_INSTALLED_PACKAGES": "Geïnstalleerde pakketten", diff --git a/snappymail/v/0.0.0/app/localization/sv-SE/admin.json b/snappymail/v/0.0.0/app/localization/sv-SE/admin.json index fccff1674..f7e2a7c1a 100644 --- a/snappymail/v/0.0.0/app/localization/sv-SE/admin.json +++ b/snappymail/v/0.0.0/app/localization/sv-SE/admin.json @@ -87,14 +87,8 @@ "LABEL_REQUIRE_VERIFICATION": "Kräver verifiering av SSL-certifikat som används (IMAP\/SMTP)", "LABEL_ALLOW_SELF_SIGNED": "Låt självsignerade certifikat" }, - "TAB_PLUGINS": { - "LEGEND_PLUGINS": "Plugin", - "LABEL_ENABLE_PLUGINS": "Aktivera Plugin", - "ALERT_NO_PLUGINS": "Inga plugin har ännu blivit installerade", - "LINK_INSTALL_NEW": "Klicka här för att installera nya", - "HINT_CLICK_NAME": "Klicka på namnet för att konfigurera pluginet" - }, "TAB_PACKAGES": { + "LABEL_ENABLE_PLUGINS": "Aktivera Plugin", "LEGEND_AVAILABLE_FOR_UPDATE": "Tillåt uppdateringar", "LEGEND_AVAILABLE_FOR_INSTALLATION": "Tillåt installation", "LEGEND_INSTALLED_PACKAGES": "installerade paket", diff --git a/snappymail/v/0.0.0/app/localization/zh-CN/admin.json b/snappymail/v/0.0.0/app/localization/zh-CN/admin.json index 7351a9e5a..a13f46193 100644 --- a/snappymail/v/0.0.0/app/localization/zh-CN/admin.json +++ b/snappymail/v/0.0.0/app/localization/zh-CN/admin.json @@ -87,14 +87,8 @@ "LABEL_REQUIRE_VERIFICATION": "要求验证 SSL 证书(IMAP\/SMTP)", "LABEL_ALLOW_SELF_SIGNED": "允许自签名证书" }, - "TAB_PLUGINS": { - "LEGEND_PLUGINS": "插件", - "LABEL_ENABLE_PLUGINS": "启用插件", - "ALERT_NO_PLUGINS": "当前还未安装插件。", - "LINK_INSTALL_NEW": "点击此处安装新插件!", - "HINT_CLICK_NAME": "点击名称来配置插件。" - }, "TAB_PACKAGES": { + "LABEL_ENABLE_PLUGINS": "启用插件", "LEGEND_AVAILABLE_FOR_UPDATE": "有更新可用", "LEGEND_AVAILABLE_FOR_INSTALLATION": "可用于安装", "LEGEND_INSTALLED_PACKAGES": "已安装的插件包", 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 9e69d13a2..f5e634cb1 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,5 +1,10 @@
+
+ + +
+
@@ -8,6 +13,13 @@
+
+ +
+
  @@ -17,7 +29,7 @@
-
+

diff --git a/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsPackagesTable.html b/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsPackagesTable.html index 5a7772396..43a53b216 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsPackagesTable.html +++ b/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsPackagesTable.html @@ -2,20 +2,18 @@ - - + + +
-   diff --git a/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsPlugins.html b/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsPlugins.html deleted file mode 100644 index 2ffbba54e..000000000 --- a/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsPlugins.html +++ /dev/null @@ -1,43 +0,0 @@ -
- -
- - -
- -
- - -
- -
- -
- -
- -
-
- -
- -
-

-
- - - - - - - - - - - - -
-
From 5213b804d9754bfa7900b59894a6ad1107a6ada5 Mon Sep 17 00:00:00 2001 From: djmaze Date: Fri, 16 Jul 2021 16:24:07 +0200 Subject: [PATCH 07/26] Some tiny changes --- README.md | 1 + dev/View/Popup/Compose.js | 1 + plugins/README.md | 3 ++- snappymail/v/0.0.0/app/libraries/RainLoop/Utils.php | 2 +- vendors/knockout/src/subscribables/subscribable.js | 1 + 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ac12eb8b9..a8ec87646 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ This fork of RainLoop has the following changes: * Replaced old Sabre library * AddressBook Contacts support MySQL/MariaDB utf8mb4 * Prevent Google FLoC +* Added [Fetch Metadata Request Headers](https://www.w3.org/TR/fetch-metadata/) checks ### Removal of old JavaScript diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index 6fbbca82b..3886fe3e8 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -744,6 +744,7 @@ class ComposePopupView extends AbstractViewPopup { this.initOnShow(type, oMessageOrArray, aToEmails, aCcEmails, aBccEmails, sCustomSubject, sCustomPlainText); } +// (navigator.standalone || matchMedia('(display-mode: standalone)').matches || matchMedia('(display-mode: fullscreen)').matches) && ThemeStore.isMobile() && this.oContent.requestFullscreen && this.oContent.requestFullscreen(); } diff --git a/plugins/README.md b/plugins/README.md index 177139cba..57279fb75 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -1,3 +1,4 @@ +PHP class Plugin extends \RainLoop\Plugins\AbstractPlugin @@ -196,7 +197,7 @@ $Plugin->addHook('hook.name', 'functionName'); string $sAction array &$aResponseItem -### filter.message-html' +### filter.message-html ### filter.message-plain params: \RainLoop\Model\Account $oAccount diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Utils.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Utils.php index 6c3ead748..b5f507762 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Utils.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Utils.php @@ -238,7 +238,7 @@ class Utils { static $sAppPath; if (!$sAppPath) { - $sAppPath = \preg_replace('#index\\.php.*$#D', '$1', $_SERVER['SCRIPT_NAME']); + $sAppPath = \preg_replace('#index\\.php.*$#D', '', $_SERVER['SCRIPT_NAME']); // $sAppPath = Api::Config()->Get('labs', 'app_default_path', ''); } return $sAppPath; diff --git a/vendors/knockout/src/subscribables/subscribable.js b/vendors/knockout/src/subscribables/subscribable.js index 3de09bca0..98686dc25 100644 --- a/vendors/knockout/src/subscribables/subscribable.js +++ b/vendors/knockout/src/subscribables/subscribable.js @@ -26,6 +26,7 @@ class koSubscription } disposeWhenNodeIsRemoved(node) { + // MutationObserver ? this._node = node; ko.utils.domNodeDisposal.addDisposeCallback(node, this._domNodeDisposalCallback = this.dispose.bind(this)); } From 6e26c767c31b6edb51a6393155db13a3f20c66f9 Mon Sep 17 00:00:00 2001 From: djmaze Date: Fri, 16 Jul 2021 16:27:19 +0200 Subject: [PATCH 08/26] Changes for base OwnCloud/NextCloud See https://github.com/the-djmaze/snappymail/issues/96 --- plugins/README.md | 4 + plugins/owncloud/OwnCloudSuggestions.php | 114 +++++++++++ plugins/owncloud/index.php | 180 ++++++++++++++++++ .../app/libraries/RainLoop/Actions/User.php | 10 + .../snappymail/attachmentsaction.php | 13 ++ 5 files changed, 321 insertions(+) create mode 100644 plugins/owncloud/OwnCloudSuggestions.php create mode 100644 plugins/owncloud/index.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/attachmentsaction.php diff --git a/plugins/README.md b/plugins/README.md index 57279fb75..f42178f31 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -278,6 +278,10 @@ $Plugin->addHook('hook.name', 'functionName'); params: string $sAction +### json.attachments + params: + \SnappyMail\AttachmentsAction $oData + ### json.suggestions-input-parameters params: string &$sQuery diff --git a/plugins/owncloud/OwnCloudSuggestions.php b/plugins/owncloud/OwnCloudSuggestions.php new file mode 100644 index 000000000..cb873c485 --- /dev/null +++ b/plugins/owncloud/OwnCloudSuggestions.php @@ -0,0 +1,114 @@ +getContactsManager(); + if (!$cm && !$cm->isEnabled()) + { + return $aResult; + } + + $aSearchResult = $cm->search($sQuery, $aParams); + } + else if (\class_exists('OCP\Contacts') && \OCP\Contacts::isEnabled()) + { + $aSearchResult = \OCP\Contacts::search($sQuery, $aParams); + } + else + { + return $aResult; + } + + //$this->oLogger->WriteDump($aSearchResult); + + $aHashes = array(); + if (\is_array($aSearchResult) && 0 < \count($aSearchResult)) + { + foreach ($aSearchResult as $aContact) + { + if (0 >= $iLimit) + { + break; + } + + $sUid = empty($aContact['UID']) ? '' : $aContact['UID']; + if (!empty($sUid)) + { + $mEmails = isset($aContact['EMAIL']) ? $aContact['EMAIL'] : ''; + + $sFullName = isset($aContact['FN']) ? \trim($aContact['FN']) : ''; + if (empty($sFullName)) + { + $sFullName = isset($aContact['NICKNAME']) ? \trim($aContact['NICKNAME']) : ''; + } + + if (!\is_array($mEmails)) + { + $mEmails = array($mEmails); + } + + foreach ($mEmails as $sEmail) + { + $sHash = '"'.$sFullName.'" <'.$sEmail.'>'; + if (!isset($aHashes[$sHash])) + { + $aHashes[$sHash] = true; + $aResult[] = array($sEmail, $sFullName); + $iLimit--; + } + } + } + } + + $aResult = \array_slice($aResult, 0, $iInputLimit); + } + + unset($aSearchResult, $aHashes); + } + catch (\Throwable $oException) + { + if ($this->oLogger) + { + $this->oLogger->WriteException($oException); + } + } + + return $aResult; + } + + /** + * @param \MailSo\Log\Logger $oLogger + */ + public function SetLogger($oLogger) + { + $this->oLogger = $oLogger instanceof \MailSo\Log\Logger ? $oLogger : null; + } +} diff --git a/plugins/owncloud/index.php b/plugins/owncloud/index.php new file mode 100644 index 000000000..7fb6a2fcd --- /dev/null +++ b/plugins/owncloud/index.php @@ -0,0 +1,180 @@ +addHook('main.fabrica', 'MainFabrica'); + $this->addHook('filter.app-data', 'FilterAppData'); + $this->addHook('json.attachments', 'DoAttachmentsActions'); + + $sAppPath = ''; + if (\class_exists('OC_App')) { + $sAppPath = \rtrim(\trim(\OC_App::getAppWebPath('snappymail')), '\\/').'/app/'; + } + if (!$sAppPath) { + $sUrl = \MailSo\Base\Http::SingletonInstance()->GetUrl(); + if ($sUrl && \preg_match('/\/index\.php\/apps\/snappymail/', $sUrl)) { + $sAppPath = \preg_replace('/\/index\.php\/apps\/snappymail.+$/', + '/apps/snappymail/app/', $sUrl); + } + } + $_SERVER['SCRIPT_NAME'] = $sAppPath; + } + } + + public function Supported() : string + { + if (!static::IsOwnCloud()) { + return 'OwnCloud not found to use this plugin'; + } + return ''; + } + + // DoAttachmentsActions + public function DoAttachmentsActions(\SnappyMail\AttachmentsAction $data) + { + if ('owncloud' === $data->action) { + if (static::IsOwnCloudLoggedIn() && \class_exists('OCP\Files')) { + $oFiles = \OCP\Files::getStorage('files'); + if ($oFiles && $data->filesProvider->IsActive() && \method_exists($oFiles, 'file_put_contents')) { + $sSaveFolder = $this->Config()->Get('plugin', 'save_folder', '') ?: 'Attachments'; + $oFiles->is_dir($sSaveFolder) || $oFiles->mkdir($sSaveFolder); + $data->result = true; + foreach ($data->items as $aItem) { + $sSavedFileName = isset($aItem['FileName']) ? $aItem['FileName'] : 'file.dat'; + $sSavedFileHash = !empty($aItem['FileHash']) ? $aItem['FileHash'] : ''; + if (!empty($sSavedFileHash)) { + $fFile = $data->filesProvider->GetFile($data->account, $sSavedFileHash, 'rb'); + if (\is_resource($fFile)) { + $sSavedFileNameFull = \MailSo\Base\Utils::SmartFileExists($sSaveFolder.'/'.$sSavedFileName, function ($sPath) use ($oFiles) { + return $oFiles->file_exists($sPath); + }); + + if (!$oFiles->file_put_contents($sSavedFileNameFull, $fFile)) { + $data->result = false; + } + + if (\is_resource($fFile)) { + \fclose($fFile); + } + } + } + } + } + } + + foreach ($data->items as $aItem) { + $sFileHash = (string) (isset($aItem['FileHash']) ? $aItem['FileHash'] : ''); + if (!empty($sFileHash)) { + $data->filesProvider->Clear($data->account, $sFileHash); + } + } + } + } + + /** + * TODO: create pre-login auth hook + */ + public function ServiceOwnCloudAuth() + { +/* + $this->oHttp->ServerNoCache(); + + if (!static::IsOwnCloud() || + !isset($_ENV['___snappymail_owncloud_email']) || + !isset($_ENV['___snappymail_owncloud_password']) || + empty($_ENV['___snappymail_owncloud_email']) + ) + { + $this->oActions->SetAuthLogoutToken(); + $this->oActions->Location('./'); + return ''; + } + + $bLogout = true; + + $sEmail = $_ENV['___snappymail_owncloud_email']; + $sPassword = $_ENV['___snappymail_owncloud_password']; + + try + { + $oAccount = $this->oActions->LoginProcess($sEmail, $sPassword); + $this->oActions->AuthToken($oAccount); + + $bLogout = !($oAccount instanceof \snappymail\Model\Account); + } + catch (\Exception $oException) + { + $this->oActions->Logger()->WriteException($oException); + } + + if ($bLogout) + { + $this->oActions->SetAuthLogoutToken(); + } + + $this->oActions->Location('./'); + return ''; +*/ + } + + /** + * @return void + */ + public function FilterAppData($bAdmin, &$aResult) + { + if (!$bAdmin && \is_array($aResult) && static::IsOwnCloud()) { + $key = \array_search(\RainLoop\Enumerations\Capa::AUTOLOGOUT, $aResult['Capa']); + if (false !== $key) { + unset($aResult['Capa'][$key]); + } + if (static::IsOwnCloudLoggedIn() && \class_exists('OCP\Files')) { + $aResult['System']['attachmentsActions'][] = 'owncloud'; + } + } + } + + /** + * @param string $sName + * @param mixed $mResult + */ + public function MainFabrica($sName, &$mResult) + { + if ('suggestions' === $sName && static::IsOwnCloud() && $this->Config()->Get('plugin', 'suggestions', true)) { + include_once __DIR__.'/OwnCloudSuggestions.php'; + if (!\is_array($mResult)) { + $mResult = array(); + } + $mResult[] = new OwnCloudSuggestions(); + } + } + + protected function configMapping() : array + { + return array( + \RainLoop\Plugins\Property::NewInstance('save_folder')->SetLabel('Save Folder') + ->SetDefaultValue('Attachments'), + \RainLoop\Plugins\Property::NewInstance('suggestions')->SetLabel('Suggestions') + ->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL) + ->SetDefaultValue(true) + ); + } +} diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php index 6952bbdc6..dfcd1bd5c 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php @@ -186,6 +186,16 @@ trait User } } break; + + default: + $data = new \SnappyMail\AttachmentsAction; + $data->action = $sAction; + $data->items = $aData; + $data->filesProvider = $oFilesProvider; + $data->account = $oAccount; + $this->Plugins()->RunHook('json.attachments', array($data)); + $mResult = $data->result; + break; } } else diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/attachmentsaction.php b/snappymail/v/0.0.0/app/libraries/snappymail/attachmentsaction.php new file mode 100644 index 000000000..5638950d2 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/attachmentsaction.php @@ -0,0 +1,13 @@ + Date: Fri, 16 Jul 2021 16:49:48 +0200 Subject: [PATCH 09/26] Fix fork url --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c2cda2472..b712239f1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ 1. Install node.js - `https://nodejs.org/download/` 2. Install yarn - `https://yarnpkg.com/en/docs/install` 3. Install gulp - `npm install gulp -g` -4. Fork snappymail - `https://github.com/the-djmaze/snappymail/issues/new#fork-destination-box` +4. Fork snappymail from https://github.com/the-djmaze/snappymail 5. Clone snappymail - `git clone git@github.com:USERNAME/snappymail.git snappymail` 6. `cd snappymail` 7. Install install all dependencies - `yarn install` From 2dde6c59ebbb35f27b23166b6b4d12df785c0728 Mon Sep 17 00:00:00 2001 From: djmaze Date: Fri, 16 Jul 2021 17:13:24 +0200 Subject: [PATCH 10/26] Bugfix: \RainLoop\Utils::IsOwnCloudLoggedIn() undefined --- plugins/owncloud/OwnCloudSuggestions.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/plugins/owncloud/OwnCloudSuggestions.php b/plugins/owncloud/OwnCloudSuggestions.php index cb873c485..a99e9052e 100644 --- a/plugins/owncloud/OwnCloudSuggestions.php +++ b/plugins/owncloud/OwnCloudSuggestions.php @@ -7,13 +7,6 @@ class OwnCloudSuggestions implements \RainLoop\Providers\Suggestions\ISuggestion */ protected $oLogger; - /** - * @param \RainLoop\Model\Account $oAccount - * @param string $sQuery - * @param int $iLimit = 20 - * - * @return array - */ public function Process(\RainLoop\Model\Account $oAccount, string $sQuery, int $iLimit = 20): array { $iInputLimit = $iLimit; @@ -22,7 +15,7 @@ class OwnCloudSuggestions implements \RainLoop\Providers\Suggestions\ISuggestion try { - if ('' === $sQuery || !$oAccount || !\RainLoop\Utils::IsOwnCloudLoggedIn()) + if ('' === $sQuery || !$oAccount || !OwnCloudPlugin::IsOwnCloudLoggedIn()) { return $aResult; } From 65200af210adb2d54c8254cfa2de4930641af51a Mon Sep 17 00:00:00 2001 From: djmaze Date: Sat, 17 Jul 2021 08:38:21 +0200 Subject: [PATCH 11/26] Added NextCloud Taken from https://github.com/pierre-alain-b/snappymail-nextcloud --- integrations/nextcloud/snappymail/INSTALL | 17 + integrations/nextcloud/snappymail/README.md | 76 ++++ integrations/nextcloud/snappymail/VERSION | 1 + .../nextcloud/snappymail/appinfo/app.php | 15 + .../nextcloud/snappymail/appinfo/info.xml | 30 ++ .../nextcloud/snappymail/appinfo/routes.php | 31 ++ .../nextcloud/snappymail/appinfo/version | 1 + .../nextcloud/snappymail/css/style.css | 6 + .../nextcloud/snappymail/img/favicon.png | Bin 0 -> 832 bytes .../nextcloud/snappymail/img/logo-64x64.png | Bin 0 -> 832 bytes .../nextcloud/snappymail/img/rainloop.svg | 265 ++++++++++++++ integrations/nextcloud/snappymail/js/admin.js | 12 + .../nextcloud/snappymail/js/personal.js | 12 + .../nextcloud/snappymail/js/rainloop.js | 114 ++++++ .../nextcloud/snappymail/js/resize.js | 26 ++ .../nextcloud/snappymail/l10n/en_GB.js | 9 + .../nextcloud/snappymail/l10n/en_GB.json | 7 + .../snappymail/lib/AppInfo/Application.php | 87 +++++ .../lib/Controller/AjaxController.php | 98 +++++ .../lib/Controller/PageController.php | 110 ++++++ .../snappymail/lib/Settings/AdminSettings.php | 45 +++ .../lib/Settings/PersonalSettings.php | 32 ++ .../snappymail/lib/Util/SnappyMailHelper.php | 341 ++++++++++++++++++ .../snappymail/templates/admin-local.php | 45 +++ .../nextcloud/snappymail/templates/empty.php | 1 + .../snappymail/templates/index-empty.php | 3 + .../nextcloud/snappymail/templates/index.php | 1 + .../snappymail/templates/personal.php | 8 + .../templates/personal_settings.php | 32 ++ plugins/nextcloud/NextCloudSuggestions.php | 107 ++++++ plugins/nextcloud/index.php | 180 +++++++++ plugins/owncloud/index.php | 2 +- 32 files changed, 1713 insertions(+), 1 deletion(-) create mode 100755 integrations/nextcloud/snappymail/INSTALL create mode 100644 integrations/nextcloud/snappymail/README.md create mode 100755 integrations/nextcloud/snappymail/VERSION create mode 100644 integrations/nextcloud/snappymail/appinfo/app.php create mode 100644 integrations/nextcloud/snappymail/appinfo/info.xml create mode 100755 integrations/nextcloud/snappymail/appinfo/routes.php create mode 100755 integrations/nextcloud/snappymail/appinfo/version create mode 100755 integrations/nextcloud/snappymail/css/style.css create mode 100644 integrations/nextcloud/snappymail/img/favicon.png create mode 100755 integrations/nextcloud/snappymail/img/logo-64x64.png create mode 100644 integrations/nextcloud/snappymail/img/rainloop.svg create mode 100755 integrations/nextcloud/snappymail/js/admin.js create mode 100755 integrations/nextcloud/snappymail/js/personal.js create mode 100755 integrations/nextcloud/snappymail/js/rainloop.js create mode 100755 integrations/nextcloud/snappymail/js/resize.js create mode 100644 integrations/nextcloud/snappymail/l10n/en_GB.js create mode 100644 integrations/nextcloud/snappymail/l10n/en_GB.json create mode 100644 integrations/nextcloud/snappymail/lib/AppInfo/Application.php create mode 100644 integrations/nextcloud/snappymail/lib/Controller/AjaxController.php create mode 100644 integrations/nextcloud/snappymail/lib/Controller/PageController.php create mode 100644 integrations/nextcloud/snappymail/lib/Settings/AdminSettings.php create mode 100644 integrations/nextcloud/snappymail/lib/Settings/PersonalSettings.php create mode 100644 integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php create mode 100755 integrations/nextcloud/snappymail/templates/admin-local.php create mode 100755 integrations/nextcloud/snappymail/templates/empty.php create mode 100755 integrations/nextcloud/snappymail/templates/index-empty.php create mode 100755 integrations/nextcloud/snappymail/templates/index.php create mode 100755 integrations/nextcloud/snappymail/templates/personal.php create mode 100755 integrations/nextcloud/snappymail/templates/personal_settings.php create mode 100644 plugins/nextcloud/NextCloudSuggestions.php create mode 100644 plugins/nextcloud/index.php diff --git a/integrations/nextcloud/snappymail/INSTALL b/integrations/nextcloud/snappymail/INSTALL new file mode 100755 index 000000000..d9c57d60b --- /dev/null +++ b/integrations/nextcloud/snappymail/INSTALL @@ -0,0 +1,17 @@ +************************************************************************ +* +* Nextcloud - SnappyMail Webmail package +* +* @author SnappyMail Team, Nextgen-Networks (@nextgen-networks), Tab Fitts (@tabp0le), Pierre-Alain Bandinelli (@pierre-alain-b) +* +************************************************************************ + +REQUIREMENTS: +- Nextcloud version 14 and above + +INSTALL & CONFIGURATION: +Start within Nextcloud, and click on the "+ Apps" button in the upper-left corner dropdown menu: +Then, enable the SnappyMail plugin that is in the "Social & communication" section. +After a quick wait, SnappyMail is installed. Even if it is really attractive, it is too soon to click on the newly appeared "Email" icon in the apps list. You should configure SnappyMail before using it (which makes some sense, doesn'it): go to Nextcloud admin panel (upper-right corner dropdown menu) and go to "Additionnal settings". There click on the "Go to SnappyMail Webmail admin panel". +In the SnappyMail admin prompt, the default login is "admin" and the default password is "12345". No need to advise you to change it once in the admin panel! +This is it, you are now free to configure SnappyMail as you wish. One important point is the Domains section when you will set up the IMAP/SMTP parameters that shall be associated with the email adresses of your users. diff --git a/integrations/nextcloud/snappymail/README.md b/integrations/nextcloud/snappymail/README.md new file mode 100644 index 000000000..12f2e2cb5 --- /dev/null +++ b/integrations/nextcloud/snappymail/README.md @@ -0,0 +1,76 @@ +# snappymail-nextcloud + +snappymail-nextcloud is a plugin for Nextcloud to use the excellent SnappyMail webmail (https://snappymail.eu/). + +## Which branch for which version of Nextcloud? + +- The master branch corresponds to the latest stable version of the plugin that is suitable for Nextcloud 19 and upwards. +- The 'up-to-nc18' corresponds to the working version of the plugin for Nextcloud 14 to 18. +- The 'nc14' branch corresponds to a beta version of the plugin for Nextcloud 14. All the changes of 'nc14' branch were merged into master when it was deemed suitable for production. +- The 'nc13' branch corresponds to the working version of the plugin for Nextcloud 10 to Nextcloud 13. + + +Thank you to all contributors to SnappyMail for nextcloud: +- SnappyMail Team, who initiated it +- Tab Fitts (@tabp0le) +- Nextgen Networks (@nextgen-networks) + +## How to Install + +Start within Nextcloud, and click on the "+ Apps" button in the upper-left corner dropdown menu: + +![Image1](https://github.com/pierre-alain-b/snappymail-nextcloud/blob/master/screenshots/help_a1.png) + +Then, enable the SnappyMail plugin that you will find in the "Social & communication" section: + +![Image2](https://github.com/pierre-alain-b/snappymail-nextcloud/blob/master/screenshots/help_a2.png) + +After a quick wait, SnappyMail is installed. Now you should configure it before use: open the Nextcloud admin panel (upper-right corner dropdown menu) and go to "Additionnal settings". There, click on the "Go to SnappyMail Webmail admin panel" link. + +![Image3](https://github.com/pierre-alain-b/snappymail-nextcloud/blob/master/screenshots/help_a3.png) + +To enter SnappyMail admin area, the default login is "admin" and the default password is "12345". Don't forget to change it once in the admin panel! + +From that point, all instance-wide SnappyMail settings can be tweaked as you wish. One important point is the "Domains" section where you should set up the IMAP/SMTP parameters that will be associated with the email adresses of your users. Basically, if a user of the Nextcloud instance starts SnappyMail and puts "firstname@domain.tld" as an email address, then SnappyMail should know how to connect to the IMAP & SMTP of domain.tld. You can fill in this information in the "Domains" section of the SnappyMail admin settings. + +![Image4](https://github.com/pierre-alain-b/snappymail-nextcloud/blob/master/screenshots/help_a4.png) + +## SnappyMail Settings, Where Are They? + +SnappyMail for Nextcloud is highly configurable. But settings are available in multiple places and this can be misleading for first-time users. + +### SnappyMail admin settings +SnappyMail admin settings can be reached only by the Nextcloud administrator. Open the Nextcloud admin panel ("Admin" in the upper-right corner dropdown menu) and go to "Additionnal settings". There, click on the "Go to SnappyMail Webmail admin panel" link. Alternatively, you may use the following link: https://path.to.nextcloud/index.php/apps/snappymail/app/?admin. + +SnappyMail admin settings include all settings that will apply to all SnappyMail users (default login rules, branding, management of plugins, security rules and domains). + +### SnappyMail user settings +Each user of SnappyMail can also change user-specific behaviors in the SnappyMail user settings. SnappyMail user settings are found within SnappyMail by clicking on the user button (in the upper-right corner of SnappyMail) and then choosing "Settings" in the dropdown menu. + +SnappyMail user settings include management of contacts, of email accounts, of folders, appearance and OpenPGP. + +### The specificity of SnappyMail user accounts +The plugin passes the login information of the user to the SnappyMail app which then creates and manages the user accounts. Accounts in SnappyMail are based soley on the authenticated email accounts, and do not take into account the nextcloud user which created them in the first place. If two or more Nextcloud users have the same email account in additional settings, they will in fact share the same 'email account' in SnappyMail including any additional email accounts that they may have added subsequently to their main account. +This is to be kept in mind for the use case where multiple users shall have the same email account but may be also tempted to add additionnal acounts to their SnappyMail. + +## How to auto-connect to SnappyMail? + +### Auto-connection for all Nextcloud users +If your Nextcloud users base is synchronized with an email system, then it is possible that Nextcloud credentials could be used right away to access the centralized email system. In the SnappyMail admin settings, the Nextcloud administrator can then tick the "Automatically login with Nextcloud/Nextcloud user credentials" checkbox. + +Beware, if you tick this box, all Nextcloud users will *not* be able to use the override it with the setting below. + +### Auto-connection for one user at a time +Except if the above setting is activated, any Nextcloud user can have Nextcloud and SnappyMail keep in mind the default email/password to connect to SnappyMail. There, logging in Nextcloud is sufficient to then access SnappyMail within Nextcloud. + +To fill in the default email address and password to use, each Nextcloud user should go in the personal settings: choose "Personal" in the upper-right corner dropdown menu. Then select the "Rainlopp webmail" section. You can also use this direct link: https://path.to.nextcloud/index.php/settings/personal#snappymail-webmail. + + +## How to Activate SnappyMail Logging and then Find Logs + +You can activate SnappyMail logging here: `/path/to/nextcloud/data/snappymail-storage/_data_/_default_/configs/application.ini` +``` +[logs] +enable = On +``` +Logs are then available in `/path/to/nextcloud/data/snappymail-storage/_data_/_default_/logs/` diff --git a/integrations/nextcloud/snappymail/VERSION b/integrations/nextcloud/snappymail/VERSION new file mode 100755 index 000000000..aedc15bb0 --- /dev/null +++ b/integrations/nextcloud/snappymail/VERSION @@ -0,0 +1 @@ +2.5.3 diff --git a/integrations/nextcloud/snappymail/appinfo/app.php b/integrations/nextcloud/snappymail/appinfo/app.php new file mode 100644 index 000000000..7b86a17b8 --- /dev/null +++ b/integrations/nextcloud/snappymail/appinfo/app.php @@ -0,0 +1,15 @@ +registerNavigation(); +$app->registerPersonalSettings(); +$app->getContainer()->query('SnappyMailHelper')->registerHooks(); + diff --git a/integrations/nextcloud/snappymail/appinfo/info.xml b/integrations/nextcloud/snappymail/appinfo/info.xml new file mode 100644 index 000000000..8823048cb --- /dev/null +++ b/integrations/nextcloud/snappymail/appinfo/info.xml @@ -0,0 +1,30 @@ + + + snappymail + SnappyMail + SnappyMail Webmail + Simple, modern and fast web-based email client. After enabling in Nextcloud, go to Nextcloud admin panel, "Additionnal settings" and you will see a "SnappyMail webmail" section. There, click on the link to go to the SnappyMail admin panel. The default user/password is admin/12345. This version is based on SnappyMail 2.5.3 (2021-07). + 2.5.3 + agpl + SnappyMail Team, Nextgen-Networks, Tab Fitts, Nathan Kinkade, Pierre-Alain Bandinelli + SnappyMail + + https://github.com/pierre-alain-b/snappymail-nextcloud/blob/master/README.md + https://snappymail.eu/ + + social + tools + integration + https://raw.githubusercontent.com/pierre-alain-b/snappymail-nextcloud/master/screenshots/2016.10.20-screenshot.jpg + https://github.com/pierre-alain-b/snappymail-nextcloud + https://github.com/pierre-alain-b/snappymail-nextcloud/issues + https://raw.githubusercontent.com/pierre-alain-b/snappymail-nextcloud/master/screenshots/2016.10.20-screenshot.jpg + + + + + + + OCA\SnappyMail\Settings\AdminSettings + + diff --git a/integrations/nextcloud/snappymail/appinfo/routes.php b/integrations/nextcloud/snappymail/appinfo/routes.php new file mode 100755 index 000000000..a19b5a4f3 --- /dev/null +++ b/integrations/nextcloud/snappymail/appinfo/routes.php @@ -0,0 +1,31 @@ + [ + [ + 'name' => 'page#index', + 'url' => '/', + 'verb' => 'GET' + ], + [ + 'name' => 'page#appGet', + 'url' => '/app/', + 'verb' => 'GET' + ], + [ + 'name' => 'page#appPost', + 'url' => '/app/', + 'verb' => 'POST' + ], + [ + 'name' => 'ajax#setPersonal', + 'url' => '/ajax/personal.php', + 'verb' => 'POST' + ], + [ + 'name' => 'ajax#setAdmin', + 'url' => '/ajax/admin.php', + 'verb' => 'POST' + ] + ] +]; diff --git a/integrations/nextcloud/snappymail/appinfo/version b/integrations/nextcloud/snappymail/appinfo/version new file mode 100755 index 000000000..a8a188756 --- /dev/null +++ b/integrations/nextcloud/snappymail/appinfo/version @@ -0,0 +1 @@ +7.1.2 diff --git a/integrations/nextcloud/snappymail/css/style.css b/integrations/nextcloud/snappymail/css/style.css new file mode 100755 index 000000000..88f73b2f7 --- /dev/null +++ b/integrations/nextcloud/snappymail/css/style.css @@ -0,0 +1,6 @@ +/* +Empty style sheet! +Only needed to give you the opportunity to theme the owncloud part +of the snappymail app with the theming system integrated in ownCoud +if the snappymail app is activated. +*/ diff --git a/integrations/nextcloud/snappymail/img/favicon.png b/integrations/nextcloud/snappymail/img/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce1dd1a886c906ec36cee1dec72b9df605886f02 GIT binary patch literal 832 zcmV-G1Hb%k7RCwC#n$brDK^(`2<+!Jah`6UC zB6^C5h=@4)4|jR5=a_ zzRF^}svNfv^ZdeBqWN>ez`6O3CP!Ob6K4KgljDGB{)*sTz z^!1_w(9GG-TyfDP!Xo{00A?<4KW=CfaL&!$~?mW93ud- z3_)ie1;GE|1Obpm2w~>A#vwlZ0OULT3;~cO2o&>Dvk;$t01|?#%xy-f#%wh&H3#uH z%$L{-A&Pnb^MGX;LLBpvWebE<%-aGXRr6^>@B+0UGRf&e7>ied9i0f84^6TZJ!A+9whO+(;!!F#YF;zg_o!^}%< zGkk06A@JD`#0GHz6bHf##J%YUURb+q4dR=rH)D8#QX6s=7zaZ2Kh67ZKWsPpMwF>h zmcl094*z1_Z=xoi4f_x)S#E};)g`u~9hP>B+z5nnM$H?8a7BP%`{qb22=eStDWm3x zg)jxNiZw*A)>t0*2B$?WvJ%!1$@}hbPqxO2d_LeA15oG_B85LARQOY3?wp_|o{0^U zHL<40a`3hPI3dTTW*ofXI!ef~!-ywEDQATr105Wd)|viX?ieA*mL>=98jTQg>=7rG z!(@x>@8onp!CAW{&f2af2c=}kUg0{JbIi%f$;rvd$*HLN6<`1!uF_d9==`$)0000< KMNUMnLSTX$7j)+U literal 0 HcmV?d00001 diff --git a/integrations/nextcloud/snappymail/img/logo-64x64.png b/integrations/nextcloud/snappymail/img/logo-64x64.png new file mode 100755 index 0000000000000000000000000000000000000000..ce1dd1a886c906ec36cee1dec72b9df605886f02 GIT binary patch literal 832 zcmV-G1Hb%k7RCwC#n$brDK^(`2<+!Jah`6UC zB6^C5h=@4)4|jR5=a_ zzRF^}svNfv^ZdeBqWN>ez`6O3CP!Ob6K4KgljDGB{)*sTz z^!1_w(9GG-TyfDP!Xo{00A?<4KW=CfaL&!$~?mW93ud- z3_)ie1;GE|1Obpm2w~>A#vwlZ0OULT3;~cO2o&>Dvk;$t01|?#%xy-f#%wh&H3#uH z%$L{-A&Pnb^MGX;LLBpvWebE<%-aGXRr6^>@B+0UGRf&e7>ied9i0f84^6TZJ!A+9whO+(;!!F#YF;zg_o!^}%< zGkk06A@JD`#0GHz6bHf##J%YUURb+q4dR=rH)D8#QX6s=7zaZ2Kh67ZKWsPpMwF>h zmcl094*z1_Z=xoi4f_x)S#E};)g`u~9hP>B+z5nnM$H?8a7BP%`{qb22=eStDWm3x zg)jxNiZw*A)>t0*2B$?WvJ%!1$@}hbPqxO2d_LeA15oG_B85LARQOY3?wp_|o{0^U zHL<40a`3hPI3dTTW*ofXI!ef~!-ywEDQATr105Wd)|viX?ieA*mL>=98jTQg>=7rG z!(@x>@8onp!CAW{&f2af2c=}kUg0{JbIi%f$;rvd$*HLN6<`1!uF_d9==`$)0000< KMNUMnLSTX$7j)+U literal 0 HcmV?d00001 diff --git a/integrations/nextcloud/snappymail/img/rainloop.svg b/integrations/nextcloud/snappymail/img/rainloop.svg new file mode 100644 index 000000000..645a728ff --- /dev/null +++ b/integrations/nextcloud/snappymail/img/rainloop.svg @@ -0,0 +1,265 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integrations/nextcloud/snappymail/js/admin.js b/integrations/nextcloud/snappymail/js/admin.js new file mode 100755 index 000000000..e84b4a0c0 --- /dev/null +++ b/integrations/nextcloud/snappymail/js/admin.js @@ -0,0 +1,12 @@ + +/** + * Nextcloud - SnappyMail mail plugin + * + * @author SnappyMail Team, Nextgen-Networks (@nextgen-networks), Tab Fitts (@tabp0le), Pierre-Alain Bandinelli (@pierre-alain-b) + * + * Based initially on https://github.com/SnappyMail/snappymail-webmail/tree/master/build/owncloud + */ + +$(function() { + SnappyMailFormHelper('#mail-snappymail-admin-form', 'admin.php'); +}); diff --git a/integrations/nextcloud/snappymail/js/personal.js b/integrations/nextcloud/snappymail/js/personal.js new file mode 100755 index 000000000..b2a7d7c3d --- /dev/null +++ b/integrations/nextcloud/snappymail/js/personal.js @@ -0,0 +1,12 @@ + +/** + * Nextcloud - SnappyMail mail plugin + * + * @author SnappyMail Team, Nextgen-Networks (@nextgen-networks), Tab Fitts (@tabp0le), Pierre-Alain Bandinelli (@pierre-alain-b) + * + * Based initially on https://github.com/SnappyMail/snappymail-webmail/tree/master/build/owncloud + */ + +$(function() { + SnappyMailFormHelper('#mail-snappymail-personal-form', 'personal.php'); +}); diff --git a/integrations/nextcloud/snappymail/js/rainloop.js b/integrations/nextcloud/snappymail/js/rainloop.js new file mode 100755 index 000000000..d8687f21b --- /dev/null +++ b/integrations/nextcloud/snappymail/js/rainloop.js @@ -0,0 +1,114 @@ +// Do the following things once the document is fully loaded. +document.onreadystatechange = function () { + if (document.readyState === 'complete') { + watchIFrameTitle(); + } +} + +// The SnappyMail application is already configured to modify the element +// of its root document with the number of unread messages in the inbox. +// However, its document is the SnappyMail iframe. This function sets up a +// Mutation Observer to watch the <title> element of the iframe for changes in +// the unread message count and propagates that to the parent <title> element, +// allowing the unread message count to be displayed in the NC tab's text when +// the SnappyMail app is selected. +function watchIFrameTitle() { + iframe = document.getElementById('rliframe'); + if (!iframe) { + return; + } + target = iframe.contentDocument.getElementsByTagName('title')[0]; + config = { + characterData: true, + childList: true, + subtree: true + } + observer = new MutationObserver(function(mutations) { + title = mutations[0].target.innerText; + if (title) { + matches = title.match(/\(([0-9]+)\)/); + if (matches) { + document.title = '('+ matches[1] + ') ' + t('snappymail', 'Email') + ' - Nextcloud'; + } else { + document.title = t('snappymail', 'Email') + ' - Nextcloud'; + } + } + }); + observer.observe(target, config); +} + +function SnappyMailFormHelper(sID, sAjaxFile, fCallback) +{ + try + { + var + oForm = $(sID), + oSubmit = $('#snappymail-save-button', oForm), + sSubmitValue = oSubmit.val(), + oDesc = oForm.find('.snappymail-result-desc') + ; + + oSubmit.click(function (oEvent) { + + var oDefAjax = null; + + oEvent.preventDefault(); + + oForm + .addClass('snappymail-ajax') + .removeClass('snappymail-error') + .removeClass('snappymail-success') + ; + + oDesc.text(''); + oSubmit.val('...'); + + oDefAjax = $.ajax({ + 'type': 'POST', + 'async': true, + 'url': OC.filePath('snappymail', 'ajax', sAjaxFile), + 'data': oForm.serialize(), + 'dataType': 'json', + 'global': true + }); + + oDefAjax.always(function (oData) { + + var bResult = false; + + oForm.removeClass('snappymail-ajax'); + oSubmit.val(sSubmitValue); + + if (oData) + { + bResult = 'success' === oData['status']; + if (oData['Message']) + { + oDesc.text(t('snappymail', oData['Message'])); + } + } + + if (bResult) + { + oForm.addClass('snappymail-success'); + } + else + { + oForm.addClass('snappymail-error'); + if ('' === oDesc.text()) + { + oDesc.text(t('snappymail', 'Error')); + } + } + + if (fCallback) + { + fCallback(bResult, oData); + } + }); + + return false; + }); + } + catch(e) {} +} diff --git a/integrations/nextcloud/snappymail/js/resize.js b/integrations/nextcloud/snappymail/js/resize.js new file mode 100755 index 000000000..5c16f0022 --- /dev/null +++ b/integrations/nextcloud/snappymail/js/resize.js @@ -0,0 +1,26 @@ +$(function (window, document) { + + var + buffer = 0, //was 5 but this was creating a white space of 5px at the bottom of the page + ifr = document.getElementById('rliframe') + ; + + + function pageY(elem) { + return elem.offsetParent ? (elem.offsetTop + pageY(elem.offsetParent)) : elem.offsetTop; + } + + function resizeIframe() { + var height = document.documentElement.clientHeight; + height -= pageY(ifr) + buffer; + height = (height < 0) ? 0 : height; + ifr.style.height = height + 'px'; + } + + if (ifr) + { + ifr.onload = resizeIframe; + window.onresize = resizeIframe; + } + +}(window, document)); diff --git a/integrations/nextcloud/snappymail/l10n/en_GB.js b/integrations/nextcloud/snappymail/l10n/en_GB.js new file mode 100644 index 000000000..dc856193f --- /dev/null +++ b/integrations/nextcloud/snappymail/l10n/en_GB.js @@ -0,0 +1,9 @@ +OC.L10N.register( + "snappymail", + { + "Email" : "Email", + "Error" : "Error", + "Save" : "Save", + "Password" : "Password" +}, +"nplurals=2; plural=(n != 1);"); diff --git a/integrations/nextcloud/snappymail/l10n/en_GB.json b/integrations/nextcloud/snappymail/l10n/en_GB.json new file mode 100644 index 000000000..98a2adb74 --- /dev/null +++ b/integrations/nextcloud/snappymail/l10n/en_GB.json @@ -0,0 +1,7 @@ +{ "translations": { + "Email" : "Email", + "Error" : "Error", + "Save" : "Save", + "Password" : "Password" +},"pluralForm" :"nplurals=2; plural=(n != 1);" +} \ No newline at end of file diff --git a/integrations/nextcloud/snappymail/lib/AppInfo/Application.php b/integrations/nextcloud/snappymail/lib/AppInfo/Application.php new file mode 100644 index 000000000..6cfe12691 --- /dev/null +++ b/integrations/nextcloud/snappymail/lib/AppInfo/Application.php @@ -0,0 +1,87 @@ +<?php + +namespace OCA\SnappyMail\AppInfo; + +use OCA\SnappyMail\Util\SnappyMailHelper; +use OCA\SnappyMail\Controller\AjaxController; +use OCA\SnappyMail\Controller\PageController; + +use OCP\AppFramework\App; +use OCP\IL10N; +use OCP\IUser; + +class Application extends App { + + public function __construct(array $urlParams = []) { + parent::__construct('snappymail', $urlParams); + + $container = $this->getContainer(); + $server = $container->getServer(); + $config = $server->getConfig(); + + /** + * Controllers + */ + $container->registerService( + 'PageController', function($c) { + return new PageController( + $c->query('AppName'), + $c->query('Request'), + $c->getServer()->getAppManager(), + $c->query('ServerContainer')->getConfig(), + $c->getServer()->getSession() + ); + } + ); + + $container->registerService( + 'AjaxController', function($c) { + return new AjaxController( + $c->query('AppName'), + $c->query('Request'), + $c->getServer()->getAppManager(), + $c->query('ServerContainer')->getConfig(), + $c->query(IL10N::class) + ); + } + ); + + /** + * Utils + */ + $container->registerService( + 'SnappyMailHelper', function($c) { + return new SnappyMailHelper( + $c->getServer()->getConfig(), + $c->getServer()->getUserSession(), + $c->getServer()->getAppManager(), + $c->getServer()->getSession() + ); + } + ); + + // Add script js/snappymail.js + \OCP\Util::addScript('snappymail', 'snappymail'); + } + + public function registerNavigation() { + $container = $this->getContainer(); + + $container->query('OCP\INavigationManager')->add(function () use ($container) { + $urlGenerator = $container->query('OCP\IURLGenerator'); + return [ + 'id' => 'snappymail', + 'order' => 10, + 'href' => $urlGenerator->linkToRoute('snappymail.page.index'), + 'icon' => $urlGenerator->imagePath('snappymail', 'snappymail.svg'), + 'name' => \OCP\Util::getL10N('snappymail')->t('Email') + ]; + }); + } + + public function registerPersonalSettings() { + \OCP\App::registerPersonal('snappymail', 'templates/personal'); + } + +} + diff --git a/integrations/nextcloud/snappymail/lib/Controller/AjaxController.php b/integrations/nextcloud/snappymail/lib/Controller/AjaxController.php new file mode 100644 index 000000000..e0a561138 --- /dev/null +++ b/integrations/nextcloud/snappymail/lib/Controller/AjaxController.php @@ -0,0 +1,98 @@ +<?php + +namespace OCA\SnappyMail\Controller; + +use OCA\SnappyMail\Util\SnappyMailHelper; + +use OCP\App\IAppManager; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\JSONResponse; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IRequest; + +class AjaxController extends Controller { + private $config; + private $appManager; + + public function __construct(string $appName, IRequest $request, IAppManager $appManager, IConfig $config, IL10N $l) { + parent::__construct($appName, $request); + $this->config = $config; + $this->appManager = $appManager; + $this->l = $l; + } + + public function setAdmin(): JSONResponse { + try { + $sUrl = ''; + $sPath = ''; + $bAutologin = false; + + if (isset($_POST['appname']) && 'snappymail' === $_POST['appname']) { + $this->config->setAppValue('snappymail', 'snappymail-autologin', + isset($_POST['snappymail-autologin']) ? '1' === $_POST['snappymail-autologin'] : false); + $this->config->setAppValue('snappymail', 'snappymail-autologin-with-email', + isset($_POST['snappymail-autologin']) ? '2' === $_POST['snappymail-autologin'] : false); + $bAutologin = $this->config->getAppValue('snappymail', 'snappymail-autologin', false); + } else { + return new JSONResponse([ + 'status' => 'error', + 'Message' => $this->l->t('Invalid argument(s)') + ]); + } + + return new JSONResponse([ + 'status' => 'success', + 'Message' => $this->l->t('Saved successfully') + ]); + } catch (Exception $e) { + return new JSONResponse([ + 'status' => 'error', + 'Message' => $e->getMessage() + ]); + } + } + + /** + * @NoAdminRequired + */ + public function setPersonal(): JSONResponse { + try { + + if (isset($_POST['appname'], $_POST['snappymail-password'], $_POST['snappymail-email']) && 'snappymail' === $_POST['appname']) { + $sUser = \OC::$server->getUserSession()->getUser()->getUID(); + + $sPostEmail = $_POST['snappymail-email']; + $this->config->setUserValue($sUser, 'snappymail', 'snappymail-email', $sPostEmail); + + $sPass = $_POST['snappymail-password']; + if ('******' !== $sPass && '' !== $sPass) { + include_once $this->appManager->getAppPath('snappymail').'/lib/Util/SnappyMailHelper.php'; + + $this->config->setUserValue($sUser, 'snappymail', 'snappymail-password', + SnappyMailHelper::encodePassword($sPass, md5($sPostEmail))); + } + + $sEmail = $this->config->getUserValue($sUser, 'snappymail', 'snappymail-email', ''); + } else { + return new JSONResponse([ + 'status' => 'error', + 'Message' => $this->l->t('Invalid argument(s)'), + 'Email' => $sEmail + ]); + } + + return new JSONResponse([ + 'status' => 'success', + 'Message' => $this->l->t('Saved successfully'), + 'Email' => $sEmail + ]); + } catch (Exception $e) { + return new JSONResponse([ + 'status' => 'error', + 'Message' => $e->getMessage() + ]); + } + } +} + diff --git a/integrations/nextcloud/snappymail/lib/Controller/PageController.php b/integrations/nextcloud/snappymail/lib/Controller/PageController.php new file mode 100644 index 000000000..2279430e3 --- /dev/null +++ b/integrations/nextcloud/snappymail/lib/Controller/PageController.php @@ -0,0 +1,110 @@ +<?php + +namespace OCA\SnappyMail\Controller; + +use OCA\SnappyMail\Util\SnappyMailHelper; + +use OCP\App\IAppManager; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\ContentSecurityPolicy; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\IConfig; +use OCP\IRequest; +use OCP\ISession; + +class PageController extends Controller { + private $userId; + private $config; + private $appManager; + private $appPath; + private $session; + + public function __construct($AppName, IRequest $request, IAppManager $appManager, IConfig $config, ISession $session) { + parent::__construct($AppName, $request); + $this->appPath = $appManager->getAppPath('snappymail'); + $this->config = $config; + $this->appManager = $appManager; + $this->session = $session; + } + + /** + * @NoAdminRequired + * @NoCSRFRequired + */ + public function index() { + \OC::$server->getNavigationManager()->setActiveEntry('snappymail'); + + \OCP\Util::addStyle('snappymail', 'style'); + + $sUrl = SnappyMailHelper::normalizeUrl(SnappyMailHelper::getAppUrl()); + + $params = [ + 'snappymail-iframe-url' => SnappyMailHelper::normalizeUrl($sUrl).'?OwnCloudAuth' + ]; + + $response = new TemplateResponse('snappymail', 'index', $params); + + $csp = new ContentSecurityPolicy(); + $csp->addAllowedFrameDomain("'self'"); + $response->setContentSecurityPolicy($csp); + + return $response; + } + + /** + * @NoAdminRequired + * @NoCSRFRequired + */ + public function appGet() { + $this->app(); + } + + /** + * @NoAdminRequired + * @NoCSRFRequired + */ + public function appPost() { + $this->app(); + } + + public function app() { + + SnappyMailHelper::regSnappyMailDataFunction(); + + if (isset($_GET['OwnCloudAuth'])) { + $sEmail = ''; + $sEncodedPassword = ''; + + $sUser = \OC::$server->getUserSession()->getUser()->getUID(); + + if ($this->config->getAppValue('snappymail', 'snappymail-autologin', false)) { + $sEmail = $sUser; + $sPasswordSalt = $sUser; + $sEncodedPassword = $this->session['snappymail-autologin-password']; + } else if ($this->config->getAppValue('snappymail', 'snappymail-autologin-with-email', false)) { + $sEmail = $this->config->getUserValue($sUser, 'settings', 'email', ''); + $sPasswordSalt = $sUser; + $sEncodedPassword = $this->session['snappymail-autologin-password']; + } + + // If the user has set credentials for SnappyMail in their personal + // settings, override everything before and use those instead. + $sIndividualEmail = $this->config->getUserValue($sUser, 'snappymail', 'snappymail-email', ''); + if ($sIndividualEmail) { + $sEmail = $sIndividualEmail; + $sPasswordSalt = $sEmail; + $sEncodedPassword = $this->config->getUserValue($sUser, 'snappymail', 'snappymail-password', ''); + } + + $sDecodedPassword = SnappyMailHelper::decodePassword($sEncodedPassword, md5($sPasswordSalt)); + + $_ENV['___snappymail_owncloud_email'] = $sEmail; + $_ENV['___snappymail_owncloud_password'] = $sDecodedPassword; + } + + include $this->appPath . '/app/index.php'; + + } + +} + diff --git a/integrations/nextcloud/snappymail/lib/Settings/AdminSettings.php b/integrations/nextcloud/snappymail/lib/Settings/AdminSettings.php new file mode 100644 index 000000000..df6107e47 --- /dev/null +++ b/integrations/nextcloud/snappymail/lib/Settings/AdminSettings.php @@ -0,0 +1,45 @@ +<?php +namespace OCA\SnappyMail\Settings; + +use OCA\SnappyMail\Util\SnappyMailHelper; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\IConfig; +use OCP\Settings\ISettings; + +class AdminSettings implements ISettings { + + private $config; + + public function __construct(IConfig $config) { + $this->config = $config; + } + + public function getForm() { + $keys = [ + 'snappymail-autologin', + 'snappymail-autologin-with-email' + ]; + + $parameters = []; + foreach ($keys as $k) { + $v = $this->config->getAppValue('snappymail', $k); + $parameters[$k] = $v; + } + + $uid = \OC::$server->getUserSession()->getUser()->getUID(); + if (\OC_User::isAdminUser($uid)) { + $parameters['snappymail-admin-panel-link'] = SnappyMailHelper::getAppUrl().'?admin'; + } + + return new TemplateResponse('snappymail', 'admin-local', $parameters); + } + + public function getSection() { + return 'additional'; + } + + public function getPriority() { + return 50; + } + +} diff --git a/integrations/nextcloud/snappymail/lib/Settings/PersonalSettings.php b/integrations/nextcloud/snappymail/lib/Settings/PersonalSettings.php new file mode 100644 index 000000000..e931868a5 --- /dev/null +++ b/integrations/nextcloud/snappymail/lib/Settings/PersonalSettings.php @@ -0,0 +1,32 @@ +<?php +namespace OCA\SnappyMail\Settings; + +use OCP\AppFramework\Http\TemplateResponse; +use OCP\IConfig; + +class PersonalSettings { + private $config; + + public function __construct(IConfig $config) { + $this->config = $config; + } + + public function getForm() { + $uid = \OC::$server->getUserSession()->getUser()->getUID(); + + $keys = [ + 'snappymail-email', + 'snappymail-password' + ]; + + $parameters = []; + foreach ($keys as $k) { + $v = $this->config->getUserValue($uid, 'snappymail', $k); + $parameters[$k] = $v; + } + + return new TemplateResponse('snappymail', 'personal_settings', $parameters, ''); + } + +} + diff --git a/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php b/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php new file mode 100644 index 000000000..6bb496e35 --- /dev/null +++ b/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php @@ -0,0 +1,341 @@ +<?php + +namespace OCA\SnappyMail\Util; + +use OCP\App\IAppManager; +use OCP\IConfig; +use OCP\ISession; +use OCP\IUserSession; + +class SnappyMailHelper { + + private $appManager; + private $config; + private $session; + private $userSession; + + public function __construct(IConfig $config, IUserSession $userSession, IAppManager $appManager, ISession $session) { + $this->appManager = $appManager; + $this->config = $config; + $this->userSession = $userSession; + $this->session = $session; + } + + public function registerHooks() { + $this->userSession->listen('\OC\User', 'postLogin', function($user, $loginName, $password, $isTokenLogin) { + $this->login($user, $loginName, $password, $isTokenLogin, $this->config, $this->session); + }); + + $this->userSession->listen('\OC\User', 'logout', function() { + $this->logout($this->appManager, $this->config, $this->session); + }); + } + + /** + * @return string + */ + public static function getAppUrl() + { + $sRequestUri = \OC::$server->getURLGenerator()->linkToRoute('snappymail.page.appGet'); + if ($sRequestUri) + { + return $sRequestUri; + } + + $sRequestUri = empty($_SERVER['REQUEST_URI']) ? '': trim($_SERVER['REQUEST_URI']); + $sRequestUri = preg_replace('/index.php\/.+$/', 'index.php/', $sRequestUri); + $sRequestUri = $sRequestUri.'apps/snappymail/app/'; + + return '/'.ltrim($sRequestUri, '/\\'); + } + + /** + * @param string $sPath + * @param string $sEmail + * @param string $sPassword + * + * @return string + */ + public static function getSsoHash($sPath, $sEmail, $sPassword) + { + $SsoHash = ''; + + $sPath = rtrim(trim($sPath), '\\/').'/index.php'; + if (file_exists($sPath)) + { + self::regSnappyMailDataFunction(); + + $_ENV['RAINLOOP_INCLUDE_AS_API'] = true; + include $sPath; + + if (class_exists('\\SnappyMail\\Api')) + { + $SsoHash = \SnappyMail\Api::GetUserSsoHash($sEmail, $sPassword); + } + } + + return $SsoHash; + } + + /** + * @param string $sUrl + * + * @return string + */ + public static function normalizeUrl($sUrl) + { + $sUrl = rtrim(trim($sUrl), '/\\'); + if ('.php' !== strtolower(substr($sUrl, -4))) + { + $sUrl .= '/'; + } + + return $sUrl; + } + + /** + * @return boolean + */ + public static function mcryptSupported() + { + return function_exists('mcrypt_encrypt') && + function_exists('mcrypt_decrypt') && + defined('MCRYPT_RIJNDAEL_256') && + defined('MCRYPT_MODE_ECB'); + } + + /** + * @return string + */ + public static function openSslSupportedMethod() + { + $method = 'AES-256-CBC'; + return function_exists('openssl_encrypt') && + function_exists('openssl_decrypt') && + function_exists('openssl_random_pseudo_bytes') && + function_exists('openssl_cipher_iv_length') && + function_exists('openssl_get_cipher_methods') && + defined('OPENSSL_RAW_DATA') && defined('OPENSSL_ZERO_PADDING') && + in_array($method, openssl_get_cipher_methods()) ? $method : ''; + } + + /** + * @param string $sMethod + * @param string $sPassword + * @param string $sSalt + * + * @return string + */ + public static function encodePasswordSsl($sMethod, $sPassword, $sSalt) + { + $sData = base64_encode($sPassword); + + $iv = @openssl_random_pseudo_bytes(openssl_cipher_iv_length($sMethod)); + $r = @openssl_encrypt($sData, $sMethod, md5($sSalt), OPENSSL_RAW_DATA, $iv); + + return @base64_encode(base64_encode($r).'|'.base64_encode($iv)); + } + + /** + * @param string $sMethod + * @param string $sPassword + * @param string $sSalt + * + * @return string + */ + public static function decodePasswordSsl($sMethod, $sPassword, $sSalt) + { + $sLine = base64_decode(trim($sPassword)); + $aParts = explode('|', $sLine, 2); + + if (is_array($aParts) && !empty($aParts[0]) && !empty($aParts[1])) { + + $sData = @base64_decode($aParts[0]); + $iv = @base64_decode($aParts[1]); + + return @base64_decode(trim( + @openssl_decrypt($sData, $sMethod, md5($sSalt), OPENSSL_RAW_DATA, $iv) + )); + } + + return ''; + } + + /** + * @param string $sPassword + * @param string $sSalt + * + * @return string + */ + public static function encodePassword($sPassword, $sSalt) + { + $method = self::openSslSupportedMethod(); + if ($method) + { + return self::encodePasswordSsl($method, $sPassword, $sSalt); + } + else if (self::mcryptSupported()) + { + return @trim(base64_encode( + @mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($sSalt), base64_encode($sPassword), MCRYPT_MODE_ECB) + )); + } + + return @trim(base64_encode($sPassword)); + } + + /** + * @param string $sPassword + * @param string $sSalt + * + * @return string + */ + public static function decodePassword($sPassword, $sSalt) + { + $method = self::openSslSupportedMethod(); + if ($method) + { + return self::decodePasswordSsl($method, $sPassword, $sSalt); + } + else if (self::mcryptSupported()) + { + return @base64_decode(trim( + @mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($sSalt), base64_decode(trim($sPassword)), MCRYPT_MODE_ECB) + )); + } + + return @base64_decode(trim($sPassword)); + } + + /** + * @param array $aParams + * @return boolean + * @UseSession + */ + public static function login($user, $loginName, $password, $isTokenLogin, $config, &$session) + { + $sUser = $user->getUID(); + + $sEmail = $sUser; + $sPassword = $password; + $sEncodedPassword = self::encodePassword($sPassword, md5($sEmail)); + + // Only store the user's password in the current session if they have + // enabled auto-login using Nextcloud username or email address. + if ($config->getAppValue('snappymail', 'snappymail-autologin') || $config->getAppValue('snappymail', 'snappymail-autologin-with-email')) { + $session['snappymail-autologin-password'] = $sEncodedPassword; + } + } + + /** + * @UseSession + */ + public static function logout($appManager, $config, &$session) + { + $session['snappymail-autologin-password'] = ''; + + $sApiPath = $appManager->getAppPath('snappymail') . '/app/index.php'; + + self::regSnappyMailDataFunction(); + + $_ENV['RAINLOOP_INCLUDE_AS_API'] = true; + include $sApiPath; + + \SnappyMail\Api::LogoutCurrentLogginedUser(); + + return true; + } + + public static function regSnappyMailDataFunction() + { + if (!@function_exists('__get_custom_data_full_path')) + { + $_ENV['SNAPPYMAIL_NEXTCLOUD'] = true; + + function __get_custom_data_full_path() + { + $sData = rtrim(trim(OC::$server->getSystemConfig()->getValue('datadirectory', '')), '\\/').'/'; + return @is_dir($sData) ? $sData.'snappymail-storage' : ''; + } + } + } + + public static function mimeContentType($filename) { + + $mime_types = array( + + 'woff' => 'application/font-woff', + + 'txt' => 'text/plain', + 'htm' => 'text/html', + 'html' => 'text/html', + 'php' => 'text/html', + 'css' => 'text/css', + 'js' => 'application/javascript', + 'json' => 'application/json', + 'xml' => 'application/xml', + 'swf' => 'application/x-shockwave-flash', + 'flv' => 'video/x-flv', + + // images + 'png' => 'image/png', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'gif' => 'image/gif', + 'bmp' => 'image/bmp', + 'ico' => 'image/vnd.microsoft.icon', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + + // archives + 'zip' => 'application/zip', + 'rar' => 'application/x-rar-compressed', + 'exe' => 'application/x-msdownload', + 'msi' => 'application/x-msdownload', + 'cab' => 'application/vnd.ms-cab-compressed', + + // audio/video + 'mp3' => 'audio/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + + // adobe + 'pdf' => 'application/pdf', + 'psd' => 'image/vnd.adobe.photoshop', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + + // ms office + 'doc' => 'application/msword', + 'rtf' => 'application/rtf', + 'xls' => 'application/vnd.ms-excel', + 'ppt' => 'application/vnd.ms-powerpoint', + + // open office + 'odt' => 'application/vnd.oasis.opendocument.text', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + ); + + if (0 < strpos($filename, '.')) + { + $ext = strtolower(array_pop(explode('.',$filename))); + if (array_key_exists($ext, $mime_types)) + { + return $mime_types[$ext]; + } + else if (function_exists('finfo_open')) + { + $finfo = finfo_open(FILEINFO_MIME); + $mimetype = finfo_file($finfo, $filename); + finfo_close($finfo); + return $mimetype; + } + } + + return 'application/octet-stream'; + } +} diff --git a/integrations/nextcloud/snappymail/templates/admin-local.php b/integrations/nextcloud/snappymail/templates/admin-local.php new file mode 100755 index 000000000..c7ee3c9a8 --- /dev/null +++ b/integrations/nextcloud/snappymail/templates/admin-local.php @@ -0,0 +1,45 @@ +<?php script('snappymail', 'admin') ?> + +<div class="section"> + <form id="mail-snappymail-admin-form" action="#" method="post"> + <input type="hidden" name="requesttoken" value="<?php echo $_['requesttoken'] ?>" id="requesttoken"> + <input type="hidden" name="appname" value="snappymail"> + + <fieldset class="personalblock"> + <h2><?php echo($l->t('SnappyMail Webmail')); ?></h2> + <br /> + <?php if ($_['snappymail-admin-panel-link']): ?> + <p> + <a href="<?php echo $_['snappymail-admin-panel-link'] ?>" target="_blank" style="text-decoration: underline"> + <?php echo($l->t('Go to SnappyMail Webmail admin panel')); ?> + </a> + </p> + <br /> + <?php endif; ?> + <p> + <div style="display: flex;"> + <input type="radio" id="snappymail-noautologin" name="snappymail-autologin" value="0" <?php if (!$_['snappymail-autologin']&&!$_['snappymail-autologin-with-email']): ?>checked="checked"<?php endif; ?> /> + <label style="margin: auto 5px;" for="snappymail-noautologin"> + <?php echo($l->t('Users will login manually, or define credentials in their personal settings for automatic logins.')); ?> + </label> + </div> + <div style="display: flex;"> + <input type="radio" id="snappymail-autologin" name="snappymail-autologin" value="1" <?php if ($_['snappymail-autologin']): ?>checked="checked"<?php endif; ?> /> + <label style="margin: auto 5px;" for="snappymail-autologin"> + <?php echo($l->t('Attempt to automatically login users with their Nextcloud username and password, or user-defined credentials, if set.')); ?> + </label> + </div> + <div style="display: flex;"> + <input type="radio" id="snappymail-autologin-with-email" name="snappymail-autologin" value="2" <?php if ($_['snappymail-autologin-with-email']): ?>checked="checked"<?php endif; ?> /> + <label style="margin: auto 5px;" for="snappymail-autologin-with-email"> + <?php echo($l->t('Attempt to automatically login users with their Nextcloud email and password, or user-defined credentials, if set.')); ?> + </label> + </div> + <br /> + <br /> + <input type="button" id="snappymail-save-button" name="snappymail-save-button" value="<?php echo($l->t('Save')); ?>" /> +   <span class="snappymail-result-desc"></span> + </p> + </fieldset> + </form> +</div> diff --git a/integrations/nextcloud/snappymail/templates/empty.php b/integrations/nextcloud/snappymail/templates/empty.php new file mode 100755 index 000000000..fa0c45f25 --- /dev/null +++ b/integrations/nextcloud/snappymail/templates/empty.php @@ -0,0 +1 @@ +<!-- empty --> \ No newline at end of file diff --git a/integrations/nextcloud/snappymail/templates/index-empty.php b/integrations/nextcloud/snappymail/templates/index-empty.php new file mode 100755 index 000000000..e95b0cd88 --- /dev/null +++ b/integrations/nextcloud/snappymail/templates/index-empty.php @@ -0,0 +1,3 @@ +<div style="margin: 20px"> + <?php p($l->t('SnappyMail Webmail is not configured yet.')); ?> +</div> diff --git a/integrations/nextcloud/snappymail/templates/index.php b/integrations/nextcloud/snappymail/templates/index.php new file mode 100755 index 000000000..fcdd98db2 --- /dev/null +++ b/integrations/nextcloud/snappymail/templates/index.php @@ -0,0 +1 @@ +<iframe id="rliframe" style="border: none; width: 100%; height: 100%; position: relative;" tabindex="-1" frameborder="0" src="<?php echo $_['snappymail-iframe-url']; ?>"></iframe><?php OCP\Util::addScript('snappymail', 'resize'); diff --git a/integrations/nextcloud/snappymail/templates/personal.php b/integrations/nextcloud/snappymail/templates/personal.php new file mode 100755 index 000000000..ccaf65de2 --- /dev/null +++ b/integrations/nextcloud/snappymail/templates/personal.php @@ -0,0 +1,8 @@ +<?php + +use OCA\SnappyMail\Settings\PersonalSettings; + +$app = new \OCA\SnappyMail\AppInfo\Application(); +$controller = $app->getContainer()->query(PersonalSettings::class); +return $controller->getForm()->render(); + diff --git a/integrations/nextcloud/snappymail/templates/personal_settings.php b/integrations/nextcloud/snappymail/templates/personal_settings.php new file mode 100755 index 000000000..ec5ff5026 --- /dev/null +++ b/integrations/nextcloud/snappymail/templates/personal_settings.php @@ -0,0 +1,32 @@ +<?php script('snappymail', 'personal'); + +$textLink1 = $l->t('may have some'); +$textLink2 = $l->t('security considerations'); +$links = [ + "<a href=\"https://github.com/pierre-alain-b/rainloop-nextcloud/issues/87\" style=\"text-decoration:underline\" target=\"_blank\">{$textLink1}</a>", + "<a href=\"https://github.com/RainLoop/rainloop-webmail/issues/1082\" style=\"text-decoration:underline\" target=\"_blank\">{$textLink2}</a>" + ]; ?> + +<div class="section"> + <form id="mail-snappymail-personal-form" action="#" method="post"> + <input type="hidden" name="requesttoken" value="<?php echo $_['requesttoken'] ?>" id="requesttoken"> + <input type="hidden" name="appname" value="snappymail"> + + <fieldset class="personalblock"> + <h2><?php echo($l->t('SnappyMail Webmail')); ?></h2> + <p> + <?php echo($l->t('Enter an email and password to auto-login to SnappyMail. <b>Please note</b> that this feature %s %s.', $links)); ?> + </p> + <p> + <input type="text" id="snappymail-email" name="snappymail-email" + value="<?php echo $_['snappymail-email']; ?>" placeholder="<?php echo($l->t('Email')); ?>" /> + + <input type="password" id="snappymail-password" name="snappymail-password" + value="<?php echo $_['snappymail-password']; ?>" placeholder="<?php echo($l->t('Password')); ?>" /> + + <input type="button" id="snappymail-save-button" name="snappymail-save-button" value="<?php echo($l->t('Save')); ?>" /> +   <span class="snappymail-result-desc"></span> + </p> + </fieldset> + </form> +</div> diff --git a/plugins/nextcloud/NextCloudSuggestions.php b/plugins/nextcloud/NextCloudSuggestions.php new file mode 100644 index 000000000..a99e9052e --- /dev/null +++ b/plugins/nextcloud/NextCloudSuggestions.php @@ -0,0 +1,107 @@ +<?php + +class OwnCloudSuggestions implements \RainLoop\Providers\Suggestions\ISuggestions +{ + /** + * @var \MailSo\Log\Logger + */ + protected $oLogger; + + public function Process(\RainLoop\Model\Account $oAccount, string $sQuery, int $iLimit = 20): array + { + $iInputLimit = $iLimit; + $aResult = array(); + $sQuery = \trim($sQuery); + + try + { + if ('' === $sQuery || !$oAccount || !OwnCloudPlugin::IsOwnCloudLoggedIn()) + { + return $aResult; + } + + $aParams = array('FN', 'NICKNAME', 'TITLE', 'EMAIL'); + if (\class_exists('OC') && isset(\OC::$server) && \method_exists(\OC::$server, 'getContactsManager')) + { + $cm = \OC::$server->getContactsManager(); + if (!$cm && !$cm->isEnabled()) + { + return $aResult; + } + + $aSearchResult = $cm->search($sQuery, $aParams); + } + else if (\class_exists('OCP\Contacts') && \OCP\Contacts::isEnabled()) + { + $aSearchResult = \OCP\Contacts::search($sQuery, $aParams); + } + else + { + return $aResult; + } + + //$this->oLogger->WriteDump($aSearchResult); + + $aHashes = array(); + if (\is_array($aSearchResult) && 0 < \count($aSearchResult)) + { + foreach ($aSearchResult as $aContact) + { + if (0 >= $iLimit) + { + break; + } + + $sUid = empty($aContact['UID']) ? '' : $aContact['UID']; + if (!empty($sUid)) + { + $mEmails = isset($aContact['EMAIL']) ? $aContact['EMAIL'] : ''; + + $sFullName = isset($aContact['FN']) ? \trim($aContact['FN']) : ''; + if (empty($sFullName)) + { + $sFullName = isset($aContact['NICKNAME']) ? \trim($aContact['NICKNAME']) : ''; + } + + if (!\is_array($mEmails)) + { + $mEmails = array($mEmails); + } + + foreach ($mEmails as $sEmail) + { + $sHash = '"'.$sFullName.'" <'.$sEmail.'>'; + if (!isset($aHashes[$sHash])) + { + $aHashes[$sHash] = true; + $aResult[] = array($sEmail, $sFullName); + $iLimit--; + } + } + } + } + + $aResult = \array_slice($aResult, 0, $iInputLimit); + } + + unset($aSearchResult, $aHashes); + } + catch (\Throwable $oException) + { + if ($this->oLogger) + { + $this->oLogger->WriteException($oException); + } + } + + return $aResult; + } + + /** + * @param \MailSo\Log\Logger $oLogger + */ + public function SetLogger($oLogger) + { + $this->oLogger = $oLogger instanceof \MailSo\Log\Logger ? $oLogger : null; + } +} diff --git a/plugins/nextcloud/index.php b/plugins/nextcloud/index.php new file mode 100644 index 000000000..099d239a3 --- /dev/null +++ b/plugins/nextcloud/index.php @@ -0,0 +1,180 @@ +<?php + +class NextCloudPlugin extends \RainLoop\Plugins\AbstractPlugin +{ + const + NAME = 'NextCloud', + VERSION = '2.0', + CATEGORY = 'Integrations', + DESCRIPTION = 'Plugin that adds functionality to integrate with NextCloud.'; + + private static function IsNextCloud() : bool + { + return !empty($_ENV['SNAPPYMAIL_NEXTCLOUD']) && \class_exists('OC'); + } + + private static function IsNextCloudLoggedIn() : bool + { + return static::IsNextCloud() && \class_exists('OCP\User') && \OCP\User::isLoggedIn(); + } + + public function Init() : void + { + if (static::IsNextCloud()) { + $this->addHook('main.fabrica', 'MainFabrica'); + $this->addHook('filter.app-data', 'FilterAppData'); + $this->addHook('json.attachments', 'DoAttachmentsActions'); + + $sAppPath = ''; + if (\class_exists('OC_App')) { + $sAppPath = \rtrim(\trim(\OC_App::getAppWebPath('snappymail')), '\\/').'/app/'; + } + if (!$sAppPath) { + $sUrl = \MailSo\Base\Http::SingletonInstance()->GetUrl(); + if ($sUrl && \preg_match('/\/index\.php\/apps\/snappymail/', $sUrl)) { + $sAppPath = \preg_replace('/\/index\.php\/apps\/snappymail.+$/', + '/apps/snappymail/app/', $sUrl); + } + } + $_SERVER['SCRIPT_NAME'] = $sAppPath; + } + } + + public function Supported() : string + { + if (!static::IsNextCloud()) { + return 'NextCloud not found to use this plugin'; + } + return ''; + } + + // DoAttachmentsActions + public function DoAttachmentsActions(\SnappyMail\AttachmentsAction $data) + { + if ('nextcloud' === $data->action) { + if (static::IsNextCloudLoggedIn() && \class_exists('OCP\Files')) { + $oFiles = \OCP\Files::getStorage('files'); + if ($oFiles && $data->filesProvider->IsActive() && \method_exists($oFiles, 'file_put_contents')) { + $sSaveFolder = $this->Config()->Get('plugin', 'save_folder', '') ?: 'Attachments'; + $oFiles->is_dir($sSaveFolder) || $oFiles->mkdir($sSaveFolder); + $data->result = true; + foreach ($data->items as $aItem) { + $sSavedFileName = isset($aItem['FileName']) ? $aItem['FileName'] : 'file.dat'; + $sSavedFileHash = !empty($aItem['FileHash']) ? $aItem['FileHash'] : ''; + if (!empty($sSavedFileHash)) { + $fFile = $data->filesProvider->GetFile($data->account, $sSavedFileHash, 'rb'); + if (\is_resource($fFile)) { + $sSavedFileNameFull = \MailSo\Base\Utils::SmartFileExists($sSaveFolder.'/'.$sSavedFileName, function ($sPath) use ($oFiles) { + return $oFiles->file_exists($sPath); + }); + + if (!$oFiles->file_put_contents($sSavedFileNameFull, $fFile)) { + $data->result = false; + } + + if (\is_resource($fFile)) { + \fclose($fFile); + } + } + } + } + } + } + + foreach ($data->items as $aItem) { + $sFileHash = (string) (isset($aItem['FileHash']) ? $aItem['FileHash'] : ''); + if (!empty($sFileHash)) { + $data->filesProvider->Clear($data->account, $sFileHash); + } + } + } + } + + /** + * TODO: create pre-login auth hook + */ + public function ServiceNextCloudAuth() + { +/* + $this->oHttp->ServerNoCache(); + + if (!static::IsNextCloud() || + !isset($_ENV['___snappymail_nextcloud_email']) || + !isset($_ENV['___snappymail_nextcloud_password']) || + empty($_ENV['___snappymail_nextcloud_email']) + ) + { + $this->oActions->SetAuthLogoutToken(); + $this->oActions->Location('./'); + return ''; + } + + $bLogout = true; + + $sEmail = $_ENV['___snappymail_nextcloud_email']; + $sPassword = $_ENV['___snappymail_nextcloud_password']; + + try + { + $oAccount = $this->oActions->LoginProcess($sEmail, $sPassword); + $this->oActions->AuthToken($oAccount); + + $bLogout = !($oAccount instanceof \snappymail\Model\Account); + } + catch (\Exception $oException) + { + $this->oActions->Logger()->WriteException($oException); + } + + if ($bLogout) + { + $this->oActions->SetAuthLogoutToken(); + } + + $this->oActions->Location('./'); + return ''; +*/ + } + + /** + * @return void + */ + public function FilterAppData($bAdmin, &$aResult) + { + if (!$bAdmin && \is_array($aResult) && static::IsNextCloud()) { + $key = \array_search(\RainLoop\Enumerations\Capa::AUTOLOGOUT, $aResult['Capa']); + if (false !== $key) { + unset($aResult['Capa'][$key]); + } + if (static::IsNextCloudLoggedIn() && \class_exists('OCP\Files')) { + $aResult['System']['attachmentsActions'][] = 'nextcloud'; + } + } + } + + /** + * @param string $sName + * @param mixed $mResult + */ + public function MainFabrica($sName, &$mResult) + { + if ('suggestions' === $sName && static::IsNextCloud() && $this->Config()->Get('plugin', 'suggestions', true)) { + include_once __DIR__.'/NextCloudSuggestions.php'; + if (!\is_array($mResult)) { + $mResult = array(); + } + $mResult[] = new NextCloudSuggestions(); + } + } + + protected function configMapping() : array + { + return array( + \RainLoop\Plugins\Property::NewInstance('save_folder')->SetLabel('Save Folder') + ->SetDefaultValue('Attachments'), + \RainLoop\Plugins\Property::NewInstance('suggestions')->SetLabel('Suggestions') + ->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL) + ->SetDefaultValue(true) + ); + } +} diff --git a/plugins/owncloud/index.php b/plugins/owncloud/index.php index 7fb6a2fcd..f50e99e43 100644 --- a/plugins/owncloud/index.php +++ b/plugins/owncloud/index.php @@ -5,7 +5,7 @@ class OwnCloudPlugin extends \RainLoop\Plugins\AbstractPlugin const NAME = 'OwnCloud', VERSION = '2.0', - CATEGORY = 'Security', + CATEGORY = 'Integrations', DESCRIPTION = 'Plugin that adds functionality to integrate with OwnCloud.'; private static function IsOwnCloud() : bool From b0d7bab1b028e1a9c1bad77e2b7775294a693feb Mon Sep 17 00:00:00 2001 From: djmaze <djmaze@djmaze.lan> Date: Sat, 17 Jul 2021 08:53:50 +0200 Subject: [PATCH 12/26] Move the NextCloud plugin to final destination --- .../_data_/_default_/plugins}/nextcloud/NextCloudSuggestions.php | 0 .../app/data/_data_/_default_/plugins}/nextcloud/index.php | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {plugins => integrations/nextcloud/snappymail/app/data/_data_/_default_/plugins}/nextcloud/NextCloudSuggestions.php (100%) rename {plugins => integrations/nextcloud/snappymail/app/data/_data_/_default_/plugins}/nextcloud/index.php (100%) diff --git a/plugins/nextcloud/NextCloudSuggestions.php b/integrations/nextcloud/snappymail/app/data/_data_/_default_/plugins/nextcloud/NextCloudSuggestions.php similarity index 100% rename from plugins/nextcloud/NextCloudSuggestions.php rename to integrations/nextcloud/snappymail/app/data/_data_/_default_/plugins/nextcloud/NextCloudSuggestions.php diff --git a/plugins/nextcloud/index.php b/integrations/nextcloud/snappymail/app/data/_data_/_default_/plugins/nextcloud/index.php similarity index 100% rename from plugins/nextcloud/index.php rename to integrations/nextcloud/snappymail/app/data/_data_/_default_/plugins/nextcloud/index.php From b8d594eb45373e36e5fbb51daf0c154560b7d6ba Mon Sep 17 00:00:00 2001 From: djmaze <djmaze@djmaze.lan> Date: Sat, 17 Jul 2021 08:56:09 +0200 Subject: [PATCH 13/26] Move OwnCloud plugin to final destination --- .../_data_/_default_/plugins}/owncloud/OwnCloudSuggestions.php | 0 .../app/data/_data_/_default_/plugins}/owncloud/index.php | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {plugins => integrations/owncloud/snappymail/app/data/_data_/_default_/plugins}/owncloud/OwnCloudSuggestions.php (100%) rename {plugins => integrations/owncloud/snappymail/app/data/_data_/_default_/plugins}/owncloud/index.php (100%) diff --git a/plugins/owncloud/OwnCloudSuggestions.php b/integrations/owncloud/snappymail/app/data/_data_/_default_/plugins/owncloud/OwnCloudSuggestions.php similarity index 100% rename from plugins/owncloud/OwnCloudSuggestions.php rename to integrations/owncloud/snappymail/app/data/_data_/_default_/plugins/owncloud/OwnCloudSuggestions.php diff --git a/plugins/owncloud/index.php b/integrations/owncloud/snappymail/app/data/_data_/_default_/plugins/owncloud/index.php similarity index 100% rename from plugins/owncloud/index.php rename to integrations/owncloud/snappymail/app/data/_data_/_default_/plugins/owncloud/index.php From bb15cd1ec3ba1a2adfa1044398779f726b5a93c8 Mon Sep 17 00:00:00 2001 From: djmaze <djmaze@djmaze.lan> Date: Mon, 19 Jul 2021 11:57:47 +0200 Subject: [PATCH 14/26] Resolve Issue #100 --- dev/Common/Selector.js | 51 ++++++++++++++------- dev/Model/Message.js | 7 --- dev/Stores/User/Message.js | 67 ++++++++++++++-------------- dev/View/Popup/Contacts.js | 6 +-- dev/View/User/MailBox/MessageList.js | 18 ++++---- 5 files changed, 81 insertions(+), 68 deletions(-) diff --git a/dev/Common/Selector.js b/dev/Common/Selector.js index 109b30135..bd01d203a 100644 --- a/dev/Common/Selector.js +++ b/dev/Common/Selector.js @@ -1,6 +1,15 @@ import ko from 'ko'; import { isArray } from 'Common/Utils'; +/* + oCallbacks: + ItemSelect + MiddleClick + AutoSelect + ItemGetUid + UpOrDown +*/ + export class Selector { /** * @param {koProperty} koList @@ -209,11 +218,9 @@ export class Selector { itemSelected(item) { if (this.isListChecked()) { - if (!item) { - (this.oCallbacks.onItemSelect || (()=>{}))(item || null); - } + item || (this.oCallbacks.ItemSelect || (()=>{}))(null); } else if (item) { - (this.oCallbacks.onItemSelect || (()=>{}))(item); + (this.oCallbacks.ItemSelect || (()=>{}))(item); } } @@ -226,20 +233,32 @@ export class Selector { this.oContentScrollable = contentScrollable; if (contentScrollable) { + let getItem = selector => { + let el = event.target.closestWithin(selector, contentScrollable); + return el ? ko.dataFor(el) : null; + }; + contentScrollable.addEventListener('click', event => { let el = event.target.closestWithin(this.sItemSelector, contentScrollable); el && this.actionClick(ko.dataFor(el), event); - el = event.target.closestWithin(this.sItemCheckedSelector, contentScrollable); - if (el) { - const item = ko.dataFor(el); + const item = getItem(this.sItemCheckedSelector); + if (item) { + if (event.shiftKey) { + this.actionClick(item, event); + } else { + this.focusedItem(item); + item.checked(!item.checked()); + } + } + }); + + contentScrollable.addEventListener('auxclick', event => { + if (1 == event.button) { + const item = getItem(this.sItemSelector); if (item) { - if (event.shiftKey) { - this.actionClick(item, event); - } else { - this.focusedItem(item); - item.checked(!item.checked()); - } + this.focusedItem(item); + (this.oCallbacks.MiddleClick || (()=>{}))(item); } } }); @@ -271,7 +290,7 @@ export class Selector { * @returns {boolean} */ autoSelect() { - return !!(this.oCallbacks.onAutoSelect || (()=>true))(); + return !!(this.oCallbacks.AutoSelect || (()=>true))(); } /** @@ -281,7 +300,7 @@ export class Selector { getItemUid(item) { let uid = ''; - const getItemUidCallback = this.oCallbacks.onItemGetUid || null; + const getItemUidCallback = this.oCallbacks.ItemGetUid || null; if (getItemUidCallback && item) { uid = getItemUidCallback(item); } @@ -312,7 +331,7 @@ export class Selector { } else if (++i < listLen) { result = list[i]; } - result || (this.oCallbacks.onUpUpOrDownDown || (()=>true))('ArrowUp' === sEventKey); + result || (this.oCallbacks.UpOrDown || (()=>true))('ArrowUp' === sEventKey); } else if ('Home' === sEventKey) { result = list[0]; } else if ('End' === sEventKey) { diff --git a/dev/Model/Message.js b/dev/Model/Message.js index a5b4b0dd1..9e31a2515 100644 --- a/dev/Model/Message.js +++ b/dev/Model/Message.js @@ -524,13 +524,6 @@ export class MessageModel extends AbstractModel { } } - storeDataInDom() { - if (this.body) { - this.body.rlIsHtml = !!this.isHtml(); - this.body.rlHasImages = !!this.hasImages(); - } - } - fetchDataFromDom() { if (this.body) { this.isHtml(!!this.body.rlIsHtml); diff --git a/dev/Stores/User/Message.js b/dev/Stores/User/Message.js index 1a570831e..4ce1bf0eb 100644 --- a/dev/Stores/User/Message.js +++ b/dev/Stores/User/Message.js @@ -220,7 +220,7 @@ export const MessageUserStore = new class { } }); - this.purgeMessageBodyCacheThrottle = this.purgeMessageBodyCache.throttle(30000); + this.purgeMessageBodyCache = this.purgeMessageBodyCache.throttle(30000); } purgeMessageBodyCache() { @@ -428,16 +428,12 @@ export const MessageUserStore = new class { ); } - setMessage(data, cached) { + setMessage(data, cached, oMessage) { let isNew = false, - body = null, json = data && data.Result, - id = '', - plain = '', - resultHtml = '', messagesDom = this.messagesBodiesDom(), selectedMessage = this.selectorMessageSelected(), - message = this.message(); + message = oMessage || this.message(); if ( json && @@ -447,7 +443,7 @@ export const MessageUserStore = new class { ) { const threads = message.threads(); if (message.uid !== json.Uid && 1 < threads.length && threads.includes(json.Uid)) { - message = MessageModel.reviveFromJson(json); + message = oMessage ? null : MessageModel.reviveFromJson(json); if (message) { message.threads(threads); MessageFlagsCache.initMessage(message); @@ -460,7 +456,7 @@ export const MessageUserStore = new class { } if (message && message.uid === json.Uid) { - this.messageError(''); + oMessage || this.messageError(''); if (cached) { delete json.IsSeen; @@ -475,7 +471,8 @@ export const MessageUserStore = new class { addRequestedMessage(message.folder, message.uid); if (messagesDom) { - id = 'rl-mgs-' + message.hash.replace(/[^a-zA-Z0-9]/g, ''); + let body = null, + id = 'rl-mgs-' + message.hash.replace(/[^a-zA-Z0-9]/g, ''); const textBody = elementById(id); if (textBody) { @@ -483,7 +480,9 @@ export const MessageUserStore = new class { message.fetchDataFromDom(); messagesDom.append(textBody); } else { - let isHtml = !!json.Html; + let isHtml = !!json.Html, + plain = '', + resultHtml = '<pre></pre>'; if (isHtml) { resultHtml = json.Html.toString(); if (SettingsUserStore.removeColors()) { @@ -508,8 +507,6 @@ export const MessageUserStore = new class { } else { resultHtml = '<pre>' + resultHtml + '</pre>'; } - } else { - resultHtml = '<pre>' + resultHtml + '</pre>'; } body = Element.fromHTML('<div id="' + id + '" hidden="" class="b-text-part ' @@ -517,22 +514,25 @@ export const MessageUserStore = new class { + findEmailAndLinks(resultHtml) + '</div>'); - // Drop Microsoft Office style properties - const rgbRE = /rgb\((\d+),\s*(\d+),\s*(\d+)\)/g, - hex = n => ('0' + parseInt(n).toString(16)).slice(-2); - body.querySelectorAll('[style*=mso]').forEach(el => - el.setAttribute('style', el.style.cssText.replace(rgbRE, (m,r,g,b) => '#' + hex(r) + hex(g) + hex(b))) - ); + if (isHtml) { + // Drop Microsoft Office style properties + const rgbRE = /rgb\((\d+),\s*(\d+),\s*(\d+)\)/g, + hex = n => ('0' + parseInt(n).toString(16)).slice(-2); + body.querySelectorAll('[style*=mso]').forEach(el => + el.setAttribute('style', el.style.cssText.replace(rgbRE, (m,r,g,b) => '#' + hex(r) + hex(g) + hex(b))) + ); + } + + body.rlIsHtml = isHtml; + body.rlHasImages = !!json.HasExternals; message.body = body; message.isHtml(isHtml); - message.hasImages(!!json.HasExternals); + message.hasImages(body.rlHasImages); messagesDom.append(body); - message.storeDataInDom(); - if (json.HasInternals) { message.showInternalImages(); } @@ -541,12 +541,12 @@ export const MessageUserStore = new class { message.showExternalImages(); } - this.purgeMessageBodyCacheThrottle(); + this.purgeMessageBodyCache(); } - this.messageActiveDom(message.body); + oMessage || this.messageActiveDom(message.body); - this.hideMessageBodies(); + oMessage || this.hideMessageBodies(); if (body) { this.initOpenPgpControls(body, message); @@ -554,7 +554,8 @@ export const MessageUserStore = new class { this.initBlockquoteSwitcher(body); } - message.body.hidden = false; + oMessage || (message.body.hidden = false); + oMessage && message.viewPopupMessage(); } MessageFlagsCache.initMessage(message); @@ -563,8 +564,6 @@ export const MessageUserStore = new class { } if (isNew) { - message = this.message(); - if ( selectedMessage && message && @@ -613,20 +612,20 @@ export const MessageUserStore = new class { } } - populateMessageBody(oMessage) { + populateMessageBody(oMessage, preload) { if (oMessage) { - this.hideMessageBodies(); - this.messageLoading(true); + preload || this.hideMessageBodies(); + preload || this.messageLoading(true); Remote.message((iError, oData, bCached) => { if (iError) { - if (Notification.RequestAborted !== iError) { + if (Notification.RequestAborted !== iError && !preload) { this.message(null); this.messageError(getNotification(iError)); } } else { - this.setMessage(oData, bCached); + this.setMessage(oData, bCached, preload ? oMessage : null); } - this.messageLoading(false); + preload || this.messageLoading(false); }, oMessage.folder, oMessage.uid); } } diff --git a/dev/View/Popup/Contacts.js b/dev/View/Popup/Contacts.js index 36187c356..e52d6cb8f 100644 --- a/dev/View/Popup/Contacts.js +++ b/dev/View/Popup/Contacts.js @@ -79,14 +79,14 @@ class ContactsPopupView extends AbstractViewPopup { '.e-contact-item.focused' ); - this.selector.on('onItemSelect', contact => { - this.populateViewContact(contact || null); + this.selector.on('ItemSelect', contact => { + this.populateViewContact(contact); if (!contact) { this.emptySelection(true); } }); - this.selector.on('onItemGetUid', contact => contact ? contact.generateUid() : ''); + this.selector.on('ItemGetUid', contact => contact ? contact.generateUid() : ''); this.bDropPageAfterDelete = false; diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index 27459d8bb..787b57c65 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -55,7 +55,7 @@ export class MessageListMailBoxUserView extends AbstractViewRight { this.bPrefetch = false; this.emptySubjectValue = ''; - this.iGoToUpUpOrDownDownTimeout = 0; + this.iGoToUpOrDownTimeout = 0; this.newMoveToFolder = !!SettingsGet('NewMoveToFolder'); @@ -191,13 +191,15 @@ export class MessageListMailBoxUserView extends AbstractViewRight { '.messageListItem.focused' ); - this.selector.on('onItemSelect', message => MessageUserStore.selectMessage(message)); + this.selector.on('ItemSelect', message => MessageUserStore.selectMessage(message)); - this.selector.on('onItemGetUid', message => (message ? message.generateUid() : '')); + this.selector.on('MiddleClick', message => MessageUserStore.populateMessageBody(message, true)); - this.selector.on('onAutoSelect', () => this.useAutoSelect()); + this.selector.on('ItemGetUid', message => (message ? message.generateUid() : '')); - this.selector.on('onUpUpOrDownDown', v => this.goToUpUpOrDownDown(v)); + this.selector.on('AutoSelect', () => this.useAutoSelect()); + + this.selector.on('UpOrDown', v => this.goToUpOrDown(v)); addEventListener('mailbox.message-list.selector.go-down', e => this.selector.newSelectPosition('ArrowDown', false, e.detail) @@ -351,13 +353,13 @@ export class MessageListMailBoxUserView extends AbstractViewRight { showMessageComposer(); } - goToUpUpOrDownDown(up) { + goToUpOrDown(up) { if (MessageUserStore.listChecked().length) { return false; } - clearTimeout(this.iGoToUpUpOrDownDownTimeout); - this.iGoToUpUpOrDownDownTimeout = setTimeout(() => { + clearTimeout(this.iGoToUpOrDownTimeout); + this.iGoToUpOrDownTimeout = setTimeout(() => { let prev, next, temp, current; this.messageListPaginator().find(item => { From 9657578de5bfbf66984251c04ad4761b63f05d3d Mon Sep 17 00:00:00 2001 From: djmaze <djmaze@djmaze.lan> Date: Mon, 19 Jul 2021 12:37:56 +0200 Subject: [PATCH 15/26] Update NextCloud code --- .../nextcloud/snappymail/appinfo/info.xml | 1 - .../nextcloud/snappymail/img/favicon.png | Bin 832 -> 992 bytes .../nextcloud/snappymail/img/logo-64x64.png | Bin 832 -> 992 bytes .../nextcloud/snappymail/img/rainloop.svg | 265 ------------------ .../snappymail/lib/AppInfo/Application.php | 2 +- .../snappymail/screenshots/help_a1.png | Bin 0 -> 14292 bytes .../snappymail/screenshots/help_a2.png | Bin 0 -> 23460 bytes .../snappymail/screenshots/help_a3.png | Bin 0 -> 13001 bytes .../snappymail/screenshots/help_a4.png | Bin 0 -> 57638 bytes 9 files changed, 1 insertion(+), 267 deletions(-) mode change 100755 => 100644 integrations/nextcloud/snappymail/img/logo-64x64.png delete mode 100644 integrations/nextcloud/snappymail/img/rainloop.svg create mode 100644 integrations/nextcloud/snappymail/screenshots/help_a1.png create mode 100644 integrations/nextcloud/snappymail/screenshots/help_a2.png create mode 100644 integrations/nextcloud/snappymail/screenshots/help_a3.png create mode 100644 integrations/nextcloud/snappymail/screenshots/help_a4.png diff --git a/integrations/nextcloud/snappymail/appinfo/info.xml b/integrations/nextcloud/snappymail/appinfo/info.xml index 8823048cb..2a4072d16 100644 --- a/integrations/nextcloud/snappymail/appinfo/info.xml +++ b/integrations/nextcloud/snappymail/appinfo/info.xml @@ -18,7 +18,6 @@ <screenshot>https://raw.githubusercontent.com/pierre-alain-b/snappymail-nextcloud/master/screenshots/2016.10.20-screenshot.jpg</screenshot> <website>https://github.com/pierre-alain-b/snappymail-nextcloud</website> <bugs>https://github.com/pierre-alain-b/snappymail-nextcloud/issues</bugs> - <screenshot>https://raw.githubusercontent.com/pierre-alain-b/snappymail-nextcloud/master/screenshots/2016.10.20-screenshot.jpg</screenshot> <dependencies> <php min-version="7.4" /> <owncloud min-version="6" max-version="9.2" /> diff --git a/integrations/nextcloud/snappymail/img/favicon.png b/integrations/nextcloud/snappymail/img/favicon.png index ce1dd1a886c906ec36cee1dec72b9df605886f02..78ab30b38cad48bf4ef39d67c2b624a5b608b172 100644 GIT binary patch delta 971 zcmV;+12p`=2H*#fBYy*@Nkl<Zc-rln%Wq9V9LGQRp&n@)T163Biv|n&@<=S)Ra+J; zNi11N#NS{g7S=2z?8MGOV#7j0q7rUz#74XyQEh37lD25C#r+*UJvryxnRBN1(L3K{ z7I*HM-~7JcdH!YwEG#T)841Pj>VULQ{$JpCnK?89D}3^&fPYC76>JBd13x|K6L7Es z8gUOW3jFb;VW7(d`PV?+lfDB371WdW17Cgo9El@BwUA#F5vq;6Q4y+@yb%$qoqQ}2 zg2?{>4g)EJDG{(Fo2=siFi}i|gp2%bV4II)-$WQwm;+7$&pi2^z)izFp#`Y-$sYv{ z0lBc2U#v7MU4PVzeIib&{1^eclEAfqmS5^v4_rwCXJyw^Yj_8oNXvfy2yhG7==0$u zFlKlTnDNQ~1!Ohi6BV=rO#$TlogdDu*dzOU?^qcJGQcu}DG?H!nM(cAcwRnlfezQ) zw_s@q65)$1E7gKNMe=!Ef8LeC)h<FY^2m~(_X=>&Wq+gx`0Qh3tinZ5A&+nk6~GZ; zxQawjC9f<%*l?&c5p>9F3E+tE0hsork8&DQZe%3z@%r~Ffcydh(h3!1fnC5VpL4+} z2|Q-YL_P&P0=D>^RnDsVRjda%SPcxyA*ee&2Aa!2zFF$_`ZFlLhCyoxcq`5NjJ0fu zMTqN%)_)LiV_YVjJ3zN9zgt@4eTf9vD%)H)hQzmVU1K^upjZo(E9v&Y|2-fXu?3u- zFjN7nK;)JONXDqR5=4ka9{jJnqKI(Da2TsPbT7prA7>7zcEp|omJF-_Rsbu2654^C z6dp>rSCAddAF0>NH{OHQR>^NDvKmFP6~`~-RevA>Jd{Agz5U!Ic~9h<0g1k@ddgpx z{G%ZPM6ngeFYQ<$C!z1OD{lsP!2Gq@1aKdC;VJu4^265asJ7zxW%mJL`+$?c4WL)U zXs47p9l@a{;5u-j$d1yuUqJ%&0>^>P8WpulnY|GNSP2}%bz4(Pzx;+khn%&o)ojHK zg?~#2T`Z^Y;;g60cc%O*dLoo0_#uNE&SL}<P_Y%oFX!P!QfCf0FQ+?wA(d<Z-cYz@ zi5m&3+KS>=M#@~&nFAVtb<#D%RBi#*7;1eLTT%R~lQNAub3ls1EpFIOF(Kci4G5rW zD~ex9`JK|)2&tT@=P5i%HFdb5Vk?SYb3~GCFk}U=0$2fj<8TLUz_}XX-R530^0ht$ tay869myjLP?iv-~DHawM78Vw@i+@H#{u)yjc^?1(002ovPDHLkV1nqhyD9(x delta 810 zcmV+_1J(TC2fzl9BYyxHbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU! z;z>k7RCwC#n$brDK^(`2<+!Jah`6UCB6^C5h=@4)4|<B|H6nV7IF2KZh$D`Oi0G-O zh=?O1A|fIp;)u9@qZ!uTZ)ShDJKN6g&X?cIwf%0spV`~}&3|u(F()S{CnqPTB5N6< z3(+UUB}6$Sp9j&_<T!;Wh2$uS>r^=o3BJl=ys8|x5cB-PSEBiI!oa!tjwVN2ToY#g zT$AH~X#R@eiBCMqe1|yjq-U*8$T0$OL-h5c0?^Fa&s=q9zUc0NX3ou{08VoT0W*KB z15gpB^8f)ezkdxe9R`5q0D{Uq!vGv30J020XC4K>|KS7ykVOb#=DEfpKKuaWJNyg* zkR=Ed^HQ@ApMC%mf~w4IMySSYH7_*>@i@$v*a{(vdH?f(Wf?*o^O0o>gjCGi0wGoN zX+!V=wM%UCr3^tZZk`bv9X3En#60t@0zHc%m^42Lv40c<K<)aZ0m1al^LKv_Vj&2i zuPBAngpkB-FBd~!q8c;`0E5u7>^JXU4L&6Sfa@+M1kC(}p|Oz~B5~AE^xs-B%*)tH z0zlIsfS~#AkPtBQUBeIt0B{`{0MJnM4eD%gf51}|WF5q;r}#Xo#vtB;03`W}Ve?D@ zffrvBzJI@0A+9whO+(;!!F#YF;zg_o!^}%<Gkk06A@JD`#0GHz6bHf##J%YUURb+q z4dR=rH)D8#QX6s=7zaZ2Kh67ZKWsPpMwF>hmcl094*z1_Z=xoi4f_x)S#E};)g`u~ z9hP>B+z5nnM$H?8a7BP%`{qb22=eStDWm3xg?}&wv5GZBu+~@}_Xej$EwU2U5Xt-Q za8I_zihMrc83R!06C#B_BUJcPV(y%vCZ356lQprX$8zws|2QGXre++x;W|plvBQWb zMJZ>69|Ij6mDZX5T<#bl$Cf4s?;4E|a_kW&mBVC<?C<1sKfzhMCC=KeCI_Wt$6nz& om?Lw{$;rvd$;rv7sQMLP03NQ=SuW`Ovj6}907*qoM6N<$f|9CqmjD0& diff --git a/integrations/nextcloud/snappymail/img/logo-64x64.png b/integrations/nextcloud/snappymail/img/logo-64x64.png old mode 100755 new mode 100644 index ce1dd1a886c906ec36cee1dec72b9df605886f02..78ab30b38cad48bf4ef39d67c2b624a5b608b172 GIT binary patch delta 971 zcmV;+12p`=2H*#fBYy*@Nkl<Zc-rln%Wq9V9LGQRp&n@)T163Biv|n&@<=S)Ra+J; zNi11N#NS{g7S=2z?8MGOV#7j0q7rUz#74XyQEh37lD25C#r+*UJvryxnRBN1(L3K{ z7I*HM-~7JcdH!YwEG#T)841Pj>VULQ{$JpCnK?89D}3^&fPYC76>JBd13x|K6L7Es z8gUOW3jFb;VW7(d`PV?+lfDB371WdW17Cgo9El@BwUA#F5vq;6Q4y+@yb%$qoqQ}2 zg2?{>4g)EJDG{(Fo2=siFi}i|gp2%bV4II)-$WQwm;+7$&pi2^z)izFp#`Y-$sYv{ z0lBc2U#v7MU4PVzeIib&{1^eclEAfqmS5^v4_rwCXJyw^Yj_8oNXvfy2yhG7==0$u zFlKlTnDNQ~1!Ohi6BV=rO#$TlogdDu*dzOU?^qcJGQcu}DG?H!nM(cAcwRnlfezQ) zw_s@q65)$1E7gKNMe=!Ef8LeC)h<FY^2m~(_X=>&Wq+gx`0Qh3tinZ5A&+nk6~GZ; zxQawjC9f<%*l?&c5p>9F3E+tE0hsork8&DQZe%3z@%r~Ffcydh(h3!1fnC5VpL4+} z2|Q-YL_P&P0=D>^RnDsVRjda%SPcxyA*ee&2Aa!2zFF$_`ZFlLhCyoxcq`5NjJ0fu zMTqN%)_)LiV_YVjJ3zN9zgt@4eTf9vD%)H)hQzmVU1K^upjZo(E9v&Y|2-fXu?3u- zFjN7nK;)JONXDqR5=4ka9{jJnqKI(Da2TsPbT7prA7>7zcEp|omJF-_Rsbu2654^C z6dp>rSCAddAF0>NH{OHQR>^NDvKmFP6~`~-RevA>Jd{Agz5U!Ic~9h<0g1k@ddgpx z{G%ZPM6ngeFYQ<$C!z1OD{lsP!2Gq@1aKdC;VJu4^265asJ7zxW%mJL`+$?c4WL)U zXs47p9l@a{;5u-j$d1yuUqJ%&0>^>P8WpulnY|GNSP2}%bz4(Pzx;+khn%&o)ojHK zg?~#2T`Z^Y;;g60cc%O*dLoo0_#uNE&SL}<P_Y%oFX!P!QfCf0FQ+?wA(d<Z-cYz@ zi5m&3+KS>=M#@~&nFAVtb<#D%RBi#*7;1eLTT%R~lQNAub3ls1EpFIOF(Kci4G5rW zD~ex9`JK|)2&tT@=P5i%HFdb5Vk?SYb3~GCFk}U=0$2fj<8TLUz_}XX-R530^0ht$ tay869myjLP?iv-~DHawM78Vw@i+@H#{u)yjc^?1(002ovPDHLkV1nqhyD9(x delta 810 zcmV+_1J(TC2fzl9BYyxHbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU! z;z>k7RCwC#n$brDK^(`2<+!Jah`6UCB6^C5h=@4)4|<B|H6nV7IF2KZh$D`Oi0G-O zh=?O1A|fIp;)u9@qZ!uTZ)ShDJKN6g&X?cIwf%0spV`~}&3|u(F()S{CnqPTB5N6< z3(+UUB}6$Sp9j&_<T!;Wh2$uS>r^=o3BJl=ys8|x5cB-PSEBiI!oa!tjwVN2ToY#g zT$AH~X#R@eiBCMqe1|yjq-U*8$T0$OL-h5c0?^Fa&s=q9zUc0NX3ou{08VoT0W*KB z15gpB^8f)ezkdxe9R`5q0D{Uq!vGv30J020XC4K>|KS7ykVOb#=DEfpKKuaWJNyg* zkR=Ed^HQ@ApMC%mf~w4IMySSYH7_*>@i@$v*a{(vdH?f(Wf?*o^O0o>gjCGi0wGoN zX+!V=wM%UCr3^tZZk`bv9X3En#60t@0zHc%m^42Lv40c<K<)aZ0m1al^LKv_Vj&2i zuPBAngpkB-FBd~!q8c;`0E5u7>^JXU4L&6Sfa@+M1kC(}p|Oz~B5~AE^xs-B%*)tH z0zlIsfS~#AkPtBQUBeIt0B{`{0MJnM4eD%gf51}|WF5q;r}#Xo#vtB;03`W}Ve?D@ zffrvBzJI@0A+9whO+(;!!F#YF;zg_o!^}%<Gkk06A@JD`#0GHz6bHf##J%YUURb+q z4dR=rH)D8#QX6s=7zaZ2Kh67ZKWsPpMwF>hmcl094*z1_Z=xoi4f_x)S#E};)g`u~ z9hP>B+z5nnM$H?8a7BP%`{qb22=eStDWm3xg?}&wv5GZBu+~@}_Xej$EwU2U5Xt-Q za8I_zihMrc83R!06C#B_BUJcPV(y%vCZ356lQprX$8zws|2QGXre++x;W|plvBQWb zMJZ>69|Ij6mDZX5T<#bl$Cf4s?;4E|a_kW&mBVC<?C<1sKfzhMCC=KeCI_Wt$6nz& om?Lw{$;rvd$;rv7sQMLP03NQ=SuW`Ovj6}907*qoM6N<$f|9CqmjD0& diff --git a/integrations/nextcloud/snappymail/img/rainloop.svg b/integrations/nextcloud/snappymail/img/rainloop.svg deleted file mode 100644 index 645a728ff..000000000 --- a/integrations/nextcloud/snappymail/img/rainloop.svg +++ /dev/null @@ -1,265 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - version="1.1" - id="svg833" - width="32" - height="32" - viewBox="0 0 32 32" - sodipodi:docname="rainloop.svg" - inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"> - <metadata - id="metadata839"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <defs - id="defs837" /> - <sodipodi:namedview - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1" - objecttolerance="10" - gridtolerance="10" - guidetolerance="10" - inkscape:pageopacity="1" - inkscape:pageshadow="2" - inkscape:window-width="1920" - inkscape:window-height="1001" - id="namedview835" - showgrid="false" - inkscape:pagecheckerboard="false" - inkscape:zoom="22.627417" - inkscape:cx="19.245844" - inkscape:cy="14.4084" - inkscape:window-x="-9" - inkscape:window-y="-9" - inkscape:window-maximized="1" - inkscape:current-layer="g841" - inkscape:lockguides="false" - inkscape:document-rotation="0" /> - <g - inkscape:groupmode="layer" - inkscape:label="Image" - id="g841"> - <g - id="g1189" - transform="translate(-5e-7,-0.1334595)"> - <g - id="g921" - transform="matrix(0.73266354,0.68059102,-0.71588328,0.69821997,-0.05840962,-0.42219604)"> - <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.50332;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1" - d="M 5.6496339,3.1617897 H 25.056799 v 0.03224 H 5.6496339 Z" - id="path923" /> - <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.50332;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1" - d="M 5.6503906,2.4101562 C 5.2350485,2.4100358 4.898317,2.7467673 4.8984375,3.1621094 v 0.03125 c -1.204e-4,0.4153421 0.336611,0.7520735 0.7519531,0.7519531 H 25.056641 c 0.415342,1.204e-4 0.752073,-0.336611 0.751953,-0.7519531 L 24.737581,-0.12374013 C 24.493696,2.1372587 24.241088,2.426127 22.836001,2.4101563 h -0.981297 -1.962593 -4.538595 z" - id="path925" - sodipodi:nodetypes="cccccccccccc" /> - </g> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.33469;stroke-opacity:1" - id="rect994-7" - width="15.036267" - height="0.032800458" - x="4.2540865" - y="-19.78083" - transform="matrix(-0.69218043,0.7217245,-0.73411036,-0.67903018,0,0)" /> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.31971;stroke-opacity:1" - id="rect994-7-4" - width="14.866495" - height="0.032434449" - x="26.006418" - y="3.4238737" - transform="matrix(0.69298606,0.72095099,0.73486654,-0.67821175,0,0)" /> - <g - id="g921-4" - transform="matrix(-0.73266354,0.68059102,0.71588328,0.69821997,32.107971,-0.42219604)"> - <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.50332;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1" - d="M 5.6496339,3.1617897 H 25.056799 v 0.03224 H 5.6496339 Z" - id="path923-2" /> - <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.50332;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1" - d="M 5.6503906,2.4101562 C 5.2350485,2.4100358 4.898317,2.7467673 4.8984375,3.1621094 v 0.03125 c -1.204e-4,0.4153421 0.336611,0.7520735 0.7519531,0.7519531 H 25.056641 c 0.415342,1.204e-4 0.752073,-0.336611 0.751953,-0.7519531 L 24.737581,-0.12374013 C 24.493696,2.1372587 24.241088,2.426127 22.836001,2.4101563 h -0.981297 -1.962593 -4.538595 z" - id="path925-2" - sodipodi:nodetypes="cccccccccccc" /> - </g> - <g - id="g1055" - transform="translate(-0.07570186)"> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.0908;stroke-linejoin:round;stroke-opacity:1" - id="rect1343-5-0" - width="0.70920312" - height="0.0092054419" - x="-1.4234889" - y="4.4235983" - transform="scale(-1,1)" /> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.75566;stroke-opacity:1" - id="rect845-4-2-1" - width="1.9386948" - height="0.027632279" - x="5.3451033" - y="5.7705297" - transform="matrix(0.99999998,-2.0192883e-4,-0.60923877,0.79298683,0,0)" /> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.75566;stroke-opacity:1" - id="rect845-4-2-1-1" - width="1.9386948" - height="0.027632279" - x="-7.0470266" - y="1.0897269" - transform="matrix(-2.0192883e-4,-0.99999998,0.79298683,0.60923877,0,0)" /> - </g> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.75566;stroke-opacity:1" - id="rect845-4-2-1-8" - width="1.9386948" - height="0.027632279" - x="14.047385" - y="5.7727451" - transform="matrix(0.99999998,-2.0192883e-4,-0.60923877,0.79298683,0,0)" /> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.75566;stroke-opacity:1" - id="rect845-4-2-1-88" - width="1.9386948" - height="0.027632279" - x="22.940956" - y="5.7750101" - transform="matrix(0.99999998,-2.0192883e-4,-0.60923877,0.79298683,0,0)" /> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.75566;stroke-opacity:1" - id="rect845-4-2-1-8-6" - width="1.9386948" - height="0.027632279" - x="14.613998" - y="-1.0194745" - transform="matrix(2.0192883e-4,0.99999998,-0.79298683,-0.60923877,0,0)" /> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.75566;stroke-opacity:1" - id="rect845-4-2-1-8-8" - width="1.9386948" - height="0.027632279" - x="-8.9796991" - y="-39.359989" - transform="matrix(2.0192883e-4,0.99999998,-0.79298683,-0.60923877,0,0)" /> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.75566;stroke-opacity:1" - id="rect845-4-2-1-8-3" - width="1.9386948" - height="0.027632279" - x="31.91931" - y="34.90218" - transform="matrix(0.99999998,-2.0192883e-4,-0.60923877,0.79298683,0,0)" /> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.75566;stroke-opacity:1" - id="rect845-4-2-1-8-64" - width="1.9386948" - height="0.027632279" - x="40.643787" - y="34.626904" - transform="matrix(0.99999998,-2.0192883e-4,-0.60923877,0.79298683,0,0)" /> - <g - id="g1055-7" - transform="rotate(180,16.037851,16.023434)"> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.0908;stroke-linejoin:round;stroke-opacity:1" - id="rect1343-5-0-1" - width="0.70920312" - height="0.0092054419" - x="-1.4234889" - y="4.4235983" - transform="scale(-1,1)" /> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.75566;stroke-opacity:1" - id="rect845-4-2-1-5" - width="1.9386948" - height="0.027632279" - x="5.3451033" - y="5.7705297" - transform="matrix(0.99999998,-2.0192883e-4,-0.60923877,0.79298683,0,0)" /> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.75566;stroke-opacity:1" - id="rect845-4-2-1-1-0" - width="1.9386948" - height="0.027632279" - x="-7.0470266" - y="1.0897269" - transform="matrix(-2.0192883e-4,-0.99999998,0.79298683,0.60923877,0,0)" /> - </g> - <g - id="g1055-0" - transform="rotate(90,16.037852,19.747164)"> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.0908;stroke-linejoin:round;stroke-opacity:1" - id="rect1343-5-0-4" - width="0.70920312" - height="0.0092054419" - x="-1.4234889" - y="4.4235983" - transform="scale(-1,1)" /> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.75566;stroke-opacity:1" - id="rect845-4-2-1-80" - width="1.9386948" - height="0.027632279" - x="5.3451033" - y="5.7705297" - transform="matrix(0.99999998,-2.0192883e-4,-0.60923877,0.79298683,0,0)" /> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.75566;stroke-opacity:1" - id="rect845-4-2-1-1-5" - width="1.9386948" - height="0.027632279" - x="-7.0470266" - y="1.0897269" - transform="matrix(-2.0192883e-4,-0.99999998,0.79298683,0.60923877,0,0)" /> - </g> - <g - id="g1055-5" - transform="rotate(-90,12.386296,16.171311)"> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.0908;stroke-linejoin:round;stroke-opacity:1" - id="rect1343-5-0-0" - width="0.70920312" - height="0.0092054419" - x="-1.4234889" - y="4.4235983" - transform="scale(-1,1)" /> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.75566;stroke-opacity:1" - id="rect845-4-2-1-0" - width="1.9386948" - height="0.027632279" - x="5.3451033" - y="5.7705297" - transform="matrix(0.99999998,-2.0192883e-4,-0.60923877,0.79298683,0,0)" /> - <rect - style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.75566;stroke-opacity:1" - id="rect845-4-2-1-1-1" - width="1.9386948" - height="0.027632279" - x="-7.0470266" - y="1.0897269" - transform="matrix(-2.0192883e-4,-0.99999998,0.79298683,0.60923877,0,0)" /> - </g> - </g> - </g> -</svg> diff --git a/integrations/nextcloud/snappymail/lib/AppInfo/Application.php b/integrations/nextcloud/snappymail/lib/AppInfo/Application.php index 6cfe12691..d1d81e6bd 100644 --- a/integrations/nextcloud/snappymail/lib/AppInfo/Application.php +++ b/integrations/nextcloud/snappymail/lib/AppInfo/Application.php @@ -73,7 +73,7 @@ class Application extends App { 'id' => 'snappymail', 'order' => 10, 'href' => $urlGenerator->linkToRoute('snappymail.page.index'), - 'icon' => $urlGenerator->imagePath('snappymail', 'snappymail.svg'), + 'icon' => $urlGenerator->imagePath('snappymail', 'logo-64x64.png'), 'name' => \OCP\Util::getL10N('snappymail')->t('Email') ]; }); diff --git a/integrations/nextcloud/snappymail/screenshots/help_a1.png b/integrations/nextcloud/snappymail/screenshots/help_a1.png new file mode 100644 index 0000000000000000000000000000000000000000..bec6cbed3ab59567b83c9a3e9880d4e4d2972282 GIT binary patch literal 14292 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 Bw%7mw literal 0 HcmV?d00001 diff --git a/integrations/nextcloud/snappymail/screenshots/help_a2.png b/integrations/nextcloud/snappymail/screenshots/help_a2.png new file mode 100644 index 0000000000000000000000000000000000000000..0d7174f5df66c578970d6ab0db0c05ba2c45fd17 GIT binary patch literal 23460 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 literal 0 HcmV?d00001 diff --git a/integrations/nextcloud/snappymail/screenshots/help_a3.png b/integrations/nextcloud/snappymail/screenshots/help_a3.png new file mode 100644 index 0000000000000000000000000000000000000000..77415d8151756b444adf0f507c28d97e46f93a7b GIT binary patch literal 13001 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 Prl6GWsL1EZKJ)tz=*B}h literal 0 HcmV?d00001 diff --git a/integrations/nextcloud/snappymail/screenshots/help_a4.png b/integrations/nextcloud/snappymail/screenshots/help_a4.png new file mode 100644 index 0000000000000000000000000000000000000000..ff2e62d666ef3bbd669f183381441ce04dc2e3e8 GIT binary patch literal 57638 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 L^2lpt6W{*=5a<>? literal 0 HcmV?d00001 From 1f2208d30440a373bdf7fdf3fe37ecd0004905a5 Mon Sep 17 00:00:00 2001 From: djmaze <djmaze@djmaze.lan> Date: Mon, 19 Jul 2021 14:26:32 +0200 Subject: [PATCH 16/26] Cleanup time handling --- dev/App/User.js | 11 ++++++++++- dev/Common/Momentor.js | 6 ------ dev/Common/Translator.js | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/dev/App/User.js b/dev/App/User.js index 82f319fa2..b055b043c 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -81,6 +81,8 @@ import { ComposePopupView } from 'View/Popup/Compose'; import { FolderSystemPopupView } from 'View/Popup/FolderSystem'; import { AskPopupView } from 'View/Popup/Ask'; +import { timeToNode } from 'Common/Momentor'; + // Every 5 minutes const refreshFolders = 300000; @@ -1020,7 +1022,14 @@ class AppUser extends AbstractApp { this.hideLoading(); } - setInterval(() => dispatchEvent(new CustomEvent('reload-time')), 60000); + setInterval(this.reloadTime(), 60000); + } + + reloadTime() + { + setTimeout(() => + doc.querySelectorAll('[data-bind*="moment:"]').forEach(element => timeToNode(element)) + , 1) } showMessageComposer(params = []) diff --git a/dev/Common/Momentor.js b/dev/Common/Momentor.js index 6da0a88ce..aa6efed35 100644 --- a/dev/Common/Momentor.js +++ b/dev/Common/Momentor.js @@ -1,4 +1,3 @@ -import { doc } from 'Common/Globals'; import { i18n } from 'Common/Translator'; export function timestampToString(timeStampInUTC, formatStr) { @@ -53,8 +52,3 @@ export function timeToNode(element, time) { console.error(e); } } - -addEventListener('reload-time', () => setTimeout(() => - doc.querySelectorAll('[data-bind*="moment:"]').forEach(element => timeToNode(element)) - , 1) -); diff --git a/dev/Common/Translator.js b/dev/Common/Translator.js index ba9728276..dd2d81bb6 100644 --- a/dev/Common/Translator.js +++ b/dev/Common/Translator.js @@ -140,7 +140,7 @@ export function reload(admin, language) { // reload the data if (init()) { i18nToNodes(doc); - dispatchEvent(new CustomEvent('reload-time')); + admin || rl.app.reloadTime(); trigger(!trigger()); } script.remove(); From d3d018020884b2021342aebbfd794ee8f6ac6697 Mon Sep 17 00:00:00 2001 From: djmaze <djmaze@djmaze.lan> Date: Mon, 19 Jul 2021 14:27:25 +0200 Subject: [PATCH 17/26] Bugfix: default attachment_size_limit to PHP upload_max_filesize --- .../app/libraries/RainLoop/Config/Application.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php index 8858d1433..8afc3ec3a 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php @@ -106,6 +106,15 @@ class Application extends \RainLoop\Config\AbstractConfig protected function defaultValues() : array { + $value = \ini_get('upload_max_filesize'); + $upload_max_filesize = \intval($value); + switch (\strtoupper(\substr($value, -1))) { + case 'G': $upload_max_filesize *= 1024; + case 'M': $upload_max_filesize *= 1024; + case 'K': $upload_max_filesize *= 1024; + } + $upload_max_filesize = $upload_max_filesize / 1024 / 1024; + return array( 'webmail' => array( @@ -127,7 +136,7 @@ class Application extends \RainLoop\Config\AbstractConfig 'messages_per_page' => array(20, 'Number of messages displayed on page by default'), - 'attachment_size_limit' => array(25, 'File size limit (MB) for file upload on compose screen + 'attachment_size_limit' => array(\min($upload_max_filesize, 25), 'File size limit (MB) for file upload on compose screen 0 for unlimited.') ), From e13337e247c4214921f6597278e8e71c8e4283db Mon Sep 17 00:00:00 2001 From: djmaze <djmaze@djmaze.lan> Date: Tue, 20 Jul 2021 16:45:55 +0200 Subject: [PATCH 18/26] Cleanup styling and gulp merge @media queries --- dev/Stores/User/Message.js | 4 +- dev/Styles/@Admin.less | 17 +- dev/Styles/@Main.less | 56 +-- dev/Styles/Account.less | 4 - .../{AdminAbout.less => Admin/About.less} | 0 .../Branding.less} | 0 .../{AdminDomain.less => Admin/Domain.less} | 4 +- .../{AdminDomains.less => Admin/Domains.less} | 0 .../{AdminGeneral.less => Admin/General.less} | 0 dev/Styles/{Admin.less => Admin/Layout.less} | 76 +++ .../Packages.less} | 0 .../{AdminPlugin.less => Admin/Plugin.less} | 2 +- dev/Styles/AdminPlugins.less | 24 - dev/Styles/FolderClear.less | 4 - dev/Styles/FolderCreate.less | 4 - dev/Styles/FolderSystem.less | 4 - dev/Styles/Languages.less | 8 +- dev/Styles/LayoutAdmin.less | 80 --- dev/Styles/Main.less | 9 +- dev/Styles/Ui.less | 45 -- dev/Styles/{ => User}/AdvancedSearch.less | 2 +- .../Animations.less} | 9 - dev/Styles/{ => User}/Attachmnets.less | 0 dev/Styles/{ => User}/Compose.less | 0 dev/Styles/{ => User}/Contacts.less | 0 dev/Styles/{ => User}/EmailAddresses.less | 0 dev/Styles/{ => User}/Filter.less | 0 dev/Styles/{ => User}/FolderList.less | 0 dev/Styles/{ => User}/Identity.less | 2 +- dev/Styles/{ => User}/Layout.less | 21 + dev/Styles/{ => User}/MessageList.less | 469 +++++++++--------- dev/Styles/{ => User}/MessageView.less | 0 dev/Styles/{ => User}/OpenPgpKey.less | 8 +- dev/Styles/{ => User}/Settings.less | 0 dev/Styles/{ => User}/SettingsAccounts.less | 0 dev/Styles/{ => User}/SettingsFilters.less | 0 dev/Styles/{ => User}/SettingsFolders.less | 0 dev/Styles/{ => User}/SettingsGeneral.less | 0 dev/Styles/{ => User}/SettingsOpenPGP.less | 0 dev/Styles/{ => User}/SettingsTemplates.less | 0 dev/Styles/{ => User}/SettingsThemes.less | 0 dev/Styles/{ => User}/Shortcuts.less | 2 +- dev/Styles/{ => User}/SquireUI.less | 0 dev/Styles/{ => User}/SystemDropDown.less | 0 dev/Styles/{ => User}/Template.less | 2 +- dev/Styles/_BootstrapFix.less | 11 - dev/Styles/_FontasticToBoot.less | 3 +- dev/View/User/MailBox/MessageList.js | 13 +- package.json | 1 + plugins/change-password/index.php | 7 +- plugins/change-password/style.less | 5 + .../templates/SettingsChangePassword.html | 2 +- .../app/templates/Views/Common/Paginator.html | 4 +- .../Views/Common/PopupsLanguages.html | 12 +- .../templates/Views/User/MailMessageList.html | 464 +++++++++-------- .../v/0.0.0/themes/SquaresDark/styles.less | 2 +- tasks/css.js | 3 + vendors/bootstrap/less/forms.less | 1 + vendors/bootstrap/less/modals.less | 3 +- yarn.lock | 43 +- 60 files changed, 699 insertions(+), 731 deletions(-) delete mode 100644 dev/Styles/Account.less rename dev/Styles/{AdminAbout.less => Admin/About.less} (100%) rename dev/Styles/{AdminBranding.less => Admin/Branding.less} (100%) rename dev/Styles/{AdminDomain.less => Admin/Domain.less} (94%) rename dev/Styles/{AdminDomains.less => Admin/Domains.less} (100%) rename dev/Styles/{AdminGeneral.less => Admin/General.less} (100%) rename dev/Styles/{Admin.less => Admin/Layout.less} (51%) rename dev/Styles/{AdminPackages.less => Admin/Packages.less} (100%) rename dev/Styles/{AdminPlugin.less => Admin/Plugin.less} (93%) delete mode 100644 dev/Styles/AdminPlugins.less delete mode 100644 dev/Styles/FolderClear.less delete mode 100644 dev/Styles/FolderCreate.less delete mode 100644 dev/Styles/FolderSystem.less delete mode 100644 dev/Styles/LayoutAdmin.less rename dev/Styles/{ => User}/AdvancedSearch.less (84%) rename dev/Styles/{AnimationsUser.less => User/Animations.less} (92%) rename dev/Styles/{ => User}/Attachmnets.less (100%) rename dev/Styles/{ => User}/Compose.less (100%) rename dev/Styles/{ => User}/Contacts.less (100%) rename dev/Styles/{ => User}/EmailAddresses.less (100%) rename dev/Styles/{ => User}/Filter.less (100%) rename dev/Styles/{ => User}/FolderList.less (100%) rename dev/Styles/{ => User}/Identity.less (91%) rename dev/Styles/{ => User}/Layout.less (95%) rename dev/Styles/{ => User}/MessageList.less (58%) rename dev/Styles/{ => User}/MessageView.less (100%) rename dev/Styles/{ => User}/OpenPgpKey.less (95%) rename dev/Styles/{ => User}/Settings.less (100%) rename dev/Styles/{ => User}/SettingsAccounts.less (100%) rename dev/Styles/{ => User}/SettingsFilters.less (100%) rename dev/Styles/{ => User}/SettingsFolders.less (100%) rename dev/Styles/{ => User}/SettingsGeneral.less (100%) rename dev/Styles/{ => User}/SettingsOpenPGP.less (100%) rename dev/Styles/{ => User}/SettingsTemplates.less (100%) rename dev/Styles/{ => User}/SettingsThemes.less (100%) rename dev/Styles/{ => User}/Shortcuts.less (74%) rename dev/Styles/{ => User}/SquireUI.less (100%) rename dev/Styles/{ => User}/SystemDropDown.less (100%) rename dev/Styles/{ => User}/Template.less (80%) create mode 100644 plugins/change-password/style.less diff --git a/dev/Stores/User/Message.js b/dev/Stores/User/Message.js index 4ce1bf0eb..f36fe51d6 100644 --- a/dev/Stores/User/Message.js +++ b/dev/Stores/User/Message.js @@ -2,7 +2,7 @@ import ko from 'ko'; import { Scope, Notification } from 'Common/Enums'; import { MessageSetAction } from 'Common/EnumsUser'; -import { doc, createElement, elementById } from 'Common/Globals'; +import { doc, $htmlCL, createElement, elementById } from 'Common/Globals'; import { isNonEmptyArray, pInt, pString, addObservablesTo, addSubscribablesTo } from 'Common/Utils'; import { plainToHtml } from 'Common/UtilsUser'; @@ -186,6 +186,8 @@ export const MessageUserStore = new class { } }, + listLoadingAnimation: value => $htmlCL.toggle('list-loading', value), + listLoading: value => this.listCompleteLoading(value || this.listIsNotCompleted()), diff --git a/dev/Styles/@Admin.less b/dev/Styles/@Admin.less index c8eb37181..83cab6e63 100644 --- a/dev/Styles/@Admin.less +++ b/dev/Styles/@Admin.less @@ -23,20 +23,19 @@ @import "Ui.less"; @import "Main.less"; -@import "LayoutAdmin.less"; @import "Components.less"; @import "Login.less"; @import "Ask.less"; @import "Languages.less"; -@import "Admin.less"; -@import "AdminGeneral.less"; -@import "AdminBranding.less"; -@import "AdminDomains.less"; -@import "AdminDomain.less"; -@import "AdminPackages.less"; -@import "AdminPlugin.less"; -@import "AdminAbout.less"; +@import "Admin/Layout.less"; +@import "Admin/General.less"; +@import "Admin/Branding.less"; +@import "Admin/Domains.less"; +@import "Admin/Domain.less"; +@import "Admin/Packages.less"; +@import "Admin/Plugin.less"; +@import "Admin/About.less"; @import "Animations.less"; diff --git a/dev/Styles/@Main.less b/dev/Styles/@Main.less index b46372da1..fb03732ad 100644 --- a/dev/Styles/@Main.less +++ b/dev/Styles/@Main.less @@ -24,45 +24,39 @@ @import "Ui.less"; @import "Main.less"; -@import "Layout.less"; @import "Components.less"; @import "Login.less"; @import "Ask.less"; @import "Languages.less"; -@import "SystemDropDown.less"; -@import "Shortcuts.less"; -@import "FolderList.less"; -/* -@import "FolderClear.less"; -@import "FolderCreate.less"; -@import "FolderSystem.less"; -@import "Account.less"; -*/ -@import "Filter.less"; -@import "Template.less"; -@import "OpenPgpKey.less"; -@import "Identity.less"; -@import "AdvancedSearch.less"; -@import "Attachmnets.less"; -@import "MessageList.less"; -@import "MessageView.less"; -@import "Contacts.less"; -@import "Compose.less"; -@import "EmailAddresses.less"; +@import "User/Layout.less"; +@import "User/SystemDropDown.less"; +@import "User/Shortcuts.less"; +@import "User/FolderList.less"; +@import "User/Filter.less"; +@import "User/Template.less"; +@import "User/OpenPgpKey.less"; +@import "User/Identity.less"; +@import "User/AdvancedSearch.less"; +@import "User/Attachmnets.less"; +@import "User/MessageList.less"; +@import "User/MessageView.less"; +@import "User/Contacts.less"; +@import "User/Compose.less"; +@import "User/EmailAddresses.less"; -@import "Settings.less"; -@import "SettingsGeneral.less"; -@import "SettingsAccounts.less"; -@import "SettingsTemplates.less"; -@import "SettingsOpenPGP.less"; -@import "SettingsFolders.less"; -@import "SettingsThemes.less"; -@import "SettingsFilters.less"; +@import "User/Settings.less"; +@import "User/SettingsGeneral.less"; +@import "User/SettingsAccounts.less"; +@import "User/SettingsTemplates.less"; +@import "User/SettingsOpenPGP.less"; +@import "User/SettingsFolders.less"; +@import "User/SettingsThemes.less"; +@import "User/SettingsFilters.less"; -@import "SquireUI.less"; +@import "User/SquireUI.less"; -@import "AnimationsUser.less"; +@import "User/Animations.less"; @import "Animations.less"; @import "_End.less"; diff --git a/dev/Styles/Account.less b/dev/Styles/Account.less deleted file mode 100644 index f233b3c39..000000000 --- a/dev/Styles/Account.less +++ /dev/null @@ -1,4 +0,0 @@ -.b-account-add-content { - .modal-header { - } -} diff --git a/dev/Styles/AdminAbout.less b/dev/Styles/Admin/About.less similarity index 100% rename from dev/Styles/AdminAbout.less rename to dev/Styles/Admin/About.less diff --git a/dev/Styles/AdminBranding.less b/dev/Styles/Admin/Branding.less similarity index 100% rename from dev/Styles/AdminBranding.less rename to dev/Styles/Admin/Branding.less diff --git a/dev/Styles/AdminDomain.less b/dev/Styles/Admin/Domain.less similarity index 94% rename from dev/Styles/AdminDomain.less rename to dev/Styles/Admin/Domain.less index 79ec859dc..4abc8f78a 100644 --- a/dev/Styles/AdminDomain.less +++ b/dev/Styles/Admin/Domain.less @@ -1,6 +1,6 @@ .b-domain-alias-content { &.modal { - width: 330px; + max-width: 330px; } .error-desc { @@ -11,7 +11,7 @@ .b-domain-content { &.modal { - width: 810px; + max-width: 810px; } .modal-body { diff --git a/dev/Styles/AdminDomains.less b/dev/Styles/Admin/Domains.less similarity index 100% rename from dev/Styles/AdminDomains.less rename to dev/Styles/Admin/Domains.less diff --git a/dev/Styles/AdminGeneral.less b/dev/Styles/Admin/General.less similarity index 100% rename from dev/Styles/AdminGeneral.less rename to dev/Styles/Admin/General.less diff --git a/dev/Styles/Admin.less b/dev/Styles/Admin/Layout.less similarity index 51% rename from dev/Styles/Admin.less rename to dev/Styles/Admin/Layout.less index e166a3cc5..59da0863c 100644 --- a/dev/Styles/Admin.less +++ b/dev/Styles/Admin/Layout.less @@ -1,7 +1,25 @@ +.UserBackground body { + background-size: contain; + background-repeat: no-repeat; + background-position: center; +} + +#rl-left, #rl-right { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: 0; + margin: 0; + border: 0; + z-index: 0; +} #rl-left { overflow: auto; padding-top: 50px + @rlLowMargin + 10px; + width: @rlLeftWidth; nav { a { @@ -26,6 +44,11 @@ } } +#rl-right { + z-index: 1; + left: @rlLeftWidth; +} + .b-admin-right { .b-toolbar { @@ -67,3 +90,56 @@ padding-left: 12px; padding-right: 12px; } + +#rl-popups > .rl-view-model { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + z-index: 1100; + overflow: auto; + background-color: rgba(0,0,0,0.3); +} + +#rl-settings-subscreen { + padding: 20px; +} + +/* desktop */ +@media screen and (min-width: 1000px) { + .toggleLeft { + display: none; + } +} + +/* desktop-large */ +@media screen and (min-width: 1401px) { + #rl-left { + width: @rlLeftWidth + 20; + } + + #rl-right { + left: @rlLeftWidth + 20; + } +} + +/* mobile and tablet */ +@media screen and (max-width: 999px) { + #rl-settings-subscreen { + padding: 10px; + } + + html:not(.rl-left-panel-disabled) #rl-right { + right: 5-@rlLeftWidth; + } + + html.rl-left-panel-disabled { + #rl-left { + width: 0 !important; + } + #rl-right { + left: 5px !important; + } + } +} diff --git a/dev/Styles/AdminPackages.less b/dev/Styles/Admin/Packages.less similarity index 100% rename from dev/Styles/AdminPackages.less rename to dev/Styles/Admin/Packages.less diff --git a/dev/Styles/AdminPlugin.less b/dev/Styles/Admin/Plugin.less similarity index 93% rename from dev/Styles/AdminPlugin.less rename to dev/Styles/Admin/Plugin.less index 94b45b005..9cda5bf21 100644 --- a/dev/Styles/AdminPlugin.less +++ b/dev/Styles/Admin/Plugin.less @@ -1,7 +1,7 @@ .b-plugin-content { &.modal { - width: 660px; + max-width: 660px; } .modal-body { diff --git a/dev/Styles/AdminPlugins.less b/dev/Styles/AdminPlugins.less deleted file mode 100644 index 35e2f916b..000000000 --- a/dev/Styles/AdminPlugins.less +++ /dev/null @@ -1,24 +0,0 @@ - -.b-admin-plugins-list-table { - max-width: 620px; /* span8 */ - - &.disabled { - opacity: 0.5; - } - - .disabled { - opacity: 0.5; - } -} - -.b-admin-plugin-property { - .help-block { - display: block; // account for any element using help-block - font-style: italic; - opacity: 0.5; - margin-bottom: 5px; - } - .controls { - width: 470px; - } -} diff --git a/dev/Styles/FolderClear.less b/dev/Styles/FolderClear.less deleted file mode 100644 index 8089f0de6..000000000 --- a/dev/Styles/FolderClear.less +++ /dev/null @@ -1,4 +0,0 @@ -.b-folder-clear-content { - .modal-header { - } -} diff --git a/dev/Styles/FolderCreate.less b/dev/Styles/FolderCreate.less deleted file mode 100644 index 2b2be8b48..000000000 --- a/dev/Styles/FolderCreate.less +++ /dev/null @@ -1,4 +0,0 @@ -.b-folder-create-content { - .modal-header { - } -} diff --git a/dev/Styles/FolderSystem.less b/dev/Styles/FolderSystem.less deleted file mode 100644 index 4a5f64d25..000000000 --- a/dev/Styles/FolderSystem.less +++ /dev/null @@ -1,4 +0,0 @@ -.b-folder-system-content { - .modal-header { - } -} diff --git a/dev/Styles/Languages.less b/dev/Styles/Languages.less index e84676baf..25dd109c9 100644 --- a/dev/Styles/Languages.less +++ b/dev/Styles/Languages.less @@ -1,16 +1,15 @@ .b-languages-content { &.modal { - width: 700px; + max-width: 700px; } - .lang-item { + label { display: inline-block; padding: 5px 15px; margin: 2px 5px; width: 180px; background-color: rgba(128, 128, 128, 0.1); - text-align: left; border: 1px solid transparent; border-radius: 3px; position: relative; @@ -26,7 +25,6 @@ } &.selected::after { content: "✔"; - color: #080; position: absolute; right: 4px; font-family: snappymail; @@ -39,7 +37,7 @@ } @media screen and (max-width: 999px) { - .b-languages-content .lang-item { + .b-languages-content label { width: calc(100% - 40px); } } diff --git a/dev/Styles/LayoutAdmin.less b/dev/Styles/LayoutAdmin.less deleted file mode 100644 index dafda8c50..000000000 --- a/dev/Styles/LayoutAdmin.less +++ /dev/null @@ -1,80 +0,0 @@ - -.UserBackground body { - background-size: contain; - background-repeat: no-repeat; - background-position: center; -} - -#rl-left, #rl-right { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - padding: 0; - margin: 0; - border: 0; - z-index: 0; -} - -#rl-left { - width: @rlLeftWidth; -} - -#rl-right { - z-index: 1; - left: @rlLeftWidth; -} - -#rl-popups > .rl-view-model { - position: fixed; - top: 0; - bottom: 0; - left: 0; - right: 0; - z-index: 1100; - overflow: auto; - background-color: rgba(0,0,0,0.3); -} - -#rl-settings-subscreen { - padding: 20px; -} - -/* desktop */ -@media screen and (min-width: 1000px) { - .toggleLeft { - display: none; - } -} - -/* desktop-large */ -@media screen and (min-width: 1401px) { - #rl-left { - width: @rlLeftWidth + 20; - } - - #rl-right { - left: @rlLeftWidth + 20; - } -} - -/* mobile and tablet */ -@media screen and (max-width: 999px) { - #rl-settings-subscreen { - padding: 10px; - } - - html:not(.rl-left-panel-disabled) #rl-right { - right: 5-@rlLeftWidth; - } - - html.rl-left-panel-disabled { - #rl-left { - width: 0 !important; - } - #rl-right { - left: 5px !important; - } - } -} diff --git a/dev/Styles/Main.less b/dev/Styles/Main.less index 62ba75249..8600d1fd0 100644 --- a/dev/Styles/Main.less +++ b/dev/Styles/Main.less @@ -20,6 +20,11 @@ body { right: 0; } +html.list-loading body { +/* cursor: wait;*/ + cursor: progress; +} + html:not(.rl-mobile) { min-width: 700px; } @@ -32,10 +37,6 @@ html:not(.rl-mobile) { } } -textarea { - resize: none; -} - option:disabled { color: #aaa; cursor: not-allowed; diff --git a/dev/Styles/Ui.less b/dev/Styles/Ui.less index 86e39f5ca..8387e010f 100644 --- a/dev/Styles/Ui.less +++ b/dev/Styles/Ui.less @@ -16,32 +16,6 @@ } } -.e-paginator { - - .e-page { - - display: inline-block; - opacity: 0.5; - text-decoration: none; - font-size: 22px; - padding: 3px; - cursor: pointer; - - &:hover { - opacity: 0.8; - } - - &.current { - opacity: 1; - } - - &.current .e-page-number { - font-size: 115%; - text-decoration: underline; - } - } -} - .settings-save-trigger { display: inline-block; @@ -93,25 +67,6 @@ textarea + .settings-save-trigger { } } -.e-languages { - - margin-top: 8px; - color: #333; - - .flag-name { - - color: #333; - border-bottom: 1px dashed #333; - cursor: pointer; - padding: 2px 0; - - &:focus { - outline: 1px; - outline-style: dotted; - } - } -} - .e-action { cursor: pointer; } diff --git a/dev/Styles/AdvancedSearch.less b/dev/Styles/User/AdvancedSearch.less similarity index 84% rename from dev/Styles/AdvancedSearch.less rename to dev/Styles/User/AdvancedSearch.less index 5ffb11241..b32ac75d0 100644 --- a/dev/Styles/AdvancedSearch.less +++ b/dev/Styles/User/AdvancedSearch.less @@ -1,6 +1,6 @@ .b-advanced-search-content { &.modal { - width: 780px; + max-width: 780px; } .control-label { width: 110px; diff --git a/dev/Styles/AnimationsUser.less b/dev/Styles/User/Animations.less similarity index 92% rename from dev/Styles/AnimationsUser.less rename to dev/Styles/User/Animations.less index c465208b1..4f1a410f3 100644 --- a/dev/Styles/AnimationsUser.less +++ b/dev/Styles/User/Animations.less @@ -43,15 +43,6 @@ transition: opacity 0.3s linear; } - .messageList { - .messageListItem { - transition: max-height 400ms ease; - } - .listDragOver { - transition: all 400ms ease; - } - } - .b-list-content { .e-contact-item { transition: max-height 400ms ease; diff --git a/dev/Styles/Attachmnets.less b/dev/Styles/User/Attachmnets.less similarity index 100% rename from dev/Styles/Attachmnets.less rename to dev/Styles/User/Attachmnets.less diff --git a/dev/Styles/Compose.less b/dev/Styles/User/Compose.less similarity index 100% rename from dev/Styles/Compose.less rename to dev/Styles/User/Compose.less diff --git a/dev/Styles/Contacts.less b/dev/Styles/User/Contacts.less similarity index 100% rename from dev/Styles/Contacts.less rename to dev/Styles/User/Contacts.less diff --git a/dev/Styles/EmailAddresses.less b/dev/Styles/User/EmailAddresses.less similarity index 100% rename from dev/Styles/EmailAddresses.less rename to dev/Styles/User/EmailAddresses.less diff --git a/dev/Styles/Filter.less b/dev/Styles/User/Filter.less similarity index 100% rename from dev/Styles/Filter.less rename to dev/Styles/User/Filter.less diff --git a/dev/Styles/FolderList.less b/dev/Styles/User/FolderList.less similarity index 100% rename from dev/Styles/FolderList.less rename to dev/Styles/User/FolderList.less diff --git a/dev/Styles/Identity.less b/dev/Styles/User/Identity.less similarity index 91% rename from dev/Styles/Identity.less rename to dev/Styles/User/Identity.less index 2d1b25209..416f324df 100644 --- a/dev/Styles/Identity.less +++ b/dev/Styles/User/Identity.less @@ -1,7 +1,7 @@ .b-identity-content { &.modal { - width: 770px; + max-width: 770px; } .modal-body { diff --git a/dev/Styles/Layout.less b/dev/Styles/User/Layout.less similarity index 95% rename from dev/Styles/Layout.less rename to dev/Styles/User/Layout.less index 75d81edd8..0a2f88f13 100644 --- a/dev/Styles/Layout.less +++ b/dev/Styles/User/Layout.less @@ -371,3 +371,24 @@ html:not(.rl-mobile) .show-mobile { html.rl-mobile .width100-on-mobile { width: 100% !important; } + +.e-paginator { + + .e-page { + opacity: 0.5; + text-decoration: none; + font-size: 22px; + padding: 3px; + cursor: pointer; + + &:hover { + opacity: 0.8; + } + + &.current { + opacity: 1; + font-size: 25px; + cursor: default; + } + } +} diff --git a/dev/Styles/MessageList.less b/dev/Styles/User/MessageList.less similarity index 58% rename from dev/Styles/MessageList.less rename to dev/Styles/User/MessageList.less index 10bda6872..af2c0c547 100644 --- a/dev/Styles/MessageList.less +++ b/dev/Styles/User/MessageList.less @@ -198,215 +198,6 @@ html.rl-no-preview-pane { } } - .messageListItem { - - position: relative; - font-size: 12px; - overflow: hidden; - cursor: pointer; - - margin: 0; - padding: 5px 0; - border: 0; - border-bottom: 1px solid rgba(153, 153, 153, 0.2); - border-left: 6px solid #eee; - - z-index: 100; - - &.focused { - background-color: rgba(128, 128, 128, 0.1); - border-left-color: #ccc; - } - - .importantMark { - display: none; - - color:red; - margin-right:5px - } - - &.deleted-mark { - opacity: .7; - .subjectParent { - text-decoration: line-through; - } - } - - &.important .importantMark { - display: inline; - } - - &.new { - max-height: 0; - } - - &.deleted { - max-height: 0; - border-color: transparent !important; - } - - .checkboxMessage { - line-height: 12px; - padding: 0 8px 0 6px; - font-size: 16px; - } - - .flagParent { - padding: 0 10px 0 5px; - } - - time, .sizeParent { - margin: 0 5px; - opacity: 0.6; - font-size: 11px; - white-space: nowrap; - } - - .threadsParent { - position: relative; - } - - .attachmentParent { - position: relative; - margin: 2px 10px 0 5px; - color: #666; - text-shadow: 0 1px 0 #eee; - } - - .senderParent, .subjectParent { - overflow: hidden; - white-space: nowrap; - } - - .threadsCountParent { - display: inline; - overflow: hidden; - background-color: #eee; - padding: 1px 5px; - margin-right: 5px; - border: 1px solid #ccc; - border-radius: 5px; - } - - .threadsCountParent.lastSelected { - background-color: #999; - border-color: #999; - color: #fff; - } - - .threadsCountParent:hover { - border-color: #666; - } - - .subjectParent .emptySubjectText { - display: none; - font-style: italic; - opacity: 0.5; - } - - &.emptySubject .subjectParent { - .subject { - display: none; - } - .emptySubjectText { - display: inline; - } - } - - .sender, .subject { - margin: 0; - overflow: hidden; - text-overflow: ellipsis; - } - - .threads-len { - .threads-len-data { - - border-radius: 6px; - padding: 2px 0 1px 4px; - border: 1px solid #ccc; - - &:hover { - background-color: rgba(127,127,127,0.3); - border-color: #666; - } - } - } - - .flagOff { - opacity: 0.5; - &:hover { - opacity: 1; - } - } - - .flagOn, .flagOnHalf { - color: orange; - } - - .replyFlag, .forwardFlag { - display: none; - margin-right: 0.25em; - } - - &.answered .replyFlag { - display: inline-block; - } - - &.forwarded .forwardFlag { - display: inline-block; - } - - &:not(.withAttachments) .attachmentParent { - display: none; - } - - &.hasUnseenSubMessage { - background-color: rgba(255, 255, 64, 0.15); - border-left-color: lighten(orange, 30%); - &.focused { - border-left-color: darken(orange, 10%); - } - } - - &.unseen { - background-color: rgba(255, 255, 64, 0.15); - border-left-color: orange; - .sender, .subjectParent { - font-weight: bold; - } - - &.focused { - border-left-color: darken(orange, 10%); - } - } - - &.checked { - border-left-color: lighten(#398CF2, 10%); - - &.focused { - border-left-color: darken(#398CF2, 5%); - } - } - - &.selected { - background-color: rgba(140, 200, 255, 0.3); - border-bottom-color: rgba(57, 140, 242, 0.2); - border-left-color: #398CF2; - z-index: 101; - - + .messageListItem { - border-bottom-color: rgba(57, 140, 242, 0.3); - } - } - - &:not(.flagged) .flagOn, - &:not(.hasFlaggedSubMessage) .flagOnHalf, - &.flagged .flagOff, &.hasFlaggedSubMessage .flagOff { - display: none; - } - } - &.message-focused { .b-message-list-wrapper { background-color: #000; @@ -415,17 +206,250 @@ html.rl-no-preview-pane { opacity: 0.97; } } +} - &.hideMessageListCheckbox { - .checkboxMessage, .checkboxCheckAll { - visibility: hidden; +.hideMessageListCheckbox { + .checkboxCheckAll { + visibility: hidden; + } + + .checkboxMessage { + display: none; + } +} + +.messageListItem { + display: flex; + flex-wrap: wrap; + + position: relative; + font-size: 12px; + overflow: hidden; + cursor: pointer; + + margin: 0; + padding: 5px 0; + border: 0; + border-bottom: 1px solid rgba(153, 153, 153, 0.2); + border-left: 6px solid #eee; + + z-index: 100; + + > * { + display: flex; + flex: 0 0 auto; + order: 0; + } + + &.focused { + background-color: rgba(128, 128, 128, 0.1); + border-left-color: #ccc; + } + + .importantMark { + display: none; + + color:red; + margin-right:5px + } + + &.deleted-mark { + opacity: .7; + .subjectParent { + text-decoration: line-through; + } + } + + &.important .importantMark { + display: inline; + } + + &.new { + max-height: 0; + } + + &.deleted { + max-height: 0; + border-color: transparent !important; + } + + .checkboxMessage { + line-height: 12px; + padding: 0 8px 0 6px; + font-size: 16px; + } + + .flagParent { + padding: 0 10px 0 5px; + } + + time, .sizeParent { + margin: 0 5px; + opacity: 0.6; + font-size: 11px; + white-space: nowrap; + } + + .threadsParent { + position: relative; + } + + .attachmentParent { + position: relative; + margin: 2px 10px 0 5px; + color: #666; + text-shadow: 0 1px 0 #eee; + } + + .senderParent, .subjectParent { + overflow: hidden; + white-space: nowrap; + } + + .threadsCountParent { + display: inline; + overflow: hidden; + background-color: #eee; + padding: 1px 5px; + margin-right: 5px; + border: 1px solid #ccc; + border-radius: 5px; + } + + .threadsCountParent.lastSelected { + background-color: #999; + border-color: #999; + color: #fff; + } + + .threadsCountParent:hover { + border-color: #666; + } + + .subjectParent .emptySubjectText { + display: none; + font-style: italic; + opacity: 0.5; + } + + &.emptySubject .subjectParent { + .subject { + display: none; + } + .emptySubjectText { + display: inline; + } + } + + .sender, .subject { + margin: 0; + overflow: hidden; + text-overflow: ellipsis; + } + + .threads-len { + .threads-len-data { + + border-radius: 6px; + padding: 2px 0 1px 4px; + border: 1px solid #ccc; + + &:hover { + background-color: rgba(127,127,127,0.3); + border-color: #666; + } + } + } + + .flagOff { + opacity: 0.5; + &:hover { + opacity: 1; + } + } + + .flagOn, .flagOnHalf { + color: orange; + } + + .replyFlag, .forwardFlag { + display: none; + margin-right: 0.25em; + } + + &.answered .replyFlag { + display: inline-block; + } + + &.forwarded .forwardFlag { + display: inline-block; + } + + &:not(.withAttachments) .attachmentParent { + display: none; + } + + &.hasUnseenSubMessage { + background-color: rgba(255, 255, 64, 0.15); + border-left-color: lighten(orange, 30%); + &.focused { + border-left-color: darken(orange, 10%); + } + } + + &.unseen { + background-color: rgba(255, 255, 64, 0.15); + border-left-color: orange; + .sender, .subjectParent { + font-weight: bold; + } + + &.focused { + border-left-color: darken(orange, 10%); + } + } + + &.checked { + border-left-color: lighten(#398CF2, 10%); + + &.focused { + border-left-color: darken(#398CF2, 5%); + } + } + + &.selected { + background-color: rgba(140, 200, 255, 0.3); + border-bottom-color: rgba(57, 140, 242, 0.2); + border-left-color: #398CF2; + z-index: 101; + + + .messageListItem { + border-bottom-color: rgba(57, 140, 242, 0.3); + } + } + + &:not(.flagged) .flagOn, + &:not(.hasFlaggedSubMessage) .flagOnHalf, + &.flagged .flagOff, &.hasFlaggedSubMessage .flagOff { + display: none; + } +} + +html.rl-ctrl-key-pressed .messageListItem { + cursor: copy; +} + +@media screen and (min-width: 1000px) { + .messageList { + .listDragOver { + transition: all 400ms ease; } } } /* desktop-large */ @media screen and (min-width: 1401px) { - .messageList .messageListItem { + .messageListItem { font-size: 13px; time { font-size: 13px; @@ -448,18 +472,6 @@ html .messageList .line-loading { top: 0; } -.messageListItem { - - display: flex; - flex-wrap: wrap; - - > * { - display: flex; - flex: 0 0 auto; - order: 0; - } -} - .rl-side-preview-pane, .rl-mobile { .messageListItem { @@ -471,7 +483,6 @@ html .messageList .line-loading { .subjectParent { flex: 1 0 auto; line-height: 16px; - margin-left: 30px; order: 1; width: calc(100% - 120px); } @@ -492,9 +503,13 @@ html .messageList .line-loading { order: 3; } } + + .b-message-list-wrapper:not(.hideMessageListCheckbox) .subjectParent { + margin-left: 30px; + } } -html:not(.rl-mobile):not(.rl-side-preview-pane) .messageList { +html:not(.rl-mobile):not(.rl-side-preview-pane) { .messageListItem { diff --git a/dev/Styles/MessageView.less b/dev/Styles/User/MessageView.less similarity index 100% rename from dev/Styles/MessageView.less rename to dev/Styles/User/MessageView.less diff --git a/dev/Styles/OpenPgpKey.less b/dev/Styles/User/OpenPgpKey.less similarity index 95% rename from dev/Styles/OpenPgpKey.less rename to dev/Styles/User/OpenPgpKey.less index 0769472c4..3145dff6e 100644 --- a/dev/Styles/OpenPgpKey.less +++ b/dev/Styles/User/OpenPgpKey.less @@ -1,6 +1,6 @@ .b-open-pgp-key-view-content, .b-open-pgp-key-generate-content { &.modal { - width: 570px; + max-width: 570px; } } @@ -13,7 +13,7 @@ .b-compose-open-pgp-content { &.modal { - width: 800px; + max-width: 800px; } .key-list { @@ -97,7 +97,7 @@ .b-message-open-pgp-content { &.modal { - width: 700px; + max-width: 700px; } .key-list { @@ -134,7 +134,7 @@ .b-open-pgp-key-add-content { &.modal { - width: 645px; + max-width: 645px; } .inputKey { font-family: var(--fontMono); diff --git a/dev/Styles/Settings.less b/dev/Styles/User/Settings.less similarity index 100% rename from dev/Styles/Settings.less rename to dev/Styles/User/Settings.less diff --git a/dev/Styles/SettingsAccounts.less b/dev/Styles/User/SettingsAccounts.less similarity index 100% rename from dev/Styles/SettingsAccounts.less rename to dev/Styles/User/SettingsAccounts.less diff --git a/dev/Styles/SettingsFilters.less b/dev/Styles/User/SettingsFilters.less similarity index 100% rename from dev/Styles/SettingsFilters.less rename to dev/Styles/User/SettingsFilters.less diff --git a/dev/Styles/SettingsFolders.less b/dev/Styles/User/SettingsFolders.less similarity index 100% rename from dev/Styles/SettingsFolders.less rename to dev/Styles/User/SettingsFolders.less diff --git a/dev/Styles/SettingsGeneral.less b/dev/Styles/User/SettingsGeneral.less similarity index 100% rename from dev/Styles/SettingsGeneral.less rename to dev/Styles/User/SettingsGeneral.less diff --git a/dev/Styles/SettingsOpenPGP.less b/dev/Styles/User/SettingsOpenPGP.less similarity index 100% rename from dev/Styles/SettingsOpenPGP.less rename to dev/Styles/User/SettingsOpenPGP.less diff --git a/dev/Styles/SettingsTemplates.less b/dev/Styles/User/SettingsTemplates.less similarity index 100% rename from dev/Styles/SettingsTemplates.less rename to dev/Styles/User/SettingsTemplates.less diff --git a/dev/Styles/SettingsThemes.less b/dev/Styles/User/SettingsThemes.less similarity index 100% rename from dev/Styles/SettingsThemes.less rename to dev/Styles/User/SettingsThemes.less diff --git a/dev/Styles/Shortcuts.less b/dev/Styles/User/Shortcuts.less similarity index 74% rename from dev/Styles/Shortcuts.less rename to dev/Styles/User/Shortcuts.less index ce6fca195..dcf948638 100644 --- a/dev/Styles/Shortcuts.less +++ b/dev/Styles/User/Shortcuts.less @@ -1,7 +1,7 @@ .rl-view-model { .b-shortcuts-content { &.modal { - width: 700px; + max-width: 700px; } } } diff --git a/dev/Styles/SquireUI.less b/dev/Styles/User/SquireUI.less similarity index 100% rename from dev/Styles/SquireUI.less rename to dev/Styles/User/SquireUI.less diff --git a/dev/Styles/SystemDropDown.less b/dev/Styles/User/SystemDropDown.less similarity index 100% rename from dev/Styles/SystemDropDown.less rename to dev/Styles/User/SystemDropDown.less diff --git a/dev/Styles/Template.less b/dev/Styles/User/Template.less similarity index 80% rename from dev/Styles/Template.less rename to dev/Styles/User/Template.less index 331dae1cf..47dc0e3f7 100644 --- a/dev/Styles/Template.less +++ b/dev/Styles/User/Template.less @@ -1,7 +1,7 @@ .b-template-add-content { &.modal { - width: 750px; + max-width: 750px; } .e-template-place { diff --git a/dev/Styles/_BootstrapFix.less b/dev/Styles/_BootstrapFix.less index dad250ce5..b7d120736 100644 --- a/dev/Styles/_BootstrapFix.less +++ b/dev/Styles/_BootstrapFix.less @@ -51,19 +51,8 @@ label.inline, span.inline { color: #b94a48; } -.form-horizontal.long-label .control-group { - .control-label { - width: 160px; - } -} - /* mobile and tablet */ @media screen and (max-width: 999px) { - - .modal { - width: calc(100% - 20px); - } - .form-horizontal { .control-group { .control-label { diff --git a/dev/Styles/_FontasticToBoot.less b/dev/Styles/_FontasticToBoot.less index 1692a00e5..4eafc03a0 100644 --- a/dev/Styles/_FontasticToBoot.less +++ b/dev/Styles/_FontasticToBoot.less @@ -63,7 +63,8 @@ margin: -1px -2px; } -.icon-spinner:not(.not-animated) { +.icon-spinner:not(.not-animated), +.list-loading .icon-spinner.not-animated { border-color: transparent; border-top-color: #999; animation: rotation .8s infinite ease-in-out; diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index 787b57c65..df76e305e 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -86,7 +86,6 @@ export class MessageListMailBoxUserView extends AbstractViewRight { this.messageListCheckedOrSelected = MessageUserStore.listCheckedOrSelected; this.messageListCompleteLoadingThrottle = MessageUserStore.listCompleteLoading; - this.messageListCompleteLoadingThrottleForAnimation = MessageUserStore.listLoadingAnimation; initOnStartOrLangChange(() => this.emptySubjectValue = i18n('MESSAGE_LIST/EMPTY_SUBJECT_TEXT')); @@ -670,31 +669,31 @@ export class MessageListMailBoxUserView extends AbstractViewRight { } onBuild(dom) { - const eqs = (ev, s) => ev.target.closestWithin(s, dom); + const eqs = (ev, s) => ev.target.closestWithin('.messageList '+s, dom); this.selector.init(dom.querySelector('.b-content'), Scope.MessageList); dom.addEventListener('click', event => { ThemeStore.isMobile() && leftPanelDisabled(true); - if (eqs(event, '.messageList .b-message-list-wrapper') && Scope.MessageView === AppUserStore.focusedState()) { + if (eqs(event, '.b-message-list-wrapper') && Scope.MessageView === AppUserStore.focusedState()) { AppUserStore.focusedState(Scope.MessageList); } let el = eqs(event, '.e-paginator .e-page'); el && this.gotoPage(ko.dataFor(el)); - eqs(event, '.messageList .checkboxCheckAll') && this.checkAll(!this.checkAll()); + eqs(event, '.checkboxCheckAll') && this.checkAll(!this.checkAll()); - el = eqs(event, '.messageList .messageListItem .flagParent'); + el = eqs(event, '.messageListItem .flagParent'); el && this.flagMessages(ko.dataFor(el)); - el = eqs(event, '.messageList .messageListItem .threads-len'); + el = eqs(event, '.messageListItem .threads-len'); el && this.gotoThread(ko.dataFor(el)); }); dom.addEventListener('dblclick', event => { - let el = eqs(event, '.messageList .messageListItem .actionHandle'); + let el = eqs(event, '.messageListItem .actionHandle'); el && this.gotoThread(ko.dataFor(el)); }); diff --git a/package.json b/package.json index 2718780d4..cff731062 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "rimraf": "3.0.2" }, "dependencies": { + "gulp-group-css-media-queries": "^1.2.2", "gulp-rollup-2": "^1.2.1", "rollup": "^2.38.0", "rollup-plugin-external-globals": "^0.6.1", diff --git a/plugins/change-password/index.php b/plugins/change-password/index.php index eebd684a1..9c354c80b 100644 --- a/plugins/change-password/index.php +++ b/plugins/change-password/index.php @@ -6,9 +6,9 @@ class ChangePasswordPlugin extends \RainLoop\Plugins\AbstractPlugin { const NAME = 'Change Password', - VERSION = '2.1', - RELEASE = '2021-03-18', - REQUIRED = '2.4.0', + VERSION = '2.2', + RELEASE = '2021-07-20', + REQUIRED = '2.5.0', CATEGORY = 'Security', DESCRIPTION = 'This plugin allows you to change passwords of email accounts'; @@ -23,6 +23,7 @@ class ChangePasswordPlugin extends \RainLoop\Plugins\AbstractPlugin { $this->UseLangs(true); // start use langs folder + $this->addCss('style.less'); $this->addJs('js/ChangePasswordUserSettings.js'); // add js file $this->addJsonHook('ChangePassword', 'ChangePassword'); $this->addTemplate('templates/SettingsChangePassword.html'); diff --git a/plugins/change-password/style.less b/plugins/change-password/style.less new file mode 100644 index 000000000..09788f981 --- /dev/null +++ b/plugins/change-password/style.less @@ -0,0 +1,5 @@ +.form-horizontal.change-password .control-group { + .control-label { + width: 160px; + } +} diff --git a/plugins/change-password/templates/SettingsChangePassword.html b/plugins/change-password/templates/SettingsChangePassword.html index f16b1b7fe..ac0e62d20 100644 --- a/plugins/change-password/templates/SettingsChangePassword.html +++ b/plugins/change-password/templates/SettingsChangePassword.html @@ -1,5 +1,5 @@ <div class="b-settings-general g-ui-user-select-none"> - <div class="form-horizontal long-label"> + <div class="form-horizontal change-password"> <div class="legend" data-i18n="SETTINGS_CHANGE_PASSWORD/LEGEND_CHANGE_PASSWORD"></div> <div class="row"> <div class="span6"> diff --git a/snappymail/v/0.0.0/app/templates/Views/Common/Paginator.html b/snappymail/v/0.0.0/app/templates/Views/Common/Paginator.html index 5c98d9bce..902e37525 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Common/Paginator.html +++ b/snappymail/v/0.0.0/app/templates/Views/Common/Paginator.html @@ -1,5 +1,3 @@ <div class="e-paginator g-ui-user-select-none" data-bind="foreach: $data"> - <a class="e-page" data-bind="css: { 'current': current, 'custom': custom }, title: title"> - <span class="e-page-number" data-bind="text: name"></span> - </a> + <a class="e-page" data-bind="css: { 'current': current, 'custom': custom }, title: title, text: name"></a> </div> diff --git a/snappymail/v/0.0.0/app/templates/Views/Common/PopupsLanguages.html b/snappymail/v/0.0.0/app/templates/Views/Common/PopupsLanguages.html index b638fb3ed..22d9819b7 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Common/PopupsLanguages.html +++ b/snappymail/v/0.0.0/app/templates/Views/Common/PopupsLanguages.html @@ -3,12 +3,10 @@ <button type="button" class="close" data-bind="command: cancelCommand">×</button> <h3 data-i18n="POPUPS_LANGUAGES/TITLE_LANGUAGES"></h3> </div> - <div class="modal-body" style="min-height: 150px;"> - <div data-bind="foreach: languages"> - <label class="lang-item" data-bind="click: function () { $root.changeLanguage(key); }, css: {'selected': selected, 'user': user}, - attr: { title: $root.languageTooltipName(key) }, - text: fullName"> - </label> - </div> + <div class="modal-body" style="min-height: 150px;" data-bind="foreach: languages"> + <label data-bind="click: function () { $root.changeLanguage(key); }, css: {'selected': selected, 'user': user}, + attr: { title: $root.languageTooltipName(key) }, + text: fullName"> + </label> </div> </div> diff --git a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html index 840979dba..343edcb7b 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html @@ -1,257 +1,255 @@ -<div id="rl-sub-left"> - <div class="messageList g-ui-user-select-none" - data-bind="css: {'message-selected': isMessageSelected, 'message-focused': !messageListFocused(), 'hideMessageListCheckbox': !useCheckboxesInList() }"> - <div class="toolbar"> - <div class="btn-toolbar"> - <a class="btn btn-thin-2 fontastic show-mobile" data-bind="click: hideLeft, visible: !leftPanelDisabled()">❮</a> - <a class="btn btn-thin-2 fontastic show-mobile" data-bind="click: showLeft, visible: leftPanelDisabled()">❯</a> - <a class="btn buttonCompose show-mobile" data-bind="click: composeClick, css: {'btn-warning': composeInEdit, 'btn-success': !composeInEdit()}, visible: allowComposer && mobileCheckedStateHide()" data-i18n="[title]FOLDER_LIST/BUTTON_NEW_MESSAGE"> - <i class="icon-paper-plane"></i> +<div id="rl-sub-left" class="messageList g-ui-user-select-none" + data-bind="css: {'message-selected': isMessageSelected, 'message-focused': !messageListFocused() }"> + <div class="toolbar"> + <div class="btn-toolbar"> + <a class="btn btn-thin-2 fontastic show-mobile" data-bind="click: hideLeft, visible: !leftPanelDisabled()">❮</a> + <a class="btn btn-thin-2 fontastic show-mobile" data-bind="click: showLeft, visible: leftPanelDisabled()">❯</a> + <a class="btn buttonCompose show-mobile" data-bind="click: composeClick, css: {'btn-warning': composeInEdit, 'btn-success': !composeInEdit()}, visible: allowComposer && mobileCheckedStateHide()" data-i18n="[title]FOLDER_LIST/BUTTON_NEW_MESSAGE"> + <i class="icon-paper-plane"></i> + </a> + <a class="btn" data-bind="command: reloadCommand, visible: allowReload && mobileCheckedStateHide()" data-i18n="[title]MESSAGE_LIST/BUTTON_RELOAD"> + <i class="icon-spinner not-animated"></i> + </a> + <!-- ko if: !newMoveToFolder --> + <div class="btn-group dropdown colored-toggle hide-mobile" data-bind="visible: allowMessageListActions, registerBootstrapDropdown: true, openDropdownTrigger: moveDropdownTrigger"> + <a id="move-dropdown-id" href="#" tabindex="-1" class="btn dropdown-toggle buttonMove" data-toggle="dropdown" data-bind="command: moveCommand" data-i18n="[title]GLOBAL/MOVE_TO"> + <i class="icon-copy visible-on-ctrl-btn"></i> + <i class="fontastic hidden-on-ctrl-btn">📁</i> + <span class="caret"></span> </a> - <a class="btn buttonReload" data-bind="command: reloadCommand, visible: allowReload && mobileCheckedStateHide()" data-i18n="[title]MESSAGE_LIST/BUTTON_RELOAD"> - <i class="icon-spinner" data-bind="css: {'not-animated': !messageListCompleteLoadingThrottleForAnimation()}"></i> + <ul class="dropdown-menu g-ui-menu" role="menu" aria-labelledby="move-dropdown-id" role="menu" data-bind="foreach: folderMenuForMove"> + <li class="e-item" role="presentation" data-bind="css: { 'disabled': disabled, 'dividerbar': dividerbar }, click: function (mdata, oEvent) { if (!disabled) $root.moveSelectedMessagesToFolder(id, oEvent && !!oEvent.ctrlKey); }"> + <a class="e-link menuitem" href="#" tabindex="-1" data-bind="text: name"></a> + </li> + </ul> + </div> + <!-- /ko --> + <!-- ko if: newMoveToFolder --> + <div class="btn-group" data-bind="visible: allowMessageListActions && mobileCheckedStateShow()"> + <a id="move-dropdown-id" href="#" tabindex="-1" class="btn buttonMove" data-bind="command: moveNewCommand" data-i18n="[title]GLOBAL/MOVE_TO"> + <i class="icon-copy visible-on-ctrl-btn"></i> + <i class="fontastic hidden-on-ctrl-btn">📁</i> </a> - <!-- ko if: !newMoveToFolder --> - <div class="btn-group dropdown colored-toggle hide-mobile" data-bind="visible: allowMessageListActions, registerBootstrapDropdown: true, openDropdownTrigger: moveDropdownTrigger"> - <a id="move-dropdown-id" href="#" tabindex="-1" class="btn dropdown-toggle buttonMove" data-toggle="dropdown" data-bind="command: moveCommand" data-i18n="[title]GLOBAL/MOVE_TO"> - <i class="icon-copy visible-on-ctrl-btn"></i> - <i class="fontastic hidden-on-ctrl-btn">📁</i> + </div> + <!-- /ko --> + <div class="btn-group" data-bind="visible: allowMessageListActions && mobileCheckedStateShow()"> + <a class="btn button-archive" + data-bind="visible: isArchiveVisible, command: archiveCommand" data-i18n="[title]GLOBAL/TO_ARCHIVE"> + <i class="icon-archive"></i> + </a> + <a class="btn button-spam fontastic" data-bind="visible: isSpamVisible, command: spamCommand" data-i18n="[title]GLOBAL/SPAM">⚠</a> + <a class="btn button-not-spam" + data-bind="visible: isUnSpamVisible, command: notSpamCommand" data-i18n="[title]GLOBAL/NOT_SPAM"> + <i class="icon-check-mark-circle-two"></i> + </a> + <a class="btn button-delete fontastic" + data-bind="command: deleteCommand" data-i18n="[title]GLOBAL/DELETE">🗑</a> + </div> + <div class="btn-group dropdown colored-toggle" data-bind="visible: allowComposer || allowMessageListActions || allowDangerousActions, registerBootstrapDropdown: true, openDropdownTrigger: moreDropdownTrigger"> + <a id="more-list-dropdown-id" class="btn dropdown-toggle fontastic" href="#" tabindex="-1" data-toggle="dropdown" data-i18n="[title]GLOBAL/MORE">☰</a> + <ul class="dropdown-menu g-ui-menu" role="menu" aria-labelledby="more-list-dropdown-id"> + <div data-bind="visible: allowMessageListActions"> + <li class="e-item" role="presentation" data-bind="click: listUnsetSeen, css: {'disabled': !hasCheckedOrSelectedLines()}"> + <a class="e-link menuitem" href="#" tabindex="-1"> + <i class="icon-none"></i> + <span data-i18n="MESSAGE_LIST/MENU_UNSET_SEEN"></span> + </a> + </li> + <li class="e-item" role="presentation" data-bind="click: listSetSeen, css: {'disabled': !hasCheckedOrSelectedLines()}"> + <a class="e-link menuitem" href="#" tabindex="-1"> + <i class="icon-none"></i> + <span data-i18n="MESSAGE_LIST/MENU_SET_SEEN"></span> + </a> + </li> + <li class="e-item" role="presentation" data-bind="click: listSetFlags, css: {'disabled': !hasCheckedOrSelectedLines()}"> + <a class="e-link menuitem" href="#" tabindex="-1"> + <i class="fontastic flagged">★</i> + <span data-i18n="MESSAGE_LIST/MENU_SET_FLAG"></span> + </a> + </li> + <li class="e-item" role="presentation" data-bind="click: listUnsetFlags, css: {'disabled': !hasCheckedOrSelectedLines()}"> + <a class="e-link menuitem" href="#" tabindex="-1"> + <i class="fontastic unflagged">☆</i> + <span data-i18n="MESSAGE_LIST/MENU_UNSET_FLAG"></span> + </a> + </li> + <li class="e-item" role="presentation" data-bind="click: listSetAllSeen, css: {'disabled': !hasMessages()}"> + <a class="e-link menuitem" href="#" tabindex="-1"> + <i class="icon-none"></i> + <span data-i18n="MESSAGE_LIST/MENU_SET_ALL_SEEN"></span> + </a> + </li> + </div> + <li class="e-item dividerbar" role="presentation" data-bind="visible: allowComposer, command: multyForwardCommand"> + <a class="e-link menuitem" href="#" tabindex="-1"> + <i class="fontastic">↞</i> + <span data-i18n="MESSAGE_LIST/BUTTON_MULTY_FORWARD"></span> + </a> + </li> + <li class="e-item dividerbar" role="presentation" data-bind="visible: allowDangerousActions, command: deleteWithoutMoveCommand"> + <a class="e-link menuitem" href="#" tabindex="-1"> + <i class="fontastic">🗑</i> + <span data-i18n="MESSAGE_LIST/BUTTON_DELETE_WITHOUT_MOVE"></span> + </a> + </li> + <li class="e-item" role="presentation" data-bind="visible: allowDangerousActions, command: clearCommand"> + <a class="e-link menuitem" href="#" tabindex="-1"> + <i class="fontastic">🔥</i> + <span data-i18n="MESSAGE_LIST/BUTTON_EMPTY_FOLDER"></span> + </a> + </li> + </ul> + </div> + <div class="btn-group dropdown sortFolder" data-bind="visible: sortSupported() && mobileCheckedStateHide(), registerBootstrapDropdown: true, openDropdownTrigger: sortDropdownTrigger"> + <a id="sort-list-dropdown-id" class="btn dropdown-toggle fontastic" href="#" tabindex="-1" data-toggle="dropdown" data-i18n="[title]MESSAGE_LIST/SORT" data-bind="text: sortText">⬇</a> + <ul class="dropdown-menu g-ui-menu" role="menu" aria-labelledby="sort-list-dropdown-id"> + <li class="e-item" role="presentation" data-sort="DATE" data-bind="click: changeSort"> + <a class="e-link menuitem" href="#" tabindex="-1"> + <i class="fontastic">📅⬆</i> + <span data-i18n="MESSAGE_LIST/SORT_DATE_ASC"></span> + </a> + </li> + <li class="e-item" role="presentation" data-sort="" data-bind="click: changeSort"> + <a class="e-link menuitem" href="#" tabindex="-1"> + <i class="fontastic">📅⬇</i> + <span data-i18n="MESSAGE_LIST/SORT_DATE_DESC"></span> + </a> + </li> + <li class="e-item dividerbar" role="presentation" data-sort="SIZE" data-bind="click: changeSort"> + <a class="e-link menuitem" href="#" tabindex="-1"> + <i class="fontastic">✉⬆</i> + <span data-i18n="MESSAGE_LIST/SORT_SIZE_ASC"></span> + </a> + </li> + <li class="e-item" role="presentation" data-sort="REVERSE SIZE" data-bind="click: changeSort"> + <a class="e-link menuitem" href="#" tabindex="-1"> + <i class="fontastic">✉⬇</i> + <span data-i18n="MESSAGE_LIST/SORT_SIZE_DESC"></span> + </a> + </li> + <li class="e-item dividerbar" role="presentation" data-sort="SUBJECT" data-bind="click: changeSort"> + <a class="e-link menuitem" href="#" tabindex="-1"> + <i class="fontastic">𝐒⬇</i> + <span data-i18n="MESSAGE_LIST/SORT_SUBJECT_ASC"></span> + </a> + </li> + <li class="e-item" role="presentation" data-sort="REVERSE SUBJECT" data-bind="click: changeSort"> + <a class="e-link menuitem" href="#" tabindex="-1"> + <i class="fontastic">𝐒⬆</i> + <span data-i18n="MESSAGE_LIST/SORT_SUBJECT_DESC"></span> + </a> + </li> + <li class="e-item dividerbar" role="presentation" data-sort="FROM" data-bind="click: changeSort"> + <a class="e-link menuitem" href="#" tabindex="-1"> + <i class="fontastic">@⬇</i> + <span data-i18n="MESSAGE_LIST/SORT_FROM_ASC"></span> + </a> + </li> + <li class="e-item" role="presentation" data-sort="REVERSE FROM" data-bind="click: changeSort"> + <a class="e-link menuitem" href="#" tabindex="-1"> + <i class="fontastic">@⬆</i> + <span data-i18n="MESSAGE_LIST/SORT_FROM_DESC"></span> + </a> + </li> + </ul> + </div> + </div> + </div> + <div class="b-message-list-wrapper" data-bind="css: {'hideMessageListCheckbox': !useCheckboxesInList() }"> + <div class="second-toolbar thm-message-list-top-toolbar"> + <div class="form-inline"> + <i class="checkboxCheckAll fontastic" data-bind="text: checkAll() ? (isIncompleteChecked() ? '▣' : '☑') : '☐'"></i> + <div class="input-append" data-bind="visible: allowSearch"> + <div class="close-input-wrp"> + <a class="close" data-bind="click: cancelSearch, visible: '' !== messageListSearchDesc()">×</a> + <input type="search" class="span4 inputSearch" tabindex="-1" placeholder="Search" autocorrect="off" autocapitalize="off" data-i18n="[placeholder]GLOBAL/SEARCH" data-bind="value: inputProxyMessageListSearch, onEnter: searchEnterAction, hasfocus: inputMessageListSearchFocus" /> + </div> + <a class="btn buttonMoreSearch" data-bind="visible: allowSearchAdv, click: advancedSearchClick"> <span class="caret"></span> </a> - <ul class="dropdown-menu g-ui-menu" role="menu" aria-labelledby="move-dropdown-id" role="menu" data-bind="foreach: folderMenuForMove"> - <li class="e-item" role="presentation" data-bind="css: { 'disabled': disabled, 'dividerbar': dividerbar }, click: function (mdata, oEvent) { if (!disabled) $root.moveSelectedMessagesToFolder(id, oEvent && !!oEvent.ctrlKey); }"> - <a class="e-link menuitem" href="#" tabindex="-1" data-bind="text: name"></a> - </li> - </ul> - </div> - <!-- /ko --> - <!-- ko if: newMoveToFolder --> - <div class="btn-group" data-bind="visible: allowMessageListActions && mobileCheckedStateShow()"> - <a id="move-dropdown-id" href="#" tabindex="-1" class="btn buttonMove" data-bind="command: moveNewCommand" data-i18n="[title]GLOBAL/MOVE_TO"> - <i class="icon-copy visible-on-ctrl-btn"></i> - <i class="fontastic hidden-on-ctrl-btn">📁</i> - </a> - </div> - <!-- /ko --> - <div class="btn-group" data-bind="visible: allowMessageListActions && mobileCheckedStateShow()"> - <a class="btn button-archive" - data-bind="visible: isArchiveVisible, command: archiveCommand" data-i18n="[title]GLOBAL/TO_ARCHIVE"> - <i class="icon-archive"></i> - </a> - <a class="btn button-spam fontastic" data-bind="visible: isSpamVisible, command: spamCommand" data-i18n="[title]GLOBAL/SPAM">⚠</a> - <a class="btn button-not-spam" - data-bind="visible: isUnSpamVisible, command: notSpamCommand" data-i18n="[title]GLOBAL/NOT_SPAM"> - <i class="icon-check-mark-circle-two"></i> - </a> - <a class="btn button-delete fontastic" - data-bind="command: deleteCommand" data-i18n="[title]GLOBAL/DELETE">🗑</a> - </div> - <div class="btn-group dropdown colored-toggle" data-bind="visible: allowComposer || allowMessageListActions || allowDangerousActions, registerBootstrapDropdown: true, openDropdownTrigger: moreDropdownTrigger"> - <a id="more-list-dropdown-id" class="btn dropdown-toggle fontastic" href="#" tabindex="-1" data-toggle="dropdown" data-i18n="[title]GLOBAL/MORE">☰</a> - <ul class="dropdown-menu g-ui-menu" role="menu" aria-labelledby="more-list-dropdown-id"> - <div data-bind="visible: allowMessageListActions"> - <li class="e-item" role="presentation" data-bind="click: listUnsetSeen, css: {'disabled': !hasCheckedOrSelectedLines()}"> - <a class="e-link menuitem" href="#" tabindex="-1"> - <i class="icon-none"></i> - <span data-i18n="MESSAGE_LIST/MENU_UNSET_SEEN"></span> - </a> - </li> - <li class="e-item" role="presentation" data-bind="click: listSetSeen, css: {'disabled': !hasCheckedOrSelectedLines()}"> - <a class="e-link menuitem" href="#" tabindex="-1"> - <i class="icon-none"></i> - <span data-i18n="MESSAGE_LIST/MENU_SET_SEEN"></span> - </a> - </li> - <li class="e-item" role="presentation" data-bind="click: listSetFlags, css: {'disabled': !hasCheckedOrSelectedLines()}"> - <a class="e-link menuitem" href="#" tabindex="-1"> - <i class="fontastic flagged">★</i> - <span data-i18n="MESSAGE_LIST/MENU_SET_FLAG"></span> - </a> - </li> - <li class="e-item" role="presentation" data-bind="click: listUnsetFlags, css: {'disabled': !hasCheckedOrSelectedLines()}"> - <a class="e-link menuitem" href="#" tabindex="-1"> - <i class="fontastic unflagged">☆</i> - <span data-i18n="MESSAGE_LIST/MENU_UNSET_FLAG"></span> - </a> - </li> - <li class="e-item" role="presentation" data-bind="click: listSetAllSeen, css: {'disabled': !hasMessages()}"> - <a class="e-link menuitem" href="#" tabindex="-1"> - <i class="icon-none"></i> - <span data-i18n="MESSAGE_LIST/MENU_SET_ALL_SEEN"></span> - </a> - </li> - </div> - <li class="e-item dividerbar" role="presentation" data-bind="visible: allowComposer, command: multyForwardCommand"> - <a class="e-link menuitem" href="#" tabindex="-1"> - <i class="fontastic">↞</i> - <span data-i18n="MESSAGE_LIST/BUTTON_MULTY_FORWARD"></span> - </a> - </li> - <li class="e-item dividerbar" role="presentation" data-bind="visible: allowDangerousActions, command: deleteWithoutMoveCommand"> - <a class="e-link menuitem" href="#" tabindex="-1"> - <i class="fontastic">🗑</i> - <span data-i18n="MESSAGE_LIST/BUTTON_DELETE_WITHOUT_MOVE"></span> - </a> - </li> - <li class="e-item" role="presentation" data-bind="visible: allowDangerousActions, command: clearCommand"> - <a class="e-link menuitem" href="#" tabindex="-1"> - <i class="fontastic">🔥</i> - <span data-i18n="MESSAGE_LIST/BUTTON_EMPTY_FOLDER"></span> - </a> - </li> - </ul> - </div> - <div class="btn-group dropdown sortFolder" data-bind="visible: sortSupported() && mobileCheckedStateHide(), registerBootstrapDropdown: true, openDropdownTrigger: sortDropdownTrigger"> - <a id="sort-list-dropdown-id" class="btn dropdown-toggle fontastic" href="#" tabindex="-1" data-toggle="dropdown" data-i18n="[title]MESSAGE_LIST/SORT" data-bind="text: sortText">⬇</a> - <ul class="dropdown-menu g-ui-menu" role="menu" aria-labelledby="sort-list-dropdown-id"> - <li class="e-item" role="presentation" data-sort="DATE" data-bind="click: changeSort"> - <a class="e-link menuitem" href="#" tabindex="-1"> - <i class="fontastic">📅⬆</i> - <span data-i18n="MESSAGE_LIST/SORT_DATE_ASC"></span> - </a> - </li> - <li class="e-item" role="presentation" data-sort="" data-bind="click: changeSort"> - <a class="e-link menuitem" href="#" tabindex="-1"> - <i class="fontastic">📅⬇</i> - <span data-i18n="MESSAGE_LIST/SORT_DATE_DESC"></span> - </a> - </li> - <li class="e-item dividerbar" role="presentation" data-sort="SIZE" data-bind="click: changeSort"> - <a class="e-link menuitem" href="#" tabindex="-1"> - <i class="fontastic">✉⬆</i> - <span data-i18n="MESSAGE_LIST/SORT_SIZE_ASC"></span> - </a> - </li> - <li class="e-item" role="presentation" data-sort="REVERSE SIZE" data-bind="click: changeSort"> - <a class="e-link menuitem" href="#" tabindex="-1"> - <i class="fontastic">✉⬇</i> - <span data-i18n="MESSAGE_LIST/SORT_SIZE_DESC"></span> - </a> - </li> - <li class="e-item dividerbar" role="presentation" data-sort="SUBJECT" data-bind="click: changeSort"> - <a class="e-link menuitem" href="#" tabindex="-1"> - <i class="fontastic">𝐒⬇</i> - <span data-i18n="MESSAGE_LIST/SORT_SUBJECT_ASC"></span> - </a> - </li> - <li class="e-item" role="presentation" data-sort="REVERSE SUBJECT" data-bind="click: changeSort"> - <a class="e-link menuitem" href="#" tabindex="-1"> - <i class="fontastic">𝐒⬆</i> - <span data-i18n="MESSAGE_LIST/SORT_SUBJECT_DESC"></span> - </a> - </li> - <li class="e-item dividerbar" role="presentation" data-sort="FROM" data-bind="click: changeSort"> - <a class="e-link menuitem" href="#" tabindex="-1"> - <i class="fontastic">@⬇</i> - <span data-i18n="MESSAGE_LIST/SORT_FROM_ASC"></span> - </a> - </li> - <li class="e-item" role="presentation" data-sort="REVERSE FROM" data-bind="click: changeSort"> - <a class="e-link menuitem" href="#" tabindex="-1"> - <i class="fontastic">@⬆</i> - <span data-i18n="MESSAGE_LIST/SORT_FROM_DESC"></span> - </a> - </li> - </ul> </div> </div> </div> - <div class="b-message-list-wrapper"> - <div class="second-toolbar thm-message-list-top-toolbar"> - <div class="form-inline"> - <i class="checkboxCheckAll fontastic" data-bind="text: checkAll() ? (isIncompleteChecked() ? '▣' : '☑') : '☐'"></i> - <div class="input-append" data-bind="visible: allowSearch"> - <div class="close-input-wrp"> - <a class="close" data-bind="click: cancelSearch, visible: '' !== messageListSearchDesc()">×</a> - <input type="search" class="span4 inputSearch" tabindex="-1" placeholder="Search" autocorrect="off" autocapitalize="off" data-i18n="[placeholder]GLOBAL/SEARCH" data-bind="value: inputProxyMessageListSearch, onEnter: searchEnterAction, hasfocus: inputMessageListSearchFocus" /> - </div> - <a class="btn buttonMoreSearch" data-bind="visible: allowSearchAdv, click: advancedSearchClick"> - <span class="caret"></span> - </a> - </div> - </div> + <div class="b-content" data-bind="initDom: dragOverBodyArea"> + <div class="listThreadUidDesc" data-bind="visible: '' !== messageListEndThreadUid(), click: cancelThreadUid"> + <i class="fontastic" data-bind="click: cancelThreadUid">⬅</i> + <span data-i18n="MESSAGE_LIST/BACK_TO_MESSAGE_LIST"></span> </div> - <div class="b-content" data-bind="initDom: dragOverBodyArea"> - <div class="listThreadUidDesc" data-bind="visible: '' !== messageListEndThreadUid(), click: cancelThreadUid"> - <i class="fontastic" data-bind="click: cancelThreadUid">⬅</i> - <span data-i18n="MESSAGE_LIST/BACK_TO_MESSAGE_LIST"></span> - </div> - <div class="listSearchDesc" data-bind="visible: '' !== messageListSearchDesc(), text: messageListSearchDesc"></div> - <div class="listDragOver" data-bind="css: {'viewAppendArea': dragOver() && '' === messageListError() && !popupVisibility(), 'dragOverEnter': dragOverEnter }, initDom: dragOverArea"> - <i class="fontastic e-icon">⬇</i> - <span data-i18n="MESSAGE_LIST/PUT_MESSAGE_HERE"></span> - </div> - <div class="listClear" data-bind="visible: clearListIsVisible()"> - <span class="g-ui-link" data-i18n="MESSAGE_LIST/BUTTON_EMPTY_FOLDER" data-bind="command: clearCommand"></span> - </div> - <div class="listError" data-bind="visible: !dragOver() && '' !== messageListError()"> - <span class="error" data-bind="text: messageListError"></span> - </div> - <div class="listEmptyList" data-bind="visible: !dragOver() && 0 === messageList().length && !messageListCompleteLoadingThrottle() && '' === messageListError() && '' === messageListSearch()" - data-i18n="MESSAGE_LIST/EMPTY_LIST"></div> - <div class="listEmptyListLoading" data-bind="visible: !dragOver() && 0 === messageList().length && - messageListCompleteLoadingThrottle() && '' === messageListError()"> - <span data-i18n="GLOBAL/LOADING"></span> - <span class="textLoadingAnimationD1">.</span> - <span class="textLoadingAnimationD2">.</span> - <span class="textLoadingAnimationD3">.</span> - </div> - <div class="listEmptySearchList" data-bind="visible: !dragOver() && 0 === messageList().length && !messageListCompleteLoadingThrottle() && '' === messageListError() && '' !== messageListSearch()" - data-i18n="MESSAGE_LIST/EMPTY_SEARCH_LIST"></div> - <div data-bind="dragmessages: getDragData"> - <div class="messageListPlace" data-bind="foreach: messageList"> + <div class="listSearchDesc" data-bind="visible: '' !== messageListSearchDesc(), text: messageListSearchDesc"></div> + <div class="listDragOver" data-bind="css: {'viewAppendArea': dragOver() && '' === messageListError() && !popupVisibility(), 'dragOverEnter': dragOverEnter }, initDom: dragOverArea"> + <i class="fontastic e-icon">⬇</i> + <span data-i18n="MESSAGE_LIST/PUT_MESSAGE_HERE"></span> + </div> + <div class="listClear" data-bind="visible: clearListIsVisible()"> + <span class="g-ui-link" data-i18n="MESSAGE_LIST/BUTTON_EMPTY_FOLDER" data-bind="command: clearCommand"></span> + </div> + <div class="listError" data-bind="visible: !dragOver() && '' !== messageListError()"> + <span class="error" data-bind="text: messageListError"></span> + </div> + <div class="listEmptyList" data-bind="visible: !dragOver() && 0 === messageList().length && !messageListCompleteLoadingThrottle() && '' === messageListError() && '' === messageListSearch()" + data-i18n="MESSAGE_LIST/EMPTY_LIST"></div> + <div class="listEmptyListLoading" data-bind="visible: !dragOver() && 0 === messageList().length && + messageListCompleteLoadingThrottle() && '' === messageListError()"> + <span data-i18n="GLOBAL/LOADING"></span> + <span class="textLoadingAnimationD1">.</span> + <span class="textLoadingAnimationD2">.</span> + <span class="textLoadingAnimationD3">.</span> + </div> + <div class="listEmptySearchList" data-bind="visible: !dragOver() && 0 === messageList().length && !messageListCompleteLoadingThrottle() && '' === messageListError() && '' !== messageListSearch()" + data-i18n="MESSAGE_LIST/EMPTY_SEARCH_LIST"></div> + <div data-bind="dragmessages: getDragData"> + <div class="messageListPlace" data-bind="foreach: messageList"> - <div class="messageListItem" data-bind="css: lineAsCss()"> - <div class="checkboxMessage fontastic" data-bind="text: checked() ? '☑' : '☐'"></div> + <div class="messageListItem" data-bind="css: lineAsCss()"> + <div class="checkboxMessage fontastic" data-bind="text: checked() ? '☑' : '☐'"></div> - <div class="flagParent"> - <i class="flagOn fontastic">★</i> - <i class="flagOnHalf fontastic">☆</i> - <i class="flagOff fontastic">☆</i> - <!-- ⚐⚑ --> - </div> + <div class="flagParent"> + <i class="flagOn fontastic">★</i> + <i class="flagOnHalf fontastic">☆</i> + <i class="flagOff fontastic">☆</i> + <!-- ⚐⚑ --> + </div> - <div class="senderParent actionHandle"> - <i class="replyFlag fontastic">←</i> - <i class="forwardFlag fontastic">→</i> - <span class="sender" data-bind="text: senderEmailsString, attr: {'title': senderClearEmailsString}"></span> - </div> + <div class="senderParent actionHandle"> + <i class="replyFlag fontastic">←</i> + <i class="forwardFlag fontastic">→</i> + <span class="sender" data-bind="text: senderEmailsString, attr: {'title': senderClearEmailsString}"></span> + </div> - <div class="attachmentParent actionHandle"> - <i data-bind="css: attachmentIconClass"></i> - </div> + <div class="attachmentParent actionHandle"> + <i data-bind="css: attachmentIconClass"></i> + </div> - <div class="subjectParent actionHandle"> - <b class="importantMark">!</b> - <span class="emptySubjectText" data-bind="text: $root.emptySubjectValue"></span> - <span class="subject" data-bind="text: subject"></span> - </div> + <div class="subjectParent actionHandle"> + <b class="importantMark">!</b> + <span class="emptySubjectText" data-bind="text: $root.emptySubjectValue"></span> + <span class="subject" data-bind="text: subject"></span> + </div> - <div class="sizeParent actionHandle" data-bind="text: friendlySize()"></div> + <div class="sizeParent actionHandle" data-bind="text: friendlySize()"></div> - <div class="threadsParent" data-bind="visible: 1 < threadsLen()"> - <span class="threads-len"> - <span class="threads-len-data"> - <span data-bind="text: threadsLen"></span> - <i class="icon-right-mini"></i> - </span> + <div class="threadsParent" data-bind="visible: 1 < threadsLen()"> + <span class="threads-len"> + <span class="threads-len-data"> + <span data-bind="text: threadsLen"></span> + <i class="icon-right-mini"></i> </span> - </div> - - <time class="actionHandle" data-moment-format="SHORT" data-moment-format-title="FULL" data-bind="moment: dateTimeStampInUTC"></time> + </span> </div> + <time class="actionHandle" data-moment-format="SHORT" data-moment-format-title="FULL" data-bind="moment: dateTimeStampInUTC"></time> </div> + </div> - <div id="messagesDragImage"><span class="text"></span> <i class="icon-mail"></i></div> </div> - <div class="b-footer thm-message-list-bottom-toolbar"> - <span data-bind="visible: 0 < userUsageProc(), attr: { title: quotaTooltip() }" class="e-quota"> - <span data-bind="text: userUsageProc"></span>% - </span> - <div class="pull-right"> - <!-- ko template: { name: 'Paginator', data: messageListPaginator } --><!-- /ko --> - </div> + <div id="messagesDragImage"><span class="text"></span> <i class="icon-mail"></i></div> + </div> + <div class="b-footer thm-message-list-bottom-toolbar"> + <span data-bind="visible: 0 < userUsageProc(), attr: { title: quotaTooltip() }" class="e-quota"> + <span data-bind="text: userUsageProc"></span>% + </span> + <div class="pull-right"> + <!-- ko template: { name: 'Paginator', data: messageListPaginator } --><!-- /ko --> </div> </div> </div> diff --git a/snappymail/v/0.0.0/themes/SquaresDark/styles.less b/snappymail/v/0.0.0/themes/SquaresDark/styles.less index 2204029fc..b24e1e61e 100644 --- a/snappymail/v/0.0.0/themes/SquaresDark/styles.less +++ b/snappymail/v/0.0.0/themes/SquaresDark/styles.less @@ -121,7 +121,7 @@ border-bottom-color: #444; } -.messageList .messageListItem { +.messageListItem { border-left-color: #111; color: #EEE; } diff --git a/tasks/css.js b/tasks/css.js index c9691d511..955750e6b 100644 --- a/tasks/css.js +++ b/tasks/css.js @@ -8,6 +8,7 @@ const eol = require('gulp-eol'); const livereload = require('gulp-livereload'); const filter = require('gulp-filter'); const expect = require('gulp-expect-file'); +const gcmq = require('gulp-group-css-media-queries'); const { config } = require('./config'); const { del } = require('./common'); @@ -85,6 +86,7 @@ const cssBootMin = () => { const cssMainMin = () => { return gulp .src(config.paths.staticCSS + config.paths.css.main.name) + .pipe(gcmq()) .pipe(cleanCss()) .pipe(rename({ suffix: '.min' })) .pipe(eol('\n', true)) @@ -94,6 +96,7 @@ const cssMainMin = () => { const cssAdminMin = () => { return gulp .src(config.paths.staticCSS + config.paths.css.admin.name) + .pipe(gcmq()) .pipe(cleanCss()) .pipe(rename({ suffix: '.min' })) .pipe(eol('\n', true)) diff --git a/vendors/bootstrap/less/forms.less b/vendors/bootstrap/less/forms.less index 8e16d3dcc..fe643998a 100644 --- a/vendors/bootstrap/less/forms.less +++ b/vendors/bootstrap/less/forms.less @@ -84,6 +84,7 @@ textarea { // Reset height since textareas have rows textarea { height: auto; + resize: none; } // Everything else textarea, diff --git a/vendors/bootstrap/less/modals.less b/vendors/bootstrap/less/modals.less index 8ccd2b203..91ea86aaa 100644 --- a/vendors/bootstrap/less/modals.less +++ b/vendors/bootstrap/less/modals.less @@ -8,7 +8,8 @@ position: relative; z-index: @zindexModal; overflow: hidden; - width: 560px; + width: calc(100% - 20px); + max-width: 560px; margin: 10px auto; background-color: @white; border: 1px solid rgba(0,0,0,.3); diff --git a/yarn.lock b/yarn.lock index 6ed1d843d..1b4fc8d05 100644 --- a/yarn.lock +++ b/yarn.lock @@ -871,6 +871,30 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" +css-parse@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/css-parse/-/css-parse-2.0.0.tgz#a468ee667c16d81ccf05c58c38d2a97c780dbfd4" + integrity sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q= + dependencies: + css "^2.0.0" + +css-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/css-stringify/-/css-stringify-2.0.0.tgz#2ef33dcf498c3d3ee82bd73d0a01ac28628cd0fa" + integrity sha1-LvM9z0mMPT7oK9c9CgGsKGKM0Po= + dependencies: + css "^2.0.0" + +css@^2.0.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" + integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw== + dependencies: + inherits "^2.0.3" + source-map "^0.6.1" + source-map-resolve "^0.5.2" + urix "^0.1.0" + d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" @@ -1603,6 +1627,14 @@ graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== +group-css-media-queries@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/group-css-media-queries/-/group-css-media-queries-1.4.1.tgz#8e54cb31cc45a9c5e08eb838940b8a28038d7175" + integrity sha1-jlTLMcxFqcXgjrg4lAuKKAONcXU= + dependencies: + css-parse "^2.0.0" + css-stringify "^2.0.0" + gulp-cached@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/gulp-cached/-/gulp-cached-1.1.1.tgz#fe7cd4f87f37601e6073cfedee5c2bdaf8b6acce" @@ -1697,6 +1729,15 @@ gulp-filter@6.0.0: plugin-error "^1.0.1" streamfilter "^3.0.0" +gulp-group-css-media-queries@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/gulp-group-css-media-queries/-/gulp-group-css-media-queries-1.2.2.tgz#c0436b9474086cbe1ab3eb5bc1a3afef4d6b37d6" + integrity sha512-uOs8aJ5TV12aRstfc6pn107edDT2XSY7KNAU721Dz+gKHu/AUIlAaOdFdo9n/eHWqfb9QWMArrM8fiPOCqZPGg== + dependencies: + group-css-media-queries "^1.4.1" + plugin-error "^1.0.1" + through2 "^2.0.3" + gulp-header@2.0.9: version "2.0.9" resolved "https://registry.yarnpkg.com/gulp-header/-/gulp-header-2.0.9.tgz#8b432c4d4379dee6788845b16785b09c7675af84" @@ -3695,7 +3736,7 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -source-map-resolve@^0.5.0: +source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== From 18cedbabad8fcce0a3a3b6209cebfc8b6f69c006 Mon Sep 17 00:00:00 2001 From: djmaze <djmaze@djmaze.lan> Date: Tue, 20 Jul 2021 16:49:03 +0200 Subject: [PATCH 19/26] Simplify UserBackground code --- dev/App/User.js | 11 ----------- dev/Common/Links.js | 8 -------- dev/Settings/User/Themes.js | 14 ++------------ dev/Stores/Theme.js | 13 ++++++++++++- 4 files changed, 14 insertions(+), 32 deletions(-) diff --git a/dev/App/User.js b/dev/App/User.js index b055b043c..2d0802b08 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -38,7 +38,6 @@ import { } from 'Common/Cache'; import { - userBackground, mailBox, root, openPgpWorkerJs, @@ -112,16 +111,6 @@ class AppUser extends AbstractApp { lastTime = currentTime; }, interval); - if (SettingsGet('UserBackgroundHash')) { - setTimeout(() => { - const img = userBackground(SettingsGet('UserBackgroundHash')); - if (img) { - $htmlCL.add('UserBackground'); - doc.body.style.backgroundImage = "url("+img+")"; - } - }, 1000); - } - const fn = (ev=>$htmlCL.toggle('rl-ctrl-key-pressed', ev.ctrlKey)).debounce(500); ['keydown','keyup'].forEach(t => doc.addEventListener(t, fn)); diff --git a/dev/Common/Links.js b/dev/Common/Links.js index dc726351f..b5edfa99f 100644 --- a/dev/Common/Links.js +++ b/dev/Common/Links.js @@ -70,14 +70,6 @@ export function change(email) { return serverRequest('Change') + encodeURIComponent(email) + '/'; } -/** - * @param {string} hash - * @returns {string} - */ -export function userBackground(hash) { - return serverRequestRaw('UserBackground', hash); -} - /** * @param {string} lang * @param {boolean} isAdmin diff --git a/dev/Settings/User/Themes.js b/dev/Settings/User/Themes.js index bc5d9ee69..2a0b82531 100644 --- a/dev/Settings/User/Themes.js +++ b/dev/Settings/User/Themes.js @@ -2,9 +2,9 @@ import ko from 'ko'; import { SaveSettingsStep, UploadErrorCode, Capa } from 'Common/Enums'; import { changeTheme, convertThemeName } from 'Common/Utils'; -import { userBackground, themePreviewLink, serverRequest } from 'Common/Links'; +import { themePreviewLink, serverRequest } from 'Common/Links'; import { i18n } from 'Common/Translator'; -import { doc, $htmlCL, Settings } from 'Common/Globals'; +import { Settings } from 'Common/Globals'; import { ThemeStore } from 'Stores/Theme'; @@ -38,16 +38,6 @@ export class ThemesUserSettings { Theme: value }); }); - - this.background.hash.subscribe((value) => { - if (!value) { - $htmlCL.remove('UserBackground'); - doc.body.removeAttribute('style'); - } else { - $htmlCL.add('UserBackground'); - doc.body.style.backgroundImage = "url("+userBackground(value)+")"; - } - }); } onBuild() { diff --git a/dev/Stores/Theme.js b/dev/Stores/Theme.js index 3a11d63c9..95f17f13a 100644 --- a/dev/Stores/Theme.js +++ b/dev/Stores/Theme.js @@ -1,6 +1,7 @@ import ko from 'ko'; -import { $htmlCL, leftPanelDisabled, Settings, SettingsGet } from 'Common/Globals'; +import { doc, $htmlCL, leftPanelDisabled, Settings, SettingsGet } from 'Common/Globals'; import { isArray } from 'Common/Utils'; +import { serverRequestRaw } from 'Common/Links'; export const ThemeStore = { themes: ko.observableArray(), @@ -25,3 +26,13 @@ export const ThemeStore = { ThemeStore.theme = ko.observable('').extend({ limitedList: ThemeStore.themes }); ThemeStore.isMobile.subscribe(value => $htmlCL.toggle('rl-mobile', value)); + +ThemeStore.userBackgroundHash.subscribe(value => { + if (value) { + $htmlCL.add('UserBackground'); + doc.body.style.backgroundImage = "url("+serverRequestRaw('UserBackground', value)+")"; + } else { + $htmlCL.remove('UserBackground'); + doc.body.removeAttribute('style'); + } +}); From 146f76ad9f68d9729a600d503154026a29796a7b Mon Sep 17 00:00:00 2001 From: djmaze <djmaze@djmaze.lan> Date: Tue, 20 Jul 2021 16:54:32 +0200 Subject: [PATCH 20/26] Resolve Issue #101 --- snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Helper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Helper.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Helper.php index 8776eab83..3df235db9 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Helper.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Helper.php @@ -4,7 +4,7 @@ namespace RainLoop\Plugins; class Helper { - static public function ValidateWildcardValues(string $sString, string $sWildcardValues, string &$sFoundedValue) : bool + static public function ValidateWildcardValues(string $sString, string $sWildcardValues, string &$sFoundedValue = null) : bool { $sFoundedValue = ''; From f57955443bcd2f28abf5721eb8935ab7dba731a5 Mon Sep 17 00:00:00 2001 From: djmaze <djmaze@djmaze.lan> Date: Tue, 20 Jul 2021 20:29:47 +0200 Subject: [PATCH 21/26] Cleanup bootstrap css --- dev/Styles/User/Contacts.less | 28 ++++--------------- dev/Styles/_BootstrapFix.less | 8 +++--- .../templates/Views/User/PopupsContacts.html | 4 +-- vendors/bootstrap/less/button-groups.less | 25 ----------------- vendors/bootstrap/less/buttons.less | 9 ------ vendors/bootstrap/less/dropdowns.less | 8 ------ vendors/bootstrap/less/forms.less | 6 ---- vendors/bootstrap/less/mixins.less | 3 -- vendors/bootstrap/less/modals.less | 4 ++- vendors/bootstrap/less/tables.less | 6 ---- 10 files changed, 14 insertions(+), 87 deletions(-) diff --git a/dev/Styles/User/Contacts.less b/dev/Styles/User/Contacts.less index 605d0354e..7c8f1c4c1 100644 --- a/dev/Styles/User/Contacts.less +++ b/dev/Styles/User/Contacts.less @@ -1,7 +1,7 @@ -@contacts-popup-left-width: 220px; +@contacts-popup-left-width: 25%; -.b-contacts-content { +.RL-PopupsContacts { .control-group { .control-label { @@ -10,11 +10,8 @@ } } - &.modal { - position: absolute; - right: 0; + .modal { bottom: 0; - left: 0; width: auto; min-width: 550px; max-width: 900px; @@ -36,8 +33,8 @@ border-bottom: 1px solid rgba(128,128,128,0.4); .e-search { - margin-top: 7px; - width: @contacts-popup-left-width - 20; + margin: 7px 0 0; + width: calc(100% - 20px); } } @@ -268,18 +265,3 @@ } } } - - -@media screen and (max-width: 700px) { - .b-contacts-content { - .b-list-content, .b-list-toolbar, .b-list-footer-toolbar { - width: 170px; - } - .b-list-toolbar .e-search { - width: 150px; - } - .b-view-content-toolbar, .b-view-content { - left: 170px; - } - } -} diff --git a/dev/Styles/_BootstrapFix.less b/dev/Styles/_BootstrapFix.less index b7d120736..956cda713 100644 --- a/dev/Styles/_BootstrapFix.less +++ b/dev/Styles/_BootstrapFix.less @@ -12,7 +12,7 @@ label.inline, span.inline { line-height: @baseLineHeight * 2; color: @grayDark; border: 0; - border-bottom: 1px solid #e5e5e5; + border-bottom: 1px solid @dropdownDividerTop; small { font-size: @baseLineHeight * .75; @@ -40,15 +40,15 @@ label.inline, span.inline { .alert a { text-decoration: underline; - color: #c09853; + color: @warningText; } .alert.alert-info a { - color: #3a87ad; + color: @infoText; } .alert.alert-error a { - color: #b94a48; + color: @errorText; } /* mobile and tablet */ diff --git a/snappymail/v/0.0.0/app/templates/Views/User/PopupsContacts.html b/snappymail/v/0.0.0/app/templates/Views/User/PopupsContacts.html index c1bea9e03..455552bda 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/PopupsContacts.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/PopupsContacts.html @@ -1,4 +1,4 @@ -<div class="modal fade b-contacts-content" data-bind="modal: modalVisibility"> +<div class="modal fade"> <div class="modal-header b-header-toolbar g-ui-user-select-none"> <button type="button" class="close" data-bind="command: cancelCommand">×</button> @@ -48,7 +48,7 @@ </div> <div class="modal-body"> <div class="b-list-toolbar"> - <input type="search" class="span3 e-search" placeholder="Search" autocorrect="off" autocapitalize="off" data-i18n="[placeholder]GLOBAL/SEARCH" data-bind="value: search" /> + <input type="search" class="e-search" placeholder="Search" autocorrect="off" autocapitalize="off" data-i18n="[placeholder]GLOBAL/SEARCH" data-bind="value: search" /> </div> <div class="b-list-content g-ui-user-select-none" data-bind="css: {'hideContactListCheckbox': !useCheckboxesInList()}"> <div class="content"> diff --git a/vendors/bootstrap/less/button-groups.less b/vendors/bootstrap/less/button-groups.less index c17b30b2b..9e9abdaf3 100644 --- a/vendors/bootstrap/less/button-groups.less +++ b/vendors/bootstrap/less/button-groups.less @@ -45,9 +45,6 @@ .btn-group > .btn-small { font-size: 12px; } -.btn-group > .btn-large { - font-size: 16px; -} // Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match .btn-group > .btn:first-child { @@ -61,17 +58,6 @@ border-top-right-radius: 3px; border-bottom-right-radius: 3px; } -// Reset corners for large buttons -.btn-group > .btn.large:first-child { - margin-left: 0; - border-top-left-radius: 6px; - border-bottom-left-radius: 6px; -} -.btn-group > .btn.large:last-child, -.btn-group > .large.dropdown-toggle { - border-top-right-radius: 6px; - border-bottom-right-radius: 6px; -} // On hover/focus/active, bring the proper btn to front .btn-group > .btn:hover, @@ -84,17 +70,6 @@ // Split button dropdowns // ---------------------- -// Give the line between buttons some depth -.btn-group > .btn + .dropdown-toggle { - padding-left: 8px; - padding-right: 8px; - box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); -} -.btn-group > .btn-large + .dropdown-toggle { - padding-left: 12px; - padding-right: 12px; -} - .btn-group.open { // The clickable button for toggling the menu diff --git a/vendors/bootstrap/less/buttons.less b/vendors/bootstrap/less/buttons.less index 03e6f4822..56bb8de24 100644 --- a/vendors/bootstrap/less/buttons.less +++ b/vendors/bootstrap/less/buttons.less @@ -65,9 +65,6 @@ line-height: normal; border-radius: 5px; } -.btn-large [class^="icon-"] { - margin-top: 2px; -} // Small .btn-small { @@ -75,9 +72,6 @@ font-size: @baseFontSize - 2px; line-height: @baseLineHeight - 2px; } -.btn-small [class^="icon-"] { - margin-top: 0; -} // Block button .btn-block { @@ -87,9 +81,6 @@ padding-right: 0; box-sizing: border-box; } -.btn-block + .btn-block { - margin-top: 5px; -} // Alternate buttons diff --git a/vendors/bootstrap/less/dropdowns.less b/vendors/bootstrap/less/dropdowns.less index 8154a6bc8..d74ca8f2f 100644 --- a/vendors/bootstrap/less/dropdowns.less +++ b/vendors/bootstrap/less/dropdowns.less @@ -92,11 +92,3 @@ background-color: transparent; cursor: default; } - -// Open state for the dropdown -// --------------------------- -.open { - & > .dropdown-menu { - display: block; - } -} diff --git a/vendors/bootstrap/less/forms.less b/vendors/bootstrap/less/forms.less index fe643998a..4b57af11b 100644 --- a/vendors/bootstrap/less/forms.less +++ b/vendors/bootstrap/less/forms.less @@ -29,12 +29,6 @@ legend { color: @grayDark; border: 0; border-bottom: 1px solid #e5e5e5; - - // Small - small { - font-size: @baseLineHeight * .75; - color: @grayLight; - } } // Identify controls by their labels diff --git a/vendors/bootstrap/less/mixins.less b/vendors/bootstrap/less/mixins.less index 0acc7292d..3f1c24509 100644 --- a/vendors/bootstrap/less/mixins.less +++ b/vendors/bootstrap/less/mixins.less @@ -104,9 +104,6 @@ vertical-align: top; } - // Set the container width - .container { .span(@gridColumns); } - // generate .spanX .spanX (@gridColumns); } diff --git a/vendors/bootstrap/less/modals.less b/vendors/bootstrap/less/modals.less index 91ea86aaa..46d3cd59a 100644 --- a/vendors/bootstrap/less/modals.less +++ b/vendors/bootstrap/less/modals.less @@ -5,7 +5,7 @@ // Base modal .modal { - position: relative; + position: absolute; z-index: @zindexModal; overflow: hidden; width: calc(100% - 20px); @@ -17,6 +17,8 @@ box-shadow: 0 5px 80px rgba(0,0,0,0.3); background-clip: padding-box; top: 0; + left: 0; + right: 0; } .modal-header { diff --git a/vendors/bootstrap/less/tables.less b/vendors/bootstrap/less/tables.less index 3f4203c43..b70946911 100644 --- a/vendors/bootstrap/less/tables.less +++ b/vendors/bootstrap/less/tables.less @@ -12,12 +12,6 @@ background-color: @tableBackground; border-collapse: collapse; border-spacing: 0; -} - -// BASELINE STYLES -// --------------- - -.table { width: 100%; margin-bottom: @baseLineHeight; // Cells From 5ef2b6daa30ec45691e8b0781df3d7d8eaab1ffd Mon Sep 17 00:00:00 2001 From: djmaze <djmaze@djmaze.lan> Date: Tue, 20 Jul 2021 20:46:47 +0200 Subject: [PATCH 22/26] drop unused small in bootstrap --- dev/Styles/_BootstrapFix.less | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dev/Styles/_BootstrapFix.less b/dev/Styles/_BootstrapFix.less index 956cda713..5baec6bc6 100644 --- a/dev/Styles/_BootstrapFix.less +++ b/dev/Styles/_BootstrapFix.less @@ -13,11 +13,6 @@ label.inline, span.inline { color: @grayDark; border: 0; border-bottom: 1px solid @dropdownDividerTop; - - small { - font-size: @baseLineHeight * .75; - color: @grayLight; - } } .legend + .control-group { From ee540097b40bf6f3595e49e12940b0e220b71878 Mon Sep 17 00:00:00 2001 From: djmaze <djmaze@djmaze.lan> Date: Tue, 20 Jul 2021 20:53:11 +0200 Subject: [PATCH 23/26] v2.5.3 --- README.md | 34 ++++---- package.json | 2 +- yarn.lock | 232 ++++++++++++++++++++++++++------------------------- 3 files changed, 138 insertions(+), 130 deletions(-) diff --git a/README.md b/README.md index a8ec87646..f2d17cdc2 100644 --- a/README.md +++ b/README.md @@ -120,25 +120,25 @@ RainLoop 1.15 vs SnappyMail |js/* |RainLoop |Snappy | |--------------- |--------: |--------: | -|admin.js |2.158.025 | 91.664 | -|app.js |4.215.733 | 453.928 | +|admin.js |2.158.025 | 91.364 | +|app.js |4.215.733 | 454.661 | |boot.js | 672.433 | 3.147 | -|libs.js | 647.679 | 218.466 | +|libs.js | 647.679 | 218.491 | |polyfills.js | 325.908 | 0 | |serviceworker.js | 0 | 285 | -|TOTAL |8.019.778 | 767.490 | +|TOTAL |8.019.778 | 767.663 | |js/min/* |RainLoop |Snappy |RL gzip |SM gzip |RL brotli |SM brotli | |--------------- |--------: |--------: |------: |------: |--------: |--------: | -|admin.min.js | 255.514 | 47.908 | 73.899 | 14.313 | 60.674 | 12.787 | -|app.min.js | 516.000 | 233.532 |140.430 | 68.577 |110.657 | 57.878 | +|admin.min.js | 255.514 | 47.527 | 73.899 | 14.263 | 60.674 | 12.750 | +|app.min.js | 516.000 | 233.755 |140.430 | 68.663 |110.657 | 58.026 | |boot.min.js | 66.456 | 1.751 | 22.553 | 1.025 | 20.043 | 858 | -|libs.min.js | 574.626 | 106.529 |177.280 | 38.607 |151.855 | 34.567 | +|libs.min.js | 574.626 | 106.543 |177.280 | 38.613 |151.855 | 34.543 | |polyfills.min.js | 32.608 | 0 | 11.315 | 0 | 10.072 | 0 | -|TOTAL |1.445.204 | 389.720 |425.477 |122.522 |353.301 |106.090 | -|TOTAL (no admin) |1.189.690 | 341.812 |351.061 |108.209 |292.627 | 93.303 | +|TOTAL |1.445.204 | 389.576 |425.477 |122.564 |353.301 |106.177 | +|TOTAL (no admin) |1.189.690 | 342.049 |351.061 |108.301 |292.627 | 93.427 | -For a user its around 68% smaller and faster than traditional RainLoop. +For a user its around 69% smaller and faster than traditional RainLoop. |OpenPGP |RainLoop |Snappy |RL gzip |SM gzip |RL brotli |SM brotli | |--------------- |--------: |--------: |------: |------: |--------: |--------: | @@ -174,13 +174,13 @@ For a user its around 68% smaller and faster than traditional RainLoop. |css/* |RainLoop |Snappy |RL gzip |SM gzip |SM brotli | -|------------ |-------: |-------: |------: |------: |--------: | -|app.css | 340.334 | 100.825 | 46,959 | 18.002 | 15.540 | -|app.min.css | 274.791 | 82.684 | 39.618 | 16.138 | 14.234 | -|boot.css | | 1.326 | | 664 | 545 | -|boot.min.css | | 1.071 | | 590 | 474 | -|admin.css | | 42.217 | | 8.714 | 7.602 | -|admin.min.css | | 33.576 | | 7.680 | 6.810 | +|------------ |-------: |------: |------: |------: |--------: | +|app.css | 340.334 | 98.499 | 46,959 | 17.842 | 15.378 | +|app.min.css | 274.791 | 81.603 | 39.618 | 15.982 | 14.056 | +|boot.css | | 1.326 | | 664 | 545 | +|boot.min.css | | 1.071 | | 590 | 474 | +|admin.css | | 40.516 | | 8.481 | 7.401 | +|admin.min.css | | 32.041 | | 7.447 | 6.610 | ### Squire vs CKEditor diff --git a/package.json b/package.json index cff731062..346ef1fa7 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "title": "SnappyMail", "description": "Simple, modern & fast web-based email client", "private": true, - "version": "2.5.2", + "version": "2.5.3", "homepage": "https://snappymail.eu", "author": { "name": "DJ Maze", diff --git a/yarn.lock b/yarn.lock index 1b4fc8d05..24152d2c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,117 +2,125 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" - integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" + integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== dependencies: - "@babel/highlight" "^7.12.13" + "@babel/highlight" "^7.14.5" -"@babel/generator@^7.14.2": - version "7.14.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.3.tgz#0c2652d91f7bddab7cccc6ba8157e4f40dcedb91" - integrity sha512-bn0S6flG/j0xtQdz3hsjJ624h3W0r3llttBMfyHX3YrZ/KtLYr15bjA0FXkgW7FpvrDuTuElXeVjiKlYRpnOFA== +"@babel/generator@^7.14.8": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.8.tgz#bf86fd6af96cf3b74395a8ca409515f89423e070" + integrity sha512-cYDUpvIzhBVnMzRoY1fkSEhK/HmwEVwlyULYgn/tMQYd6Obag3ylCjONle3gdErfXBW61SVTlR9QR7uWlgeIkg== dependencies: - "@babel/types" "^7.14.2" + "@babel/types" "^7.14.8" jsesc "^2.5.1" source-map "^0.5.0" -"@babel/helper-function-name@^7.14.2": - version "7.14.2" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz#397688b590760b6ef7725b5f0860c82427ebaac2" - integrity sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ== +"@babel/helper-function-name@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz#89e2c474972f15d8e233b52ee8c480e2cfcd50c4" + integrity sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ== dependencies: - "@babel/helper-get-function-arity" "^7.12.13" - "@babel/template" "^7.12.13" - "@babel/types" "^7.14.2" + "@babel/helper-get-function-arity" "^7.14.5" + "@babel/template" "^7.14.5" + "@babel/types" "^7.14.5" -"@babel/helper-get-function-arity@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" - integrity sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg== +"@babel/helper-get-function-arity@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815" + integrity sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg== dependencies: - "@babel/types" "^7.12.13" + "@babel/types" "^7.14.5" -"@babel/helper-split-export-declaration@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz#e9430be00baf3e88b0e13e6f9d4eaf2136372b05" - integrity sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg== +"@babel/helper-hoist-variables@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz#e0dd27c33a78e577d7c8884916a3e7ef1f7c7f8d" + integrity sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ== dependencies: - "@babel/types" "^7.12.13" + "@babel/types" "^7.14.5" -"@babel/helper-validator-identifier@^7.14.0": - version "7.14.0" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" - integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A== - -"@babel/highlight@^7.12.13": - version "7.14.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.0.tgz#3197e375711ef6bf834e67d0daec88e4f46113cf" - integrity sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg== +"@babel/helper-split-export-declaration@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a" + integrity sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA== dependencies: - "@babel/helper-validator-identifier" "^7.14.0" + "@babel/types" "^7.14.5" + +"@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.8": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz#32be33a756f29e278a0d644fa08a2c9e0f88a34c" + integrity sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow== + +"@babel/highlight@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" + integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.5" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.12.13", "@babel/parser@^7.14.2", "@babel/parser@^7.7.0": - version "7.14.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.3.tgz#9b530eecb071fd0c93519df25c5ff9f14759f298" - integrity sha512-7MpZDIfI7sUC5zWo2+foJ50CSI5lcqDehZ0lVgIhSi4bFEk94fLAKlF3Q0nzSQQ+ca0lm+O6G9ztKVBeu8PMRQ== +"@babel/parser@^7.14.5", "@babel/parser@^7.14.8", "@babel/parser@^7.7.0": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.8.tgz#66fd41666b2d7b840bd5ace7f7416d5ac60208d4" + integrity sha512-syoCQFOoo/fzkWDeM0dLEZi5xqurb5vuyzwIMNZRNun+N/9A4cUZeQaE7dTrB8jGaKuJRBtEOajtnmw0I5hvvA== -"@babel/template@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" - integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== +"@babel/template@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" + integrity sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g== dependencies: - "@babel/code-frame" "^7.12.13" - "@babel/parser" "^7.12.13" - "@babel/types" "^7.12.13" + "@babel/code-frame" "^7.14.5" + "@babel/parser" "^7.14.5" + "@babel/types" "^7.14.5" "@babel/traverse@^7.7.0": - version "7.14.2" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.2.tgz#9201a8d912723a831c2679c7ebbf2fe1416d765b" - integrity sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA== + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.8.tgz#c0253f02677c5de1a8ff9df6b0aacbec7da1a8ce" + integrity sha512-kexHhzCljJcFNn1KYAQ6A5wxMRzq9ebYpEDV4+WdNyr3i7O44tanbDOR/xjiG2F3sllan+LgwK+7OMk0EmydHg== dependencies: - "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.14.2" - "@babel/helper-function-name" "^7.14.2" - "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/parser" "^7.14.2" - "@babel/types" "^7.14.2" + "@babel/code-frame" "^7.14.5" + "@babel/generator" "^7.14.8" + "@babel/helper-function-name" "^7.14.5" + "@babel/helper-hoist-variables" "^7.14.5" + "@babel/helper-split-export-declaration" "^7.14.5" + "@babel/parser" "^7.14.8" + "@babel/types" "^7.14.8" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.12.13", "@babel/types@^7.14.2", "@babel/types@^7.7.0": - version "7.14.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.2.tgz#4208ae003107ef8a057ea8333e56eb64d2f6a2c3" - integrity sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw== +"@babel/types@^7.14.5", "@babel/types@^7.14.8", "@babel/types@^7.7.0": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.8.tgz#38109de8fcadc06415fbd9b74df0065d4d41c728" + integrity sha512-iob4soQa7dZw8nodR/KlOQkPh9S4I8RwCxwRIFuiMRYjOzH/KJzdUfDgz6cGi5dDaclXF4P2PAhCdrBJNIg68Q== dependencies: - "@babel/helper-validator-identifier" "^7.14.0" + "@babel/helper-validator-identifier" "^7.14.8" to-fast-properties "^2.0.0" "@rollup/pluginutils@^4.0.0": - version "4.1.0" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.0.tgz#0dcc61c780e39257554feb7f77207dceca13c838" - integrity sha512-TrBhfJkFxA+ER+ew2U2/fHbebhLT/l/2pRk0hfj9KusXUuRXd2v0R58AfaZK9VXDQ4TogOSEmICVrQAA3zFnHQ== + version "4.1.1" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.1.tgz#1d4da86dd4eded15656a57d933fda2b9a08d47ec" + integrity sha512-clDjivHqWGXi7u+0d2r2sBi4Ie6VLEAzWMIkvJLnDmxoOhBYOTfzGbOQBA32THHm11/LiJbd01tJUpJsbshSWQ== dependencies: estree-walker "^2.0.1" picomatch "^2.2.2" "@types/estree@*": - version "0.0.47" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.47.tgz#d7a51db20f0650efec24cd04994f523d93172ed4" - integrity sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg== + version "0.0.50" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" + integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== "@types/minimatch@^3.0.3": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21" - integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" + integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== "@types/node@*": - version "15.3.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.3.0.tgz#d6fed7d6bc6854306da3dea1af9f874b00783e26" - integrity sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ== + version "16.3.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.3.3.tgz#0c30adff37bbbc7a50eb9b58fae2a504d0d88038" + integrity sha512-8h7k1YgQKxKXWckzFCMfsIwn0Y61UK6tlD6y2lOb3hTOIMlK3t9/QwHOhc81TwU+RMf0As5fj7NPjroERCnejQ== accord@^0.29.0: version "0.29.0" @@ -135,9 +143,9 @@ accord@^0.29.0: when "^3.7.8" acorn-jsx@^5.2.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" - integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn@^7.1.1: version "7.4.1" @@ -824,9 +832,9 @@ continuable-cache@^0.3.1: integrity sha1-vXJ6f67XfnH/OYWskzUakSczrQ8= convert-source-map@^1.5.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" - integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + version "1.8.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" + integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== dependencies: safe-buffer "~5.1.1" @@ -851,9 +859,9 @@ copy-props@^2.0.1: is-plain-object "^5.0.0" core-js@^3.8.3: - version "3.12.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.12.1.tgz#6b5af4ff55616c08a44d386f1f510917ff204112" - integrity sha512-Ne9DKPHTObRuB09Dru5AjwKjY4cJHVGu+y5f7coGn1E9Grkc3p2iBwE9AI/nJzsE29mQF7oq+mhYYRqOMFN1Bw== + version "3.15.2" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.15.2.tgz#740660d2ff55ef34ce664d7e2455119c5bdd3d61" + integrity sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q== core-util-is@~1.0.0: version "1.0.2" @@ -923,9 +931,9 @@ debug@^3.1.0: ms "^2.1.1" debug@^4.0.1, debug@^4.1.0: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + version "4.3.2" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== dependencies: ms "2.1.2" @@ -1493,7 +1501,7 @@ fsevents@^1.2.7: bindings "^1.5.0" nan "^2.12.1" -fsevents@~2.3.1: +fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -2133,9 +2141,9 @@ is-buffer@^1.1.5: integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== is-core-module@^2.2.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" - integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== + version "2.5.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.5.0.tgz#f754843617c70bfd29b7bd87327400cda5c18491" + integrity sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg== dependencies: has "^1.0.3" @@ -2969,14 +2977,14 @@ object-copy@^0.1.0: kind-of "^3.0.3" object-hash@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.1.1.tgz#9447d0279b4fcf80cff3259bf66a1dc73afabe09" - integrity sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ== + version "2.2.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" + integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== object-inspect@^1.9.0: - version "1.10.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" - integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== + version "1.11.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" + integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" @@ -3151,9 +3159,9 @@ path-key@^2.0.1: integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-root-regex@^0.1.0: version "0.1.2" @@ -3177,9 +3185,9 @@ path-type@^1.0.0: pinkie-promise "^2.0.0" picomatch@^2.2.2: - version "2.2.3" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.3.tgz#465547f359ccc206d3c48e46a1bcb89bf7ee619d" - integrity sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg== + version "2.3.0" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" + integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== pify@^2.0.0: version "2.3.0" @@ -3587,11 +3595,11 @@ rollup-pluginutils@^1.5.0: minimatch "^3.0.2" rollup@^2.38.0, rollup@^2.41.4: - version "2.48.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.48.0.tgz#fceb01ed771f991f29f7bd2ff7838146e55acb74" - integrity sha512-wl9ZSSSsi5579oscSDYSzGn092tCS076YB+TQrzsGuSfYyJeep8eEWj0eaRjuC5McuMNmcnR8icBqiE/FWNB1A== + version "2.53.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.53.2.tgz#3279f9bfba1fe446585560802e418c5fbcaefa51" + integrity sha512-1CtEYuS5CRCzFZ7SNW5528SlDlk4VDXIRGwbm/2POQxA/G4+7/crIqJwkmnj8Q/74hGx4oVlNvh4E1CJQ5hZ6w== optionalDependencies: - fsevents "~2.3.1" + fsevents "~2.3.2" run-async@^2.4.0: version "2.4.1" @@ -3807,9 +3815,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.8" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.8.tgz#eb1e97ad99b11bf3f82a3b71a0472dd9a00f2ecf" - integrity sha512-NDgA96EnaLSvtbM7trJj+t1LUR3pirkDCcz9nOUlPb5DMBGsH7oES6C3hs3j7R9oHEa1EMvReS/BUAIT5Tcr0g== + version "3.0.9" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz#8a595135def9592bda69709474f1cbeea7c2467f" + integrity sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ== split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" @@ -4006,9 +4014,9 @@ ternary-stream@^2.0.1: through2 "^2.0.1" terser@>=4, terser@^5.0.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.7.0.tgz#a761eeec206bc87b605ab13029876ead938ae693" - integrity sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g== + version "5.7.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.7.1.tgz#2dc7a61009b66bb638305cb2a824763b116bf784" + integrity sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg== dependencies: commander "^2.20.0" source-map "~0.7.2" From 7ab832e854f4a349e11feb580b8204ec458aabb1 Mon Sep 17 00:00:00 2001 From: djmaze <djmaze@djmaze.lan> Date: Wed, 21 Jul 2021 11:19:52 +0200 Subject: [PATCH 24/26] Custom new mail notification sounds --- assets/sounds/alert.mp3 | Bin 0 -> 9364 bytes assets/sounds/alert.ogg | Bin 0 -> 9958 bytes assets/sounds/ping.mp3 | Bin 0 -> 12072 bytes assets/sounds/ping.ogg | Bin 0 -> 12344 bytes dev/Common/Audio.js | 10 +++++----- dev/Settings/User/General.js | 6 ++++++ .../v/0.0.0/app/libraries/RainLoop/Actions.php | 6 ++++++ .../app/libraries/RainLoop/Actions/User.php | 1 + .../templates/Views/User/SettingsGeneral.html | 2 ++ 9 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 assets/sounds/alert.mp3 create mode 100644 assets/sounds/alert.ogg create mode 100644 assets/sounds/ping.mp3 create mode 100644 assets/sounds/ping.ogg diff --git a/assets/sounds/alert.mp3 b/assets/sounds/alert.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f1779dfe99e7b9c4b206fc10dbfc9eb4c17e5024 GIT binary patch literal 9364 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 literal 0 HcmV?d00001 diff --git a/assets/sounds/alert.ogg b/assets/sounds/alert.ogg new file mode 100644 index 0000000000000000000000000000000000000000..e848b3f425911e95e82b744e5035157e62e3fc99 GIT binary patch literal 9958 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 literal 0 HcmV?d00001 diff --git a/assets/sounds/ping.mp3 b/assets/sounds/ping.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..52885ece878db24f65b763b947d26a6b0cdd05db GIT binary patch literal 12072 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 literal 0 HcmV?d00001 diff --git a/assets/sounds/ping.ogg b/assets/sounds/ping.ogg new file mode 100644 index 0000000000000000000000000000000000000000..6620ded87486177f99e69bf6ee6509e0827a7ca3 GIT binary patch literal 12344 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 literal 0 HcmV?d00001 diff --git a/dev/Common/Audio.js b/dev/Common/Audio.js index d6d9f5aeb..26660d695 100644 --- a/dev/Common/Audio.js +++ b/dev/Common/Audio.js @@ -1,5 +1,5 @@ import * as Links from 'Common/Links'; -import { doc } from 'Common/Globals'; +import { doc, SettingsGet } from 'Common/Globals'; let notificator = null, player = null, @@ -106,11 +106,11 @@ export const SMAudio = new class { playNotification(silent) { if ('running' == audioCtx.state && (this.supportedMp3 || this.supportedOgg)) { - if (!notificator) { - notificator = createNewObject(); - notificator.src = Links.staticLink('sounds/new-mail.'+ (this.supportedMp3 ? 'mp3' : 'ogg')); - } + notificator = notificator || createNewObject(); if (notificator) { + notificator.src = Links.staticLink('sounds/' + + SettingsGet('NotificationSound') + + (this.supportedMp3 ? '.mp3' : '.ogg')); notificator.volume = silent ? 0.01 : 1; notificator.play(); } diff --git a/dev/Settings/User/General.js b/dev/Settings/User/General.js index ed49b0af6..9df238ca7 100644 --- a/dev/Settings/User/General.js +++ b/dev/Settings/User/General.js @@ -32,6 +32,8 @@ export class GeneralUserSettings { this.layout = SettingsUserStore.layout; this.enableSoundNotification = NotificationUserStore.enableSoundNotification; + this.notificationSound = ko.observable(SettingsGet('NotificationSound')); + this.notificationSounds = ko.observableArray(SettingsGet('NewMailSounds')); this.enableDesktopNotification = NotificationUserStore.enableDesktopNotification; this.isDesktopNotificationDenied = NotificationUserStore.isDesktopNotificationDenied; @@ -115,6 +117,10 @@ export class GeneralUserSettings { enableDesktopNotification: value => Remote.saveSetting('DesktopNotifications', value ? 1 : 0), enableSoundNotification: value => Remote.saveSetting('SoundNotification', value ? 1 : 0), + notificationSound: value => { + Remote.saveSetting('NotificationSound', value); + rl.settings.set('NotificationSound', value); + }, replySameFolder: value => Remote.saveSetting('ReplySameFolder', value ? 1 : 0), diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php index c73a6404c..c0b8e42a2 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -1060,6 +1060,7 @@ class Actions 'RemoveColors' => (bool) $oConfig->Get('defaults', 'remove_colors', false), 'MPP' => (int) $oConfig->Get('webmail', 'messages_per_page', 25), 'SoundNotification' => false, + 'NotificationSound' => 'new-mail', 'DesktopNotifications' => false, 'Layout' => (int) $oConfig->Get('defaults', 'view_layout', Enumerations\Layout::SIDE_PREVIEW), 'EditorDefaultType' => (string) $oConfig->Get('defaults', 'view_editor_type', ''), @@ -1165,6 +1166,7 @@ class Actions $aResult['ContactsAutosave'] = (bool)$oSettings->GetConf('ContactsAutosave', $aResult['ContactsAutosave']); $aResult['MPP'] = (int)$oSettings->GetConf('MPP', $aResult['MPP']); $aResult['SoundNotification'] = (bool)$oSettings->GetConf('SoundNotification', $aResult['SoundNotification']); + $aResult['NotificationSound'] = (string)$oSettings->GetConf('NotificationSound', $aResult['NotificationSound']); $aResult['DesktopNotifications'] = (bool)$oSettings->GetConf('DesktopNotifications', $aResult['DesktopNotifications']); $aResult['UseCheckboxesInList'] = (bool)$oSettings->GetConf('UseCheckboxesInList', $aResult['UseCheckboxesInList']); $aResult['AllowDraftAutosave'] = (bool)$oSettings->GetConf('AllowDraftAutosave', $aResult['AllowDraftAutosave']); @@ -1186,6 +1188,10 @@ class Actions $aResult['ReplySameFolder'] = (bool)$oSettingsLocal->GetConf('ReplySameFolder', $aResult['ReplySameFolder']); } } + $aResult['NewMailSounds'] = []; + foreach (\glob(APP_VERSION_ROOT_PATH.'static/sounds/*.mp3') as $file) { + $aResult['NewMailSounds'][] = \basename($file, '.mp3'); + } } else { if ($oConfig->Get('login', 'allow_languages_on_login', true) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php index dfcd1bd5c..05f4e98b5 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php @@ -347,6 +347,7 @@ trait User $this->setSettingsFromParams($oSettings, 'ContactsAutosave', 'bool'); $this->setSettingsFromParams($oSettings, 'DesktopNotifications', 'bool'); $this->setSettingsFromParams($oSettings, 'SoundNotification', 'bool'); + $this->setSettingsFromParams($oSettings, 'NotificationSound', 'string'); $this->setSettingsFromParams($oSettings, 'UseCheckboxesInList', 'bool'); $this->setSettingsFromParams($oSettings, 'AllowDraftAutosave', 'bool'); $this->setSettingsFromParams($oSettings, 'AutoLogout', 'int'); diff --git a/snappymail/v/0.0.0/app/templates/Views/User/SettingsGeneral.html b/snappymail/v/0.0.0/app/templates/Views/User/SettingsGeneral.html index c3aa316dc..02f169b0c 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/SettingsGeneral.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/SettingsGeneral.html @@ -132,6 +132,8 @@ } }"></div>   + <select data-bind="options: notificationSounds, value: notificationSound"></select> +   <span class="fontastic" data-bind="click: testSoundNotification" style="color:green;cursor:pointer">▶️</span> </div> </div> From 689f329b4eb3c057c0ba38fc3afb2d93107b9150 Mon Sep 17 00:00:00 2001 From: djmaze <djmaze@djmaze.lan> Date: Wed, 21 Jul 2021 11:20:55 +0200 Subject: [PATCH 25/26] Replace gulp blacklisted modules https://github.com/the-djmaze/snappymail/issues/102#issuecomment-883843969 --- tasks/common.js | 6 +- tasks/css.js | 9 +- tasks/js.js | 2 +- yarn.lock | 469 +++++++++++++++++++++--------------------------- 4 files changed, 210 insertions(+), 276 deletions(-) diff --git a/tasks/common.js b/tasks/common.js index 8e670d5a4..e0a403a7c 100644 --- a/tasks/common.js +++ b/tasks/common.js @@ -1,11 +1,11 @@ /* RainLoop Webmail (c) RainLoop Team | Licensed under AGPL 3 */ const gulp = require('gulp'); -const rimraf = require('gulp-rimraf'); +const del = require('del'); const fs = require('node-fs'); const { config } = require('./config'); -exports.del = (dir) => gulp.src(dir, { read: false, allowEmpty: true }).pipe(rimraf()); +exports.del = (dir) => del(dir); exports.copy = (sFile, sNewFile, done) => { fs.writeFileSync(sNewFile, fs.readFileSync(sFile)); @@ -14,4 +14,4 @@ exports.copy = (sFile, sNewFile, done) => { exports.getHead = () => config.head.agpl; -exports.cleanStatic = () => exports.del(config.paths.static); +exports.cleanStatic = () => del(config.paths.static); diff --git a/tasks/css.js b/tasks/css.js index 955750e6b..6cb4fad13 100644 --- a/tasks/css.js +++ b/tasks/css.js @@ -1,11 +1,10 @@ /* RainLoop Webmail (c) RainLoop Team | Licensed under AGPL 3 */ const gulp = require('gulp'); -const concat = require('gulp-concat-util'); +const concat = require('gulp-concat'); const rename = require('gulp-rename'); const replace = require('gulp-replace'); const eol = require('gulp-eol'); -const livereload = require('gulp-livereload'); const filter = require('gulp-filter'); const expect = require('gulp-expect-file'); const gcmq = require('gulp-group-css-media-queries'); @@ -47,8 +46,7 @@ const cssMainBuild = () => { .pipe(concat(config.paths.css.main.name)) .pipe(replace(/\.\.\/(img|images|fonts|svg)\//g, '$1/')) .pipe(eol('\n', true)) - .pipe(gulp.dest(config.paths.staticCSS)) - .pipe(livereload()); + .pipe(gulp.dest(config.paths.staticCSS)); }; const cssAdminBuild = () => { @@ -70,8 +68,7 @@ const cssAdminBuild = () => { .pipe(concat(config.paths.css.admin.name)) .pipe(replace(/\.\.\/(img|images|fonts|svg)\//g, '$1/')) .pipe(eol('\n', true)) - .pipe(gulp.dest(config.paths.staticCSS)) - .pipe(livereload()); + .pipe(gulp.dest(config.paths.staticCSS)); }; const cssBootMin = () => { diff --git a/tasks/js.js b/tasks/js.js index b5adc6174..c221902ac 100644 --- a/tasks/js.js +++ b/tasks/js.js @@ -1,7 +1,7 @@ /* RainLoop Webmail (c) RainLoop Team | Licensed under AGPL 3 */ const gulp = require('gulp'); -const concat = require('gulp-concat-util'), +const concat = require('gulp-concat'), header = require('gulp-header'), rename = require('gulp-rename'), replace = require('gulp-replace'), diff --git a/yarn.lock b/yarn.lock index 24152d2c9..375f0eb56 100644 --- a/yarn.lock +++ b/yarn.lock @@ -99,6 +99,27 @@ "@babel/helper-validator-identifier" "^7.14.8" to-fast-properties "^2.0.0" +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + "@rollup/pluginutils@^4.0.0": version "4.1.1" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.1.tgz#1d4da86dd4eded15656a57d933fda2b9a08d47ec" @@ -118,9 +139,9 @@ integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== "@types/node@*": - version "16.3.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.3.3.tgz#0c30adff37bbbc7a50eb9b58fae2a504d0d88038" - integrity sha512-8h7k1YgQKxKXWckzFCMfsIwn0Y61UK6tlD6y2lOb3hTOIMlK3t9/QwHOhc81TwU+RMf0As5fj7NPjroERCnejQ== + version "16.4.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.4.0.tgz#2c219eaa3b8d1e4d04f4dd6e40bc68c7467d5272" + integrity sha512-HrJuE7Mlqcjj+00JqMWpZ3tY8w7EUd+S0U3L1+PQSWiXZbOgyQDvi+ogoUxaHApPJq5diKxYBQwA3iIlNcPqOg== accord@^0.29.0: version "0.29.0" @@ -152,6 +173,14 @@ acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv@^6.10.0, ajv@^6.10.2: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -497,16 +526,6 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" -body@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/body/-/body-5.1.0.tgz#e4ba0ce410a46936323367609ecb4e6553125069" - integrity sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk= - dependencies: - continuable-cache "^0.3.1" - error "^7.0.0" - raw-body "~1.1.0" - safe-json-parse "~1.0.1" - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -531,6 +550,13 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" +braces@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + buffer-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" @@ -541,11 +567,6 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== -bytes@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-1.0.0.tgz#3569ede8ba34315fab99c3e92cb04c7220de1fa8" - integrity sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g= - cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -611,7 +632,7 @@ chalk@^1.0.0: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1: +chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.0: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -669,6 +690,11 @@ clean-css@4.2.3, clean-css@4.2.x: dependencies: source-map "~0.6.0" +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -819,18 +845,13 @@ concat-stream@^1.6.0: readable-stream "^2.2.2" typedarray "^0.0.6" -concat-with-sourcemaps@^1.0.4, concat-with-sourcemaps@^1.1.0: +concat-with-sourcemaps@^1.0.0, concat-with-sourcemaps@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" integrity sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg== dependencies: source-map "^0.6.1" -continuable-cache@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f" - integrity sha1-vXJ6f67XfnH/OYWskzUakSczrQ8= - convert-source-map@^1.5.0: version "1.8.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" @@ -923,13 +944,6 @@ debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@^3.1.0: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - debug@^4.0.1, debug@^4.1.0: version "4.3.2" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" @@ -993,11 +1007,32 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" +del@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/del/-/del-6.0.0.tgz#0b40d0332cea743f1614f818be4feb717714c952" + integrity sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ== + dependencies: + globby "^11.0.1" + graceful-fs "^4.2.4" + is-glob "^4.0.1" + is-path-cwd "^2.2.0" + is-path-inside "^3.0.2" + p-map "^4.0.0" + rimraf "^3.0.2" + slash "^3.0.0" + detect-file@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + doctrine@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" @@ -1012,19 +1047,12 @@ duplexer2@0.0.2: dependencies: readable-stream "~1.1.9" -duplexer2@~0.1.0: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" - integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME= - dependencies: - readable-stream "^2.0.2" - duplexer@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== -duplexify@^3.5.0, duplexify@^3.6.0: +duplexify@^3.6.0: version "3.7.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== @@ -1078,13 +1106,6 @@ error-ex@^1.2.0: dependencies: is-arrayish "^0.2.1" -error@^7.0.0: - version "7.2.1" - resolved "https://registry.yarnpkg.com/error/-/error-7.2.1.tgz#eab21a4689b5f684fc83da84a0e390de82d94894" - integrity sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA== - dependencies: - string-template "~0.2.1" - es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50: version "0.10.53" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" @@ -1334,6 +1355,17 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-glob@^3.1.1: + version "3.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" + integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -1349,12 +1381,12 @@ fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= -faye-websocket@~0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" - integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= +fastq@^1.6.0: + version "1.11.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.1.tgz#5d8175aae17db61947f8b162cfc7f63264d22807" + integrity sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw== dependencies: - websocket-driver ">=0.5.1" + reusify "^1.0.4" figures@^3.0.0: version "3.2.0" @@ -1385,6 +1417,13 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -1468,11 +1507,6 @@ for-own@^1.0.0: dependencies: for-in "^1.0.1" -fork-stream@^0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/fork-stream/-/fork-stream-0.0.4.tgz#db849fce77f6708a5f8f386ae533a0907b54ae70" - integrity sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA= - fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -1543,7 +1577,7 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.0.0: +glob-parent@^5.0.0, glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -1623,6 +1657,18 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" +globby@^11.0.1: + version "11.0.4" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" + integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + glogg@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.2.tgz#2d7dd702beda22eb3bffadf880696da6d846313f" @@ -1630,7 +1676,7 @@ glogg@^1.0.0: dependencies: sparkles "^1.0.0" -graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6: +graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.4: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== @@ -1685,17 +1731,14 @@ gulp-cli@^2.2.0: v8flags "^3.2.0" yargs "^7.1.0" -gulp-concat-util@0.5.5: - version "0.5.5" - resolved "https://registry.yarnpkg.com/gulp-concat-util/-/gulp-concat-util-0.5.5.tgz#c8f0633b4e6e950ff93c475b3c8d3b84be03b7dc" - integrity sha1-yPBjO05ulQ/5PEdbPI07hL4Dt9w= +gulp-concat@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/gulp-concat/-/gulp-concat-2.6.1.tgz#633d16c95d88504628ad02665663cee5a4793353" + integrity sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M= dependencies: - concat-with-sourcemaps "^1.0.4" - gulp-if "^2.0.0" - gulp-util "^3.0.6" - lodash.assign "^3.2.0" - stream-combiner2 "^1.1.1" + concat-with-sourcemaps "^1.0.0" through2 "^2.0.0" + vinyl "^2.0.0" gulp-eol@0.2.0: version "0.2.0" @@ -1756,15 +1799,6 @@ gulp-header@2.0.9: map-stream "0.0.7" through2 "^2.0.0" -gulp-if@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/gulp-if/-/gulp-if-2.0.2.tgz#a497b7e7573005041caa2bc8b7dda3c80444d629" - integrity sha1-pJe351cwBQQcqivIt92jyARE1ik= - dependencies: - gulp-match "^1.0.3" - ternary-stream "^2.0.1" - through2 "^2.0.1" - gulp-less@4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/gulp-less/-/gulp-less-4.0.1.tgz#348c33a5dde7a207c5771b1d8261d1ac1021ceed" @@ -1778,26 +1812,6 @@ gulp-less@4.0.1: through2 "^2.0.0" vinyl-sourcemaps-apply "^0.2.0" -gulp-livereload@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/gulp-livereload/-/gulp-livereload-4.0.2.tgz#fc8a75c7511cd65afd2202cbcdc8bb0f8dde377b" - integrity sha512-InmaR50Xl1xB1WdEk4mrUgGHv3VhhlRLrx7u60iY5AAer90FlK95KXitPcGGQoi28zrUJM189d/h6+V470Ncgg== - dependencies: - chalk "^2.4.1" - debug "^3.1.0" - fancy-log "^1.3.2" - lodash.assign "^4.2.0" - readable-stream "^3.0.6" - tiny-lr "^1.1.1" - vinyl "^2.2.0" - -gulp-match@^1.0.3: - version "1.1.0" - resolved "https://registry.yarnpkg.com/gulp-match/-/gulp-match-1.1.0.tgz#552b7080fc006ee752c90563f9fec9d61aafdf4f" - integrity sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ== - dependencies: - minimatch "^3.0.3" - gulp-rename@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/gulp-rename/-/gulp-rename-2.0.0.tgz#9bbc3962b0c0f52fc67cd5eaff6c223ec5b9cf6c" @@ -1812,14 +1826,6 @@ gulp-replace@1.0.0: readable-stream "^2.0.1" replacestream "^4.0.0" -gulp-rimraf@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gulp-rimraf/-/gulp-rimraf-1.0.0.tgz#ccb3e639447e95831601f3d6d678bd3f33962285" - integrity sha512-RIHk+Ec5Sk0DFkgsAeSF6GUSlqikLMhXNHpUxCeNeIR7SG2j1f525cP7oLiHmgy9gzSeILRaC5i+79mQmTIy6A== - dependencies: - rimraf "^2.6.2" - through2 "^3.0.1" - gulp-rollup-2@^1.2.1: version "1.3.1" resolved "https://registry.yarnpkg.com/gulp-rollup-2/-/gulp-rollup-2-1.3.1.tgz#bbe1a8725ccaa4fc270627b298d1072d960fbe0c" @@ -1868,7 +1874,7 @@ gulp-terser@^1.4.0: through2 "^4.0.2" vinyl-sourcemaps-apply "^0.2.1" -gulp-util@3.0.8, gulp-util@^3.0.6: +gulp-util@3.0.8: version "3.0.8" resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f" integrity sha1-AFTh50RQLifATBh8PsxQXdVLu08= @@ -2014,11 +2020,6 @@ html-minifier@^3.0.2: relateurl "0.2.x" uglify-js "3.4.x" -http-parser-js@>=0.5.1: - version "0.5.3" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9" - integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg== - iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -2031,6 +2032,11 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +ignore@^5.1.4: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + image-size@~0.5.0: version "0.5.5" resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" @@ -2049,6 +2055,11 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + indx@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/indx/-/indx-0.2.3.tgz#15dcf56ee9cf65c0234c513c27fbd580e70fbc50" @@ -2244,6 +2255,21 @@ is-number@^4.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-cwd@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + +is-path-inside@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -2481,11 +2507,6 @@ liftoff@^3.1.0: rechoir "^0.6.2" resolve "^1.1.7" -livereload-js@^2.3.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.4.0.tgz#447c31cf1ea9ab52fc20db615c5ddf678f78009c" - integrity sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw== - load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -2497,14 +2518,6 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" -lodash._baseassign@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" - integrity sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4= - dependencies: - lodash._basecopy "^3.0.0" - lodash.keys "^3.0.0" - lodash._basecopy@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" @@ -2520,20 +2533,6 @@ lodash._basevalues@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" integrity sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc= -lodash._bindcallback@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" - integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4= - -lodash._createassigner@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" - integrity sha1-g4pbri/aymOsIt7o4Z+k5taXCxE= - dependencies: - lodash._bindcallback "^3.0.0" - lodash._isiterateecall "^3.0.0" - lodash.restparam "^3.0.0" - lodash._getnative@^3.0.0: version "3.9.1" resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" @@ -2564,20 +2563,6 @@ lodash._root@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" integrity sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI= -lodash.assign@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-3.2.0.tgz#3ce9f0234b4b2223e296b8fa0ac1fee8ebca64fa" - integrity sha1-POnwI0tLIiPilrj6CsH+6OvKZPo= - dependencies: - lodash._baseassign "^3.0.0" - lodash._createassigner "^3.0.0" - lodash.keys "^3.0.0" - -lodash.assign@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" - integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= - lodash.clone@^4.3.2: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6" @@ -2753,18 +2738,16 @@ matchdep@^2.0.0: resolve "^1.4.0" stack-trace "0.0.10" -merge-stream@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" - integrity sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE= - dependencies: - readable-stream "^2.0.1" - merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -2784,6 +2767,14 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" +micromatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + dependencies: + braces "^3.0.1" + picomatch "^2.2.3" + mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -2794,7 +2785,7 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: +minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -2831,11 +2822,6 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - multimatch@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-4.0.0.tgz#8c3c0f6e3e8449ada0af3dd29efb491a375191b3" @@ -2962,7 +2948,7 @@ object-assign@^3.0.0: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I= -object-assign@^4.0.1, object-assign@^4.1.0: +object-assign@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -2981,11 +2967,6 @@ object-hash@^2.1.1: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== -object-inspect@^1.9.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" - integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== - object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -3091,6 +3072,13 @@ os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + param-case@2.1.x: version "2.1.1" resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" @@ -3184,7 +3172,12 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" -picomatch@^2.2.2: +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picomatch@^2.2.2, picomatch@^2.2.3: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== @@ -3294,12 +3287,10 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -qs@^6.4.0: - version "6.10.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a" - integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg== - dependencies: - side-channel "^1.0.4" +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== randombytes@^2.1.0: version "2.1.0" @@ -3308,14 +3299,6 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -raw-body@~1.1.0: - version "1.1.7" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-1.1.7.tgz#1d027c2bfa116acc6623bca8f00016572a87d425" - integrity sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU= - dependencies: - bytes "1" - string_decoder "0.10" - read-pkg-up@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" @@ -3525,6 +3508,11 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + right-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" @@ -3539,20 +3527,13 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" -rimraf@3.0.2: +rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" -rimraf@^2.6.2: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - rollup-plugin-external-globals@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/rollup-plugin-external-globals/-/rollup-plugin-external-globals-0.6.1.tgz#861c260b5727144e0fd1b424b103f9f0282fc365" @@ -3595,9 +3576,9 @@ rollup-pluginutils@^1.5.0: minimatch "^3.0.2" rollup@^2.38.0, rollup@^2.41.4: - version "2.53.2" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.53.2.tgz#3279f9bfba1fe446585560802e418c5fbcaefa51" - integrity sha512-1CtEYuS5CRCzFZ7SNW5528SlDlk4VDXIRGwbm/2POQxA/G4+7/crIqJwkmnj8Q/74hGx4oVlNvh4E1CJQ5hZ6w== + version "2.53.3" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.53.3.tgz#14b0e57f0874d4ad23bdbb13050cf70bcd1eabf7" + integrity sha512-79QIGP5DXz5ZHYnCPi3tLz+elOQi6gudp9YINdaJdjG0Yddubo6JRFUM//qCZ0Bap/GJrsUoEBVdSOc4AkMlRA== optionalDependencies: fsevents "~2.3.2" @@ -3606,6 +3587,13 @@ run-async@^2.4.0: resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + rxjs@^6.6.0: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" @@ -3613,7 +3601,7 @@ rxjs@^6.6.0: dependencies: tslib "^1.9.0" -safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: +safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -3623,11 +3611,6 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-json-parse@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/safe-json-parse/-/safe-json-parse-1.0.1.tgz#3e76723e38dfdda13c9b1d29a1e07ffee4b30b57" - integrity sha1-PnZyPjjf3aE8mx0poeB//uSzC1c= - safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -3691,20 +3674,16 @@ shebang-regex@^1.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + slice-ansi@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" @@ -3844,14 +3823,6 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -stream-combiner2@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe" - integrity sha1-+02KFCDqNidk4hrUeAOXvry0HL4= - dependencies: - duplexer2 "~0.1.0" - readable-stream "^2.0.2" - stream-counter@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stream-counter/-/stream-counter-1.0.0.tgz#91cf2569ce4dc5061febcd7acb26394a5a114751" @@ -3874,11 +3845,6 @@ streamfilter@^3.0.0: dependencies: readable-stream "^3.0.6" -string-template@~0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" - integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0= - string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -3906,11 +3872,6 @@ string-width@^4.1.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string_decoder@0.10, string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -3918,6 +3879,11 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -4003,16 +3969,6 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" -ternary-stream@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ternary-stream/-/ternary-stream-2.1.1.tgz#4ad64b98668d796a085af2c493885a435a8a8bfc" - integrity sha512-j6ei9hxSoyGlqTmoMjOm+QNvUKDOIY6bNl4Uh1lhBvl6yjPW2iLqxDUYyfDPZknQ4KdRziFl+ec99iT4l7g0cw== - dependencies: - duplexify "^3.5.0" - fork-stream "^0.0.4" - merge-stream "^1.0.0" - through2 "^2.0.1" - terser@>=4, terser@^5.0.0: version "5.7.1" resolved "https://registry.yarnpkg.com/terser/-/terser-5.7.1.tgz#2dc7a61009b66bb638305cb2a824763b116bf784" @@ -4096,18 +4052,6 @@ time-stamp@^1.0.0: resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= -tiny-lr@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/tiny-lr/-/tiny-lr-1.1.1.tgz#9fa547412f238fedb068ee295af8b682c98b2aab" - integrity sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA== - dependencies: - body "^5.1.0" - debug "^3.1.0" - faye-websocket "~0.10.0" - livereload-js "^2.3.0" - object-assign "^4.1.0" - qs "^6.4.0" - tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -4143,6 +4087,13 @@ to-regex-range@^2.1.0: is-number "^3.0.0" repeat-string "^1.6.1" +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" @@ -4393,20 +4344,6 @@ vinyl@^2.0.0, vinyl@^2.2.0, vinyl@^2.2.1: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" -websocket-driver@>=0.5.1: - version "0.7.4" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" - integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== - dependencies: - http-parser-js ">=0.5.1" - safe-buffer ">=5.1.0" - websocket-extensions ">=0.1.1" - -websocket-extensions@>=0.1.1: - version "0.1.4" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" - integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== - when@^3.7.8: version "3.7.8" resolved "https://registry.yarnpkg.com/when/-/when-3.7.8.tgz#c7130b6a7ea04693e842cdc9e7a1f2aa39a39f82" From 319e30265be786b903f4ea273b314425c931e939 Mon Sep 17 00:00:00 2001 From: djmaze <djmaze@djmaze.lan> Date: Wed, 21 Jul 2021 12:24:12 +0200 Subject: [PATCH 26/26] Replace gulp blacklisted modules https://github.com/the-djmaze/snappymail/issues/102#issuecomment-883843969 --- package.json | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 346ef1fa7..3914c9a46 100644 --- a/package.json +++ b/package.json @@ -48,25 +48,23 @@ "gulp": "4.0.2", "gulp-cached": "1.1.1", "gulp-clean-css": "4.3.0", - "gulp-concat-util": "0.5.5", "gulp-eol": "0.2.0", "gulp-eslint": "6.0.0", "gulp-expect-file": "1.0.2", "gulp-filter": "6.0.0", "gulp-header": "2.0.9", "gulp-less": "4.0.1", - "gulp-livereload": "4.0.2", "gulp-rename": "2.0.0", "gulp-replace": "1.0.0", - "gulp-rimraf": "1.0.0", "gulp-size": "3.0.0", "gulp-stripbom": "1.0.5", "gulp-terser": "^1.4.0", "gulp-util": "3.0.8", - "node-fs": "0.1.7", - "rimraf": "3.0.2" + "node-fs": "0.1.7" }, "dependencies": { + "del": "^6.0.0", + "gulp-concat": "^2.6.1", "gulp-group-css-media-queries": "^1.2.2", "gulp-rollup-2": "^1.2.1", "rollup": "^2.38.0",