Embed admin panel into Nextcloud (with autologin)

This commit is contained in:
the-djmaze 2022-10-31 13:35:22 +01:00
parent 028f4ecb08
commit 6e4be76664
8 changed files with 73 additions and 36 deletions

View file

@ -94,15 +94,15 @@
background-image: linear-gradient(to bottom, #f4f4f4, #dfdfdf) !important; background-image: linear-gradient(to bottom, #f4f4f4, #dfdfdf) !important;
background-repeat: repeat-x !important; background-repeat: repeat-x !important;
} }
#V-AdminPane > .b-toolbar, #V-AdminPane > .b-toolbar {
color: var(--main-color);
text-shadow: none;
}
#V-PopupsCompose header { #V-PopupsCompose header {
background-color: #0082c9; background-color: #0082c9;
background-image: linear-gradient(40deg, #0082c9 0%, #30b6ff 100%); background-image: linear-gradient(40deg, #0082c9 0%, #30b6ff 100%);
color: #fff; color: #fff;
} }
#V-AdminPane > .b-toolbar h4::before {
content: 'Nextcloud: ';
}
.messageList, .messageView { .messageList, .messageView {
border-color: var(--color-border); border-color: var(--color-border);

View file

@ -1,6 +1,9 @@
/* /*
This stylesheet is used when SnappyMail runs in embed mode. This stylesheet is used when SnappyMail runs in embed mode.
*/ */
#content.app-snappymail {
max-height: 100%;
}
#content #rl-app { #content #rl-app {
position: relative; position: relative;
min-width: 100%; min-width: 100%;
@ -28,7 +31,8 @@ This stylesheet is used when SnappyMail runs in embed mode.
#content[class*="app-"] #rl-app textarea, #content[class*="app-"] #rl-app textarea,
#content[class*="app-"] #rl-app input, #content[class*="app-"] #rl-app input,
#content[class*="app-"] #rl-app .input-block-level, #content[class*="app-"] #rl-app .input-block-level,
#content[class*="app-"] #rl-app .icon-spinner #content[class*="app-"] #rl-app .icon-spinner,
#content[class*="app-"] #rl-app #V-AdminPane #rl-settings-subscreen
{ {
box-sizing: border-box; box-sizing: border-box;
} }
@ -37,6 +41,10 @@ This stylesheet is used when SnappyMail runs in embed mode.
min-height: 100%; min-height: 100%;
} }
#V-AdminPane .btn-logout {
display: none;
}
#rl-app select, #rl-app select,
#rl-app input #rl-app input
{ {

View file

@ -74,7 +74,13 @@ class PageController extends Controller
*/ */
private static function index_embed() private static function index_embed()
{ {
if (!empty($_SERVER['QUERY_STRING'])) { /*
$oConfig = \RainLoop\Api::Config();
$bAdmin = empty($_SERVER['QUERY_STRING']) || $oConfig->Get('security', 'admin_panel_key', 'admin') != $_SERVER['QUERY_STRING'];
*/
$bAdmin = !empty($_SERVER['QUERY_STRING']) && 'admin' == $_SERVER['QUERY_STRING'];
if (!$bAdmin && !empty($_SERVER['QUERY_STRING'])) {
SnappyMailHelper::startApp(true); SnappyMailHelper::startApp(true);
} }
@ -84,7 +90,7 @@ class PageController extends Controller
SnappyMailHelper::startApp(); SnappyMailHelper::startApp();
$oConfig = \RainLoop\Api::Config(); $oConfig = \RainLoop\Api::Config();
$oActions = \RainLoop\Api::Actions(); $oActions = $bAdmin ? new \RainLoop\ActionsAdmin() : \RainLoop\Api::Actions();
$oHttp = \MailSo\Base\Http::SingletonInstance(); $oHttp = \MailSo\Base\Http::SingletonInstance();
$oServiceActions = new \RainLoop\ServiceActions($oHttp, $oActions); $oServiceActions = new \RainLoop\ServiceActions($oHttp, $oActions);
$sAppJsMin = $oConfig->Get('labs', 'use_app_debug_js', false) ? '' : '.min'; $sAppJsMin = $oConfig->Get('labs', 'use_app_debug_js', false) ? '' : '.min';
@ -96,23 +102,24 @@ class PageController extends Controller
// \RainLoop\Service::setCSP($sScriptNonce); // \RainLoop\Service::setCSP($sScriptNonce);
$params = [ $params = [
'Admin' => $bAdmin ? 1 : 0,
'LoadingDescriptionEsc' => \htmlspecialchars($oConfig->Get('webmail', 'loading_description', 'SnappyMail'), ENT_QUOTES|ENT_IGNORE, 'UTF-8'), 'LoadingDescriptionEsc' => \htmlspecialchars($oConfig->Get('webmail', 'loading_description', 'SnappyMail'), ENT_QUOTES|ENT_IGNORE, 'UTF-8'),
'BaseTemplates' => \RainLoop\Utils::ClearHtmlOutput($oServiceActions->compileTemplates(false)), 'BaseTemplates' => \RainLoop\Utils::ClearHtmlOutput($oServiceActions->compileTemplates($bAdmin)),
'BaseAppBootScript' => \file_get_contents(APP_VERSION_ROOT_PATH.'static/js'.($sAppJsMin ? '/min' : '').'/boot'.$sAppJsMin.'.js'), 'BaseAppBootScript' => \file_get_contents(APP_VERSION_ROOT_PATH.'static/js'.($sAppJsMin ? '/min' : '').'/boot'.$sAppJsMin.'.js'),
'BaseAppBootScriptNonce' => $sScriptNonce, 'BaseAppBootScriptNonce' => $sScriptNonce,
'BaseLanguage' => $oActions->compileLanguage($sLanguage, false), 'BaseLanguage' => $oActions->compileLanguage($sLanguage, $bAdmin),
'BaseAppBootCss' => \file_get_contents(APP_VERSION_ROOT_PATH.'static/css/boot'.$sAppCssMin.'.css'), 'BaseAppBootCss' => \file_get_contents(APP_VERSION_ROOT_PATH.'static/css/boot'.$sAppCssMin.'.css'),
'BaseAppThemeCssLink' => $oActions->ThemeLink(false), 'BaseAppThemeCssLink' => $oActions->ThemeLink($bAdmin),
'BaseAppThemeCss' => \preg_replace( 'BaseAppThemeCss' => \preg_replace(
'/\\s*([:;{},]+)\\s*/s', '/\\s*([:;{},]+)\\s*/s',
'$1', '$1',
$oActions->compileCss($oActions->GetTheme(false), false) $oActions->compileCss($oActions->GetTheme($bAdmin), $bAdmin)
) )
]; ];
// Nextcloud html encodes, so addHeader('style') is not possible // Nextcloud html encodes, so addHeader('style') is not possible
// \OCP\Util::addHeader('style', ['id'=>'app-boot-css'], \file_get_contents(APP_VERSION_ROOT_PATH.'static/css/boot'.$sAppCssMin.'.css')); // \OCP\Util::addHeader('style', ['id'=>'app-boot-css'], \file_get_contents(APP_VERSION_ROOT_PATH.'static/css/boot'.$sAppCssMin.'.css'));
\OCP\Util::addHeader('link', ['type'=>'text/css','rel'=>'stylesheet','href'=>\RainLoop\Utils::WebStaticPath('css/app'.$sAppCssMin.'.css')], ''); \OCP\Util::addHeader('link', ['type'=>'text/css','rel'=>'stylesheet','href'=>\RainLoop\Utils::WebStaticPath('css/'.($bAdmin?'admin':'app').$sAppCssMin.'.css')], '');
// \OCP\Util::addHeader('style', ['id'=>'app-theme-style','data-href'=>$params['BaseAppThemeCssLink']], $params['BaseAppThemeCss']); // \OCP\Util::addHeader('style', ['id'=>'app-theme-style','data-href'=>$params['BaseAppThemeCssLink']], $params['BaseAppThemeCss']);
$response = new TemplateResponse('snappymail', 'index_embed', $params); $response = new TemplateResponse('snappymail', 'index_embed', $params);

View file

@ -29,7 +29,9 @@ class AdminSettings implements ISettings
} }
$uid = \OC::$server->getUserSession()->getUser()->getUID(); $uid = \OC::$server->getUserSession()->getUser()->getUID();
if (\OC_User::isAdminUser($uid)) { if (\OC_User::isAdminUser($uid)) {
$parameters['snappymail-admin-panel-link'] = SnappyMailHelper::getAppUrl().'?admin'; // $parameters['snappymail-admin-panel-link'] = SnappyMailHelper::getAppUrl().'?admin';
$parameters['snappymail-admin-panel-link'] =
\OC::$server->getURLGenerator()->linkToRoute('snappymail.page.index').'?admin';
} }
\OCA\SnappyMail\Util\SnappyMailHelper::loadApp(); \OCA\SnappyMail\Util\SnappyMailHelper::loadApp();

View file

@ -57,26 +57,28 @@ class SnappyMailHelper
) { ) {
$aDomains = \array_unique([ $aDomains = \array_unique([
'nextcloud', 'nextcloud',
\preg_replace('/:\d+/$','',$_SERVER['HTTP_HOST']), \preg_replace('/:\d+$/','',$_SERVER['HTTP_HOST']),
$_SERVER['SERVER_NAME'], $_SERVER['SERVER_NAME'],
\gethostname() \gethostname()
]); ]);
foreach ($aDomains as $i => $sDomain) { foreach ($aDomains as $i => $sDomain) {
$oProvider = \RainLoop\Api::Actions()->DomainProvider(); if ($sDomain) {
$oDomain = $oProvider->Load($sDomain); $oProvider = \RainLoop\Api::Actions()->DomainProvider();
if (!($oDomain instanceof \RainLoop\Model\Domain)) { $oDomain = $oProvider->Load($sDomain);
$oDomain = new \RainLoop\Model\Domain($sDomain); if (!($oDomain instanceof \RainLoop\Model\Domain)) {
$bShortLogin = !$i; $oDomain = new \RainLoop\Model\Domain($sDomain);
$iSecurityType = \MailSo\Net\Enumerations\ConnectionSecurityType::NONE; $bShortLogin = !$i;
$oDomain->SetConfig( $iSecurityType = \MailSo\Net\Enumerations\ConnectionSecurityType::NONE;
'localhost', 143, $iSecurityType, $bShortLogin, $oDomain->SetConfig(
true, 'localhost', 4190, $iSecurityType, 'localhost', 143, $iSecurityType, $bShortLogin,
'localhost', 25, $iSecurityType, $bShortLogin, true, false, false, true, 'localhost', 4190, $iSecurityType,
''); 'localhost', 25, $iSecurityType, $bShortLogin, true, false, false,
$oProvider->Save($oDomain); '');
if (!$oConfig->Get('login', 'default_domain', '')) { $oProvider->Save($oDomain);
$oConfig->Set('login', 'default_domain', 'nextcloud'); if (!$oConfig->Get('login', 'default_domain', '')) {
$bSave = true; $oConfig->Set('login', 'default_domain', 'nextcloud');
$bSave = true;
}
} }
} }
} }
@ -93,8 +95,10 @@ class SnappyMailHelper
$oActions = \RainLoop\Api::Actions(); $oActions = \RainLoop\Api::Actions();
$oConfig = \RainLoop\Api::Config(); $oConfig = \RainLoop\Api::Config();
if (isset($_GET[$oConfig->Get('security', 'admin_panel_key', 'admin')])) { if (isset($_GET[$oConfig->Get('security', 'admin_panel_key', 'admin')])) {
/* // TODO: check Nextcloud group if allowed if ($oConfig->Get('security', 'allow_admin_panel', true)
if ($oConfig->Get('security', 'allow_admin_panel', true) && !$oActions->IsAdminLoggined(false)) { && \OC_User::isAdminUser(\OC::$server->getUserSession()->getUser()->getUID())
&& !$oActions->IsAdminLoggined(false)
) {
$sRand = \MailSo\Base\Utils::Sha1Rand(); $sRand = \MailSo\Base\Utils::Sha1Rand();
if ($oActions->Cacher(null, true)->Set(\RainLoop\KeyPathHelper::SessionAdminKey($sRand), \time())) { if ($oActions->Cacher(null, true)->Set(\RainLoop\KeyPathHelper::SessionAdminKey($sRand), \time())) {
$sToken = \RainLoop\Utils::EncodeKeyValuesQ(array('token', $sRand)); $sToken = \RainLoop\Utils::EncodeKeyValuesQ(array('token', $sRand));
@ -102,7 +106,6 @@ class SnappyMailHelper
\RainLoop\Utils::SetCookie('smadmin', $sToken); \RainLoop\Utils::SetCookie('smadmin', $sToken);
} }
} }
*/
} else if (!$oActions->getMainAccountFromToken(false)) { } else if (!$oActions->getMainAccountFromToken(false)) {
$aCredentials = SnappyMailHelper::getLoginCredentials(); $aCredentials = SnappyMailHelper::getLoginCredentials();
if ($aCredentials[0] && $aCredentials[1]) { if ($aCredentials[0] && $aCredentials[1]) {

View file

@ -6,7 +6,7 @@
<br /> <br />
<?php if ($_['snappymail-admin-panel-link']) { ?> <?php if ($_['snappymail-admin-panel-link']) { ?>
<p> <p>
<a href="<?php echo $_['snappymail-admin-panel-link'] ?>" target="_blank" style="text-decoration: underline"> <a href="<?php echo $_['snappymail-admin-panel-link'] ?>" style="text-decoration: underline">
<?php echo($l->t('Go to SnappyMail Webmail admin panel')); ?> <?php echo($l->t('Go to SnappyMail Webmail admin panel')); ?>
</a> </a>
<?php if ($_['snappymail-admin-password']) { ?> <?php if ($_['snappymail-admin-password']) { ?>

View file

@ -1,6 +1,6 @@
<style id="app-boot-css"><?php echo $_['BaseAppBootCss']; ?></style> <style id="app-boot-css"><?php echo $_['BaseAppBootCss']; ?></style>
<style id="app-theme-style" data-href="<?php echo $_['BaseAppThemeCssLink']; ?>"><?php echo $_['BaseAppThemeCss']; ?></style> <style id="app-theme-style" data-href="<?php echo $_['BaseAppThemeCssLink']; ?>"><?php echo $_['BaseAppThemeCss']; ?></style>
<div id="rl-app" data-admin="0" spellcheck="false"> <div id="rl-app" data-admin="<?php echo $_['Admin']; ?>" spellcheck="false">
<div id="rl-loading"> <div id="rl-loading">
<div id="rl-loading-desc"><?php echo $_['LoadingDescriptionEsc']; ?></div> <div id="rl-loading-desc"><?php echo $_['LoadingDescriptionEsc']; ?></div>
<i class="icon-spinner"></i> <i class="icon-spinner"></i>

View file

@ -123,6 +123,7 @@
if (VEVENT) { if (VEVENT) {
VEVENT.rawText = text; VEVENT.rawText = text;
VEVENT.isCancelled = () => VEVENT.STATUS?.includes('CANCELLED'); VEVENT.isCancelled = () => VEVENT.STATUS?.includes('CANCELLED');
VEVENT.isConfirmed = () => VEVENT.STATUS?.includes('CONFIRMED');
VEVENT.shouldReply = () => VEVENT.METHOD?.includes('REPLY'); VEVENT.shouldReply = () => VEVENT.METHOD?.includes('REPLY');
console.dir({ console.dir({
isCancelled: VEVENT.isCancelled(), isCancelled: VEVENT.isCancelled(),
@ -138,6 +139,7 @@
}); });
let template = document.getElementById('MailMessageView'); let template = document.getElementById('MailMessageView');
let cfg = rl.settings.get('Nextcloud');
const attachmentsControls = template.content.querySelector('.attachmentsControls'); const attachmentsControls = template.content.querySelector('.attachmentsControls');
if (attachmentsControls) { if (attachmentsControls) {
@ -149,14 +151,29 @@
</span>`)); </span>`));
// https://github.com/nextcloud/calendar/issues/4684 // https://github.com/nextcloud/calendar/issues/4684
let cfg = rl.settings.get('Nextcloud');
if (cfg.CalDAV) { if (cfg.CalDAV) {
attachmentsControls.append(Element.fromHTML(`<span data-bind="visible: nextcloudICS" data-icon="📅"> attachmentsControls.append(Element.fromHTML(`<span data-bind="visible: nextcloudICS" data-icon="📅">
<span class="g-ui-link" data-bind="click: nextcloudSaveICS" data-i18n="NEXTCLOUD/SAVE_ICS"></span> <span class="g-ui-link" data-bind="click: nextcloudSaveICS" data-i18n="NEXTCLOUD/SAVE_ICS"></span>
</span>`)); </span>`));
} }
} }
/*
// https://github.com/the-djmaze/snappymail/issues/592
if (cfg.CalDAV) {
const attachmentsPlace = template.content.querySelector('.attachmentsPlace');
attachmentsPlace.after(Element.fromHTML(`
<table data-bind="if: nextcloudICS, visible: nextcloudICS"><tbody style="white-space:pre">
<tr><td>Summary</td><td data-icon="📅" data-bind="text: nextcloudICS().SUMMARY"></td></tr>
<tr><td>Organizer</td><td data-bind="text: nextcloudICS().ORGANIZER"></td></tr>
<tr><td>Start</td><td data-bind="text: nextcloudICS().DTSTART"></td></tr>
<tr><td>End</td><td data-bind="text: nextcloudICS().DTEND"></td></tr>
<tr><td>Transparency</td><td data-bind="text: nextcloudICS().TRANSP"></td></tr>
<tr data-bind="foreach: nextcloudICS().ATTENDEE">
<td></td><td data-bind="text: $data.replace(/;/g,';\\n')"></td>
</tr>
</tbody></table>`));
}
*/
const msgMenu = template.content.querySelector('#more-view-dropdown-id + menu'); const msgMenu = template.content.querySelector('#more-view-dropdown-id + menu');
if (msgMenu) { if (msgMenu) {
msgMenu.append(Element.fromHTML(`<li role="presentation"> msgMenu.append(Element.fromHTML(`<li role="presentation">