diff --git a/dev/boot.js b/dev/boot.js index bfc0717ac..eca88cb51 100644 --- a/dev/boot.js +++ b/dev/boot.js @@ -144,7 +144,12 @@ win.rl = { rl.hash.set(); if (appData) { + if (appData.NewThemeLink) { + eId('app-theme-link').href = appData.NewThemeLink; + } + loadScript(appData.StaticLibJsLink) + .then(() => loadScript(appData.LangLink)) .then(() => loadScript(appData.StaticAppJsLink)) .then(() => appData.PluginsLink ? loadScript(appData.PluginsLink) : Promise.resolve()) .then(() => win.__APP_BOOT ? win.__APP_BOOT(showError) : showError()) diff --git a/dev/bootstrap.js b/dev/bootstrap.js index 5ee544e59..9cb786364 100644 --- a/dev/bootstrap.js +++ b/dev/bootstrap.js @@ -3,7 +3,7 @@ import { i18n } from 'Common/Translator'; import { root } from 'Common/Links'; -export default (App) => { +export default App => { addEventListener('keydown', event => { event = event || window.event; @@ -136,10 +136,10 @@ export default (App) => { }; window.__APP_BOOT = () => { - const cb = () => setTimeout(() => { - setTimeout(() => App.bootstart(), 10); - window.__APP_BOOT = null; - }, 10); + const cb = () => { + window.__APP_BOOT = null; + App.bootstart(); + }; ('loading' !== doc.readyState) ? cb() : doc.addEventListener('DOMContentLoaded', cb); }; }; 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 f246dd7e6..9e0513473 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -15,7 +15,6 @@ class Actions use Actions\Localization; use Actions\Themes; - const AUTH_TFA_SIGN_ME_TOKEN_KEY = 'rltfasmauth'; const AUTH_SIGN_ME_TOKEN_KEY = 'rlsmauth'; const AUTH_MAILTO_TOKEN_KEY = 'rlmailtoauth'; const AUTH_SPEC_TOKEN_KEY = 'rlspecauth'; @@ -511,9 +510,9 @@ class Actions { $sResult = Utils::GetCookie(self::AUTH_SPEC_TOKEN_KEY, ''); if (0 < strlen($sResult)) { + // TODO: Browser F5 issue Utils::ClearCookie(self::AUTH_SPEC_TOKEN_KEY); } - return $sResult; } @@ -1015,12 +1014,8 @@ class Actions ) : array()); } - public function AppData(bool $bAdmin, string $sAuthAccountHash = ''): array + public function AppData(bool $bAdmin, string $sAuthAccountHash): array { - if (0 < \strlen($sAuthAccountHash) && \preg_match('/[^_\-\.a-zA-Z0-9]/', $sAuthAccountHash)) { - $sAuthAccountHash = ''; - } - $oAccount = null; $oConfig = $this->Config(); @@ -1088,7 +1083,7 @@ class Actions 'UserBackgroundHash' => '' ); - if (0 < \strlen($sAuthAccountHash)) { + if (\strlen($sAuthAccountHash) && !\preg_match('/[^_\-\.a-zA-Z0-9]/', $sAuthAccountHash)) { $aResult['AuthAccountHash'] = $sAuthAccountHash; } @@ -1105,7 +1100,6 @@ class Actions } $sLanguage = $oConfig->Get('webmail', 'language', 'en'); - $sLanguageAdmin = $oConfig->Get('webmail', 'language_admin', 'en'); $UserLanguageRaw = $this->detectUserLanguage($bAdmin); if (!$bAdmin) { @@ -1258,24 +1252,22 @@ class Actions $sStaticCache = $this->StaticCache(); $aResult['Theme'] = $this->GetTheme($bAdmin); + $aResult['NewThemeLink'] = $this->ThemeLink($bAdmin); $aResult['Language'] = $this->ValidateLanguage($sLanguage, '', false); $aResult['UserLanguage'] = $this->ValidateLanguage($UserLanguageRaw, '', false, true); if ($bAdmin) { - $aResult['LanguageAdmin'] = $this->ValidateLanguage($sLanguageAdmin, '', true); + $aResult['LanguageAdmin'] = $this->ValidateLanguage($oConfig->Get('webmail', 'language_admin', 'en'), '', true); $aResult['UserLanguageAdmin'] = $this->ValidateLanguage($UserLanguageRaw, '', true, true); } + $aResult['LangLink'] = './?/Lang/0/' . ($bAdmin ? 'Admin' : 'App') . '/' . + ($bAdmin ? $aResult['LanguageAdmin'] : $aResult['Language']) . '/' . $sStaticCache . '/'; $aResult['PluginsLink'] = ''; if (0 < $this->Plugins()->Count() && $this->Plugins()->HaveJs($bAdmin)) { $aResult['PluginsLink'] = './?/Plugins/0/' . ($bAdmin ? 'Admin' : 'User') . '/' . $sStaticCache . '/'; } -// $aResult['LangLink'] = './?/Lang/0/' . ($bAdmin ? 'Admin' : 'App') . '/' . -// ($bAdmin ? $aResult['LanguageAdmin'] : $aResult['Language']) . '/' . $sStaticCache . '/'; - -// $aResult['TemplatesLink'] = './?/Templates/0/' . ($bAdmin ? 'Admin' : 'App') . '/' . $sStaticCache . '/'; - $bAppJsDebug = !!$this->Config()->Get('labs', 'use_app_debug_js', false); $aResult['StaticLibJsLink'] = $this->StaticPath('js/' . ($bAppJsDebug ? '' : 'min/') . diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Localization.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Localization.php index 7ef4c62f6..0d88e8c45 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Localization.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Localization.php @@ -114,4 +114,35 @@ trait Localization return $aLang[$aKey] ?? $sKey; } + + public function compileLanguage(string $sLanguage, bool $bAdmin = false) : string + { + $sLanguage = \strtr($sLanguage, '_', '-'); + + $aResultLang = \json_decode(\file_get_contents(APP_VERSION_ROOT_PATH.'app/localization/langs.json'), true); + $langs = \array_flip(\SnappyMail\L10n::getLanguages($bAdmin)); + $aResultLang['LANGS_NAMES'] = \array_intersect_key($aResultLang['LANGS_NAMES'], $langs); + $aResultLang['LANGS_NAMES_EN'] = \array_intersect_key($aResultLang['LANGS_NAMES_EN'], $langs); + + $aResultLang = \array_replace_recursive( + $aResultLang, + \SnappyMail\L10n::load($sLanguage, ($bAdmin ? 'admin' : 'user')) + ); + + $this->Plugins()->ReadLang($sLanguage, $aResultLang); + + $sResult = \json_encode($aResultLang, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK); + + $sTimeFormat = ''; + $options = [$sLanguage, \substr($sLanguage, 0, 2), 'en']; + foreach ($options as $lang) { + $sFileName = APP_VERSION_ROOT_PATH.'app/localization/'.$lang.'/relativetimeformat.js'; + if (\is_file($sFileName)) { + $sTimeFormat = \preg_replace('/^\\s+/', '', \file_get_contents($sFileName)); + break; + } + } + + return "document.documentElement.lang = '{$sLanguage}';\nwindow.snappymailI18N={$sResult};\nDate.defineRelativeTimeFormat({$sTimeFormat});"; + } } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Themes.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Themes.php index 4341c5944..a2e8909f4 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Themes.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Themes.php @@ -89,7 +89,33 @@ trait Themes public function ValidateTheme(string $sTheme): string { - return \in_array($sTheme, $this->GetThemes()) ? - $sTheme : $this->Config()->Get('themes', 'default', 'Default'); + return \in_array($sTheme, $this->GetThemes()) ? $sTheme : $this->Config()->Get('themes', 'default', 'Default'); + } + + public function compileCss(string $sTheme, bool $bAdmin) : string + { + $bCustomTheme = '@custom' === \substr($sTheme, -7); + if ($bCustomTheme) { + $sTheme = \substr($sTheme, 0, -7); + } + + $oLess = new \LessPHP\lessc(); + $oLess->setFormatter('compressed'); + + $aResult = array(); + + $sThemeFile = ($bCustomTheme ? APP_INDEX_ROOT_PATH : APP_VERSION_ROOT_PATH).'themes/'.$sTheme.'/styles.less'; + + if (\is_file($sThemeFile)) { + $aResult[] = '@base: "' + . ($bCustomTheme ? Utils::WebPath() : Utils::WebVersionPath()) + . 'themes/'.$sTheme.'/";'; + + $aResult[] = \file_get_contents($sThemeFile); + } + + $aResult[] = $this->Plugins()->CompileCss($bAdmin); + + return $oLess->compile(\implode("\n", $aResult)); } } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Service.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Service.php index e4594cd9e..9ea8ddbd2 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Service.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Service.php @@ -159,19 +159,25 @@ class Service } $aTemplateParameters = $this->indexTemplateParameters($bAdmin); - $sLanguage = $this->oActions->GetLanguage($bAdmin); + + // TODO: Browser F5 issue +// $this->oServiceActions->getAuthAccountHash($bAdmin); +// $sLanguage = $this->oActions->GetLanguage($bAdmin); $sCacheFileName = ''; if ($this->oActions->Config()->Get('labs', 'cache_system_data', true) && !empty($aTemplateParameters['{{BaseHash}}'])) { - $sCacheFileName = 'TMPL:'.$sLanguage.$aTemplateParameters['{{BaseHash}}']; + // TODO: Browser F5 issue +// $sCacheFileName = 'TMPL:'.$sLanguage.$aTemplateParameters['{{BaseHash}}']; + $sCacheFileName = 'TMPL:'.$aTemplateParameters['{{BaseHash}}']; $sResult = $this->oActions->Cacher()->Get($sCacheFileName); } if (0 === \strlen($sResult)) { // $aTemplateParameters['{{BaseAppThemeCss}}'] = $this->oServiceActions->compileCss($this->oActions->GetTheme($bAdmin)); - $aTemplateParameters['{{BaseLanguage}}'] = $this->oServiceActions->compileLanguage($sLanguage, $bAdmin); + // TODO: Browser F5 issue +// $aTemplateParameters['{{BaseLanguage}}'] = $this->oServiceActions->compileLanguage($sLanguage, $bAdmin); $aTemplateParameters['{{BaseTemplates}}'] = $this->oServiceActions->compileTemplates($bAdmin, false); $sResult = \strtr(\file_get_contents(APP_VERSION_ROOT_PATH.'app/templates/Index.html'), $aTemplateParameters); @@ -231,7 +237,6 @@ class Service '{{BaseAppMainCssLink}}' => $this->staticPath('css/'.($bAdmin ? 'admin' : 'app').($bAppCssDebug ? '' : '.min').'.css'), '{{BaseAppThemeCssLink}}' => $this->oActions->ThemeLink($bAdmin), '{{BaseAppBootScript}}' => \file_get_contents(APP_VERSION_ROOT_PATH.'static/js/min/boot.min.js'), -// '{{BaseDir}}' => false && \in_array($sLanguage, array('ar', 'he', 'ur')) ? 'rtl' : 'ltr', '{{BaseAppManifestLink}}' => $this->staticPath('manifest.json'), '{{BaseAppBootCss}}' => \file_get_contents(APP_VERSION_ROOT_PATH.'static/css/boot.min.css'), '{{LoadingDescriptionEsc}}' => \htmlspecialchars($LoadingDescription, ENT_QUOTES|ENT_IGNORE, 'UTF-8'), diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php index 624fe8996..3de4a9d91 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php @@ -489,7 +489,7 @@ class ServiceActions if (0 === \strlen($sResult)) { - $sResult = $this->compileLanguage($sLanguage, $bAdmin); + $sResult = $this->oActions->compileLanguage($sLanguage, $bAdmin); if ($bCacheEnabled && 0 < \strlen($sCacheFileName)) { $this->Cacher()->Set($sCacheFileName, $sResult); @@ -580,7 +580,7 @@ class ServiceActions { try { - $sResult = $this->compileCss($sTheme); + $sResult = $this->oActions->compileCss($sTheme, $bAdmin); if ($bCacheEnabled) { @@ -883,26 +883,31 @@ class ServiceActions \header('Content-Type: application/javascript; charset=utf-8'); $this->oHttp->ServerNoCache(); - $sAuthAccountHash = ''; - if (!$bAdmin && 0 === \strlen($this->oActions->GetSpecAuthLogoutTokenWithDeletion())) - { - $sAuthAccountHash = $this->oActions->GetSpecAuthTokenWithDeletion(); - if (empty($sAuthAccountHash)) - { - $sAuthAccountHash = $this->oActions->GetSpecAuthToken(); - } + $sResult = 'rl.initData(' + .\json_encode($this->oActions->AppData($bAdmin, $this->getAuthAccountHash($bAdmin))) + .');'; - if (empty($sAuthAccountHash)) - { - $oAccount = $this->oActions->GetAccountFromSignMeToken(); - if ($oAccount) - { - try + $this->Logger()->Write($sResult, \MailSo\Log\Enumerations\Type::INFO, 'APPDATA'); + + return $sResult; + } + + public function getAuthAccountHash(bool $bAdmin) : string + { + static $sAuthAccountHash = null; + if (null === $sAuthAccountHash) { + $sAuthAccountHash = ''; + if (!$bAdmin && 0 === \strlen($this->oActions->GetSpecAuthLogoutTokenWithDeletion())) { + $sAuthAccountHash = $this->oActions->GetSpecAuthTokenWithDeletion(); + if (empty($sAuthAccountHash)) { + $sAuthAccountHash = $this->oActions->GetSpecAuthToken(); + } + if (empty($sAuthAccountHash)) { + $oAccount = $this->oActions->GetAccountFromSignMeToken(); + if ($oAccount) try { $this->oActions->CheckMailConnection($oAccount); - $this->oActions->AuthToken($oAccount); - $sAuthAccountHash = $this->oActions->GetSpecAuthToken(); } catch (\Throwable $oException) @@ -911,46 +916,10 @@ class ServiceActions $this->oActions->ClearSignMeData($oAccount); } } + $this->oActions->SetSpecAuthToken($sAuthAccountHash); } - - $this->oActions->SetSpecAuthToken($sAuthAccountHash); } - - $sResult = 'rl.initData(' - .\json_encode($this->oActions->AppData($bAdmin, $sAuthAccountHash)) - .');'; - - $this->Logger()->Write($sResult, \MailSo\Log\Enumerations\Type::INFO, 'APPDATA'); - - return $sResult; - } - - public function compileCss(string $sTheme) : string - { - $bCustomTheme = '@custom' === \substr($sTheme, -7); - if ($bCustomTheme) { - $sTheme = \substr($sTheme, 0, -7); - } - - $oLess = new \LessPHP\lessc(); - $oLess->setFormatter('compressed'); - - $aResult = array(); - - $sThemeFile = ($bCustomTheme ? APP_INDEX_ROOT_PATH : APP_VERSION_ROOT_PATH).'themes/'.$sTheme.'/styles.less'; - - if (\is_file($sThemeFile)) - { - $aResult[] = '@base: "' - . ($bCustomTheme ? Utils::WebPath() : Utils::WebVersionPath()) - . 'themes/'.$sTheme.'/";'; - - $aResult[] = \file_get_contents($sThemeFile); - } - - $aResult[] = $this->Plugins()->CompileCss($bAdmin); - - return $oLess->compile(\implode("\n", $aResult)); + return $sAuthAccountHash; } public function compileTemplates(bool $bAdmin = false, bool $bJsOutput = true) : string @@ -975,35 +944,4 @@ class ServiceActions return $bJsOutput ? 'rl.TEMPLATES='.\MailSo\Base\Utils::Php2js($sHtml, $this->Logger()).';' : $sHtml; } - - public function compileLanguage(string $sLanguage, bool $bAdmin = false) : string - { - $sLanguage = \strtr($sLanguage, '_', '-'); - - $aResultLang = \json_decode(\file_get_contents(APP_VERSION_ROOT_PATH.'app/localization/langs.json'), true); - $langs = \array_flip(\SnappyMail\L10n::getLanguages($bAdmin)); - $aResultLang['LANGS_NAMES'] = \array_intersect_key($aResultLang['LANGS_NAMES'], $langs); - $aResultLang['LANGS_NAMES_EN'] = \array_intersect_key($aResultLang['LANGS_NAMES_EN'], $langs); - - $aResultLang = \array_replace_recursive( - $aResultLang, - \SnappyMail\L10n::load($sLanguage, ($bAdmin ? 'admin' : 'user')) - ); - - $this->Plugins()->ReadLang($sLanguage, $aResultLang); - - $sResult = \json_encode($aResultLang, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK); - - $sTimeFormat = ''; - $options = [$sLanguage, \substr($sLanguage, 0, 2), 'en']; - foreach ($options as $lang) { - $sFileName = APP_VERSION_ROOT_PATH.'app/localization/'.$lang.'/relativetimeformat.js'; - if (\is_file($sFileName)) { - $sTimeFormat = \preg_replace('/^\\s+/', '', \file_get_contents($sFileName)); - break; - } - } - - return "document.documentElement.lang = '{$sLanguage}';\nwindow.snappymailI18N={$sResult};\nDate.defineRelativeTimeFormat({$sTimeFormat});"; - } } diff --git a/snappymail/v/0.0.0/app/templates/Index.html b/snappymail/v/0.0.0/app/templates/Index.html index 3358398bb..b37d1874d 100644 --- a/snappymail/v/0.0.0/app/templates/Index.html +++ b/snappymail/v/0.0.0/app/templates/Index.html @@ -1,5 +1,5 @@ - +