[Web] dark mode logo support

Signed-off-by: Kristian Feldsam <feldsam@gmail.com>
This commit is contained in:
Kristian Feldsam 2023-09-03 18:49:12 +02:00
parent 372b1c7bbc
commit 3540075b61
11 changed files with 69 additions and 24 deletions

View File

@ -108,6 +108,7 @@ $template_data = [
'rsettings' => $rsettings, 'rsettings' => $rsettings,
'rspamd_regex_maps' => $rspamd_regex_maps, 'rspamd_regex_maps' => $rspamd_regex_maps,
'logo_specs' => customize('get', 'main_logo_specs'), 'logo_specs' => customize('get', 'main_logo_specs'),
'logo_dark_specs' => customize('get', 'main_logo_dark_specs'),
'ip_check' => customize('get', 'ip_check'), 'ip_check' => customize('get', 'ip_check'),
'password_complexity' => password_complexity('get'), 'password_complexity' => password_complexity('get'),
'show_rspamd_global_filters' => @$_SESSION['show_rspamd_global_filters'], 'show_rspamd_global_filters' => @$_SESSION['show_rspamd_global_filters'],

View File

@ -24,9 +24,10 @@ function customize($_action, $_item, $_data = null) {
} }
switch ($_item) { switch ($_item) {
case 'main_logo': case 'main_logo':
if (in_array($_data['main_logo']['type'], array('image/gif', 'image/jpeg', 'image/pjpeg', 'image/x-png', 'image/png', 'image/svg+xml'))) { case 'main_logo_dark':
if (in_array($_data[$_item]['type'], array('image/gif', 'image/jpeg', 'image/pjpeg', 'image/x-png', 'image/png', 'image/svg+xml'))) {
try { try {
if (file_exists($_data['main_logo']['tmp_name']) !== true) { if (file_exists($_data[$_item]['tmp_name']) !== true) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data), 'log' => array(__FUNCTION__, $_action, $_item, $_data),
@ -34,7 +35,7 @@ function customize($_action, $_item, $_data = null) {
); );
return false; return false;
} }
$image = new Imagick($_data['main_logo']['tmp_name']); $image = new Imagick($_data[$_item]['tmp_name']);
if ($image->valid() !== true) { if ($image->valid() !== true) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
@ -63,7 +64,7 @@ function customize($_action, $_item, $_data = null) {
return false; return false;
} }
try { try {
$redis->Set('MAIN_LOGO', 'data:' . $_data['main_logo']['type'] . ';base64,' . base64_encode(file_get_contents($_data['main_logo']['tmp_name']))); $redis->Set(strtoupper($_item), 'data:' . $_data[$_item]['type'] . ';base64,' . base64_encode(file_get_contents($_data[$_item]['tmp_name'])));
} }
catch (RedisException $e) { catch (RedisException $e) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
@ -201,8 +202,9 @@ function customize($_action, $_item, $_data = null) {
} }
switch ($_item) { switch ($_item) {
case 'main_logo': case 'main_logo':
case 'main_logo_dark':
try { try {
if ($redis->del('MAIN_LOGO')) { if ($redis->del(strtoupper($_item))) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'success', 'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_item, $_data), 'log' => array(__FUNCTION__, $_action, $_item, $_data),
@ -239,8 +241,9 @@ function customize($_action, $_item, $_data = null) {
return ($app_links) ? $app_links : false; return ($app_links) ? $app_links : false;
break; break;
case 'main_logo': case 'main_logo':
case 'main_logo_dark':
try { try {
return $redis->get('MAIN_LOGO'); return $redis->get(strtoupper($_item));
} }
catch (RedisException $e) { catch (RedisException $e) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
@ -277,9 +280,14 @@ function customize($_action, $_item, $_data = null) {
} }
break; break;
case 'main_logo_specs': case 'main_logo_specs':
case 'main_logo_dark_specs':
try { try {
$image = new Imagick(); $image = new Imagick();
if($_item == 'main_logo_specs') {
$img_data = explode('base64,', customize('get', 'main_logo')); $img_data = explode('base64,', customize('get', 'main_logo'));
} else {
$img_data = explode('base64,', customize('get', 'main_logo_dark'));
}
if ($img_data[1]) { if ($img_data[1]) {
$image->readImageBlob(base64_decode($img_data[1])); $image->readImageBlob(base64_decode($img_data[1]));
return $image->identifyImage(); return $image->identifyImage();

View File

@ -40,6 +40,7 @@ $globalVariables = [
'ui_texts' => $UI_TEXTS, 'ui_texts' => $UI_TEXTS,
'css_path' => '/cache/'.basename($CSSPath), 'css_path' => '/cache/'.basename($CSSPath),
'logo' => customize('get', 'main_logo'), 'logo' => customize('get', 'main_logo'),
'logo_dark' => customize('get', 'main_logo_dark'),
'available_languages' => $AVAILABLE_LANGUAGES, 'available_languages' => $AVAILABLE_LANGUAGES,
'lang' => $lang, 'lang' => $lang,
'skip_sogo' => (getenv('SKIP_SOGO') == 'y'), 'skip_sogo' => (getenv('SKIP_SOGO') == 'y'),

View File

@ -120,10 +120,14 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admi
if (isset($_POST["submit_main_logo"])) { if (isset($_POST["submit_main_logo"])) {
if ($_FILES['main_logo']['error'] == 0) { if ($_FILES['main_logo']['error'] == 0) {
customize('add', 'main_logo', $_FILES); customize('add', 'main_logo', $_FILES);
}
if ($_FILES['main_logo_dark']['error'] == 0) {
customize('add', 'main_logo_dark', $_FILES);
} }
} }
if (isset($_POST["reset_main_logo"])) { if (isset($_POST["reset_main_logo"])) {
customize('delete', 'main_logo'); customize('delete', 'main_logo');
customize('delete', 'main_logo_dark');
} }
// Some actions will not be available via API // Some actions will not be available via API
if (isset($_POST["license_validate_now"])) { if (isset($_POST["license_validate_now"])) {

View File

@ -314,19 +314,28 @@ $(document).ready(function() {
$('#dark-mode-toggle').click(toggleDarkMode); $('#dark-mode-toggle').click(toggleDarkMode);
if ($('#dark-mode-theme').length) { if ($('#dark-mode-theme').length) {
$('#dark-mode-toggle').prop('checked', true); $('#dark-mode-toggle').prop('checked', true);
$('.main-logo').addClass('d-none');
$('.main-logo-dark').removeClass('d-none');
if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_light.png'); if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_light.png');
if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_light.png'); if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_light.png');
} else {
$('.main-logo').removeClass('d-none');
$('.main-logo-dark').addClass('d-none');
} }
function toggleDarkMode(){ function toggleDarkMode(){
if($('#dark-mode-theme').length){ if($('#dark-mode-theme').length){
$('#dark-mode-theme').remove(); $('#dark-mode-theme').remove();
$('#dark-mode-toggle').prop('checked', false); $('#dark-mode-toggle').prop('checked', false);
$('.main-logo').removeClass('d-none');
$('.main-logo-dark').addClass('d-none');
if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_dark.png'); if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_dark.png');
if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_dark.png'); if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_dark.png');
localStorage.setItem('theme', 'light'); localStorage.setItem('theme', 'light');
}else{ }else{
$('head').append('<link id="dark-mode-theme" rel="stylesheet" type="text/css" href="/css/themes/mailcow-darkmode.css">'); $('head').append('<link id="dark-mode-theme" rel="stylesheet" type="text/css" href="/css/themes/mailcow-darkmode.css">');
$('#dark-mode-toggle').prop('checked', true); $('#dark-mode-toggle').prop('checked', true);
$('.main-logo').addClass('d-none');
$('.main-logo-dark').removeClass('d-none');
if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_light.png'); if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_light.png');
if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_light.png'); if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_light.png');
localStorage.setItem('theme', 'dark'); localStorage.setItem('theme', 'dark');

View File

@ -149,6 +149,8 @@
"ays": "Are you sure you want to proceed?", "ays": "Are you sure you want to proceed?",
"ban_list_info": "See a list of banned IPs below: <b>network (remaining ban time) - [actions]</b>.<br />IPs queued to be unbanned will be removed from the active ban list within a few seconds.<br />Red labels indicate active permanent bans by blacklisting.", "ban_list_info": "See a list of banned IPs below: <b>network (remaining ban time) - [actions]</b>.<br />IPs queued to be unbanned will be removed from the active ban list within a few seconds.<br />Red labels indicate active permanent bans by blacklisting.",
"change_logo": "Change logo", "change_logo": "Change logo",
"logo_normal_label": "Normal",
"logo_dark_label": "Inverted for dark mode",
"configuration": "Configuration", "configuration": "Configuration",
"convert_html_to_text": "Convert HTML to plain text", "convert_html_to_text": "Convert HTML to plain text",
"cors_settings": "CORS Settings", "cors_settings": "CORS Settings",

View File

@ -0,0 +1,9 @@
<div class="thumbnail mb-4">
<img class="img-thumbnail mb-4{% if dark %} bg-black{% endif %}" src="{{ logo }}" alt="mailcow logo">
<div class="caption">
<span class="badge fs-5 bg-info">{{ logo_specs.geometry.width }}x{{ logo_specs.geometry.height }} px</span>
<span class="badge fs-5 bg-info">{{ logo_specs.mimetype }}</span>
<span class="badge fs-5 bg-info">{{ logo_specs.fileSize }}</span>
</div>
</div>

View File

@ -10,22 +10,26 @@
<legend><i class="bi bi-file-image"></i> {{ lang.admin.change_logo }}</legend><hr /> <legend><i class="bi bi-file-image"></i> {{ lang.admin.change_logo }}</legend><hr />
<p class="text-muted">{{ lang.admin.logo_info }}</p> <p class="text-muted">{{ lang.admin.logo_info }}</p>
<form class="form-inline" role="form" method="post" enctype="multipart/form-data"> <form class="form-inline" role="form" method="post" enctype="multipart/form-data">
<p> <div class="mb-4">
<input class="mb-4" type="file" name="main_logo" accept="image/gif, image/jpeg, image/pjpeg, image/x-png, image/png, image/svg+xml"><br> <label for="main_logo_input" class="form-label">{{ lang.admin.logo_normal_label }}</label>
<input class="form-control" id="main_logo_input" type="file" name="main_logo" accept="image/gif, image/jpeg, image/pjpeg, image/x-png, image/png, image/svg+xml">
</div>
<div class="mb-4">
<label for="main_logo_dark_input" class="form-label">{{ lang.admin.logo_dark_label }}</label>
<input class="form-control" id="main_logo_dark_input" type="file" name="main_logo_dark" accept="image/gif, image/jpeg, image/pjpeg, image/x-png, image/png, image/svg+xml">
</div>
<button name="submit_main_logo" type="submit" class="btn btn-sm d-block d-sm-inline btn-secondary"><i class="bi bi-upload"></i> {{ lang.admin.upload }}</button> <button name="submit_main_logo" type="submit" class="btn btn-sm d-block d-sm-inline btn-secondary"><i class="bi bi-upload"></i> {{ lang.admin.upload }}</button>
</p>
</form> </form>
{% if logo %} {% if logo or logo_dark %}
<div class="row"> <div class="row mt-4">
<div class="col-sm-4"> <div class="col-sm-4">
<div class="thumbnail"> {% if logo %}
<img class="img-thumbnail" src="{{ logo }}" alt="mailcow logo"> {% include 'admin/customize/logo.twig' %}
<div class="caption"> {% endif %}
<span class="badge fs-5 bg-info">{{ logo_specs.geometry.width }}x{{ logo_specs.geometry.height }} px</span> {% if logo_dark %}
<span class="badge fs-5 bg-info">{{ logo_specs.mimetype }}</span> {% include 'admin/customize/logo.twig' with {'logo': logo_dark, 'logo_specs': logo_dark_specs, 'dark': 1} %}
<span class="badge fs-5 bg-info">{{ logo_specs.fileSize }}</span> {% endif %}
</div>
</div>
<hr> <hr>
<form class="form-inline" role="form" method="post"> <form class="form-inline" role="form" method="post">
<p><button name="reset_main_logo" type="submit" class="btn btn-sm d-block d-sm-inline btn-secondary">{{ lang.admin.reset_default }}</button></p> <p><button name="reset_main_logo" type="submit" class="btn btn-sm d-block d-sm-inline btn-secondary">{{ lang.admin.reset_default }}</button></p>

View File

@ -31,7 +31,10 @@
{% block navbar %} {% block navbar %}
<nav class="navbar navbar-expand-lg navbar-light bg-light navbar-fixed-top p-0"> <nav class="navbar navbar-expand-lg navbar-light bg-light navbar-fixed-top p-0">
<div class="container-fluid"> <div class="container-fluid">
<a class="navbar-brand" href="/"><img alt="mailcow-logo" src="{{ logo|default('/img/cow_mailcow.svg') }}"></a> <a class="navbar-brand" href="/">
<img class="main-logo" alt="mailcow-logo" src="{{ logo|default('/img/cow_mailcow.svg') }}">
<img class="main-logo-dark" alt="mailcow-logo-dark" src="{{ logo_dark|default('/img/cow_mailcow.svg') }}">
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-list fs-3"></i> <i class="bi bi-list fs-3"></i>
</button> </button>

View File

@ -37,7 +37,8 @@
<div class="card-body"> <div class="card-body">
<div class="row"> <div class="row">
<div class="col-sm-12 col-md-4 d-flex flex-column"> <div class="col-sm-12 col-md-4 d-flex flex-column">
<img class="img-responsive my-auto m-auto" alt="mailcow-logo" style="max-width: 85%; max-height: 85%;" src="{{ logo|default('/img/cow_mailcow.svg') }}"> <img class="main-logo img-responsive my-auto m-auto" alt="mailcow-logo" style="max-width: 85%; max-height: 85%;" src="{{ logo|default('/img/cow_mailcow.svg') }}">
<img class="main-logo-dark img-responsive my-auto m-auto" alt="mailcow-logo-dark" style="max-width: 85%; max-height: 85%;" src="{{ logo_dark|default('/img/cow_mailcow.svg') }}">
</div> </div>
<div class="col-sm-12 col-md-8"> <div class="col-sm-12 col-md-8">
<div class="table-responsive" style="margin-top: 10px;"> <div class="table-responsive" style="margin-top: 10px;">

View File

@ -14,7 +14,10 @@
</div> </div>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="text-center mailcow-logo mb-4"><img src="{{ logo|default('/img/cow_mailcow.svg') }}" alt="mailcow"></div> <div class="text-center mailcow-logo mb-4">
<img class="main-logo" src="{{ logo|default('/img/cow_mailcow.svg') }}" alt="mailcow">
<img class="main-logo-dark" src="{{ logo_dark|default('/img/cow_mailcow.svg') }}" alt="mailcow-logo-dark">
</div>
{% if ui_texts.ui_announcement_text and ui_texts.ui_announcement_active %} {% if ui_texts.ui_announcement_text and ui_texts.ui_announcement_active %}
<div class="my-4 alert alert-{{ ui_texts.ui_announcement_type }} rot-enc ui-announcement-alert">{{ ui_texts.ui_announcement_text|rot13 }}</div> <div class="my-4 alert alert-{{ ui_texts.ui_announcement_type }} rot-enc ui-announcement-alert">{{ ui_texts.ui_announcement_text|rot13 }}</div>
{% endif %} {% endif %}