This commit is contained in:
Konstantinos Spartalis 2026-05-25 23:39:08 +03:00 committed by GitHub
commit 67655d1764
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 102 additions and 8 deletions

View file

@ -383,14 +383,19 @@ class ControllerBase extends ControllerRoot
$this->view->menuBreadcrumbs = $menu->getBreadcrumbs();
// set theme in ui_theme template var, let template handle its defaults (if there is no theme).
if (
$cnf->object()->theme->count() > 0 && !empty($cnf->object()->theme) &&
$theme_config = (string)$cnf->object()->theme;
if ($theme_config === 'opnsense-auto') {
$this->view->ui_theme = 'opnsense';
$this->view->theme_auto = true;
} elseif (
$cnf->object()->theme->count() > 0 && !empty($theme_config) &&
(
is_dir('/usr/local/opnsense/www/themes/' . (string)$cnf->object()->theme) ||
is_dir('/usr/local/opnsense/www/themes/' . $theme_config) ||
!is_dir('/usr/local/opnsense/www/themes')
)
) {
$this->view->ui_theme = $cnf->object()->theme;
$this->view->ui_theme = $theme_config;
}
// parse product properties, use template (.in) when not found

View file

@ -215,6 +215,11 @@
<!-- theme JS -->
<script src="{{ cache_safe(theme_file_or_default('/js/theme.js', theme_name)) }}"></script>
<!-- Auto Theme Switcher -->
{% if theme_auto is defined and theme_auto %}
<script>window.opnsenseAutoThemeEnabled = true;</script>
{% endif %}
<script src="{{ cache_safe('/ui/js/opnsense_theme_auto.js') }}"></script>
</head>
<body>
<header class="page-head">

View file

@ -0,0 +1,59 @@
/*
* Copyright (C) 2026 Konstantinos Spartalis <cspartalis@potatonetworks.com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
(function() {
if (window.opnsenseAutoThemeEnabled || document.cookie.indexOf('opnsense_theme=opnsense-auto') !== -1) {
function updateTheme() {
var isDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
var activeTheme = isDark ? 'opnsense-dark' : 'opnsense';
var links = document.getElementsByTagName('link');
for (var i = 0; i < links.length; i++) {
if (links[i].href && links[i].href.indexOf('/ui/themes/') !== -1) {
links[i].href = links[i].href.replace(/\/ui\/themes\/[^\/]+\//, '/ui/themes/' + activeTheme + '/');
}
}
var imgs = document.getElementsByTagName('img');
for (var j = 0; j < imgs.length; j++) {
if (imgs[j].src && imgs[j].src.indexOf('/ui/themes/') !== -1) {
imgs[j].src = imgs[j].src.replace(/\/ui\/themes\/[^\/]+\//, '/ui/themes/' + activeTheme + '/');
}
}
if (typeof d3 !== 'undefined' && typeof nv !== 'undefined') {
window.dispatchEvent(new Event('resize'));
}
}
updateTheme();
document.addEventListener('DOMContentLoaded', updateTheme);
if (window.matchMedia) {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateTheme);
}
}
})();

View file

@ -336,6 +336,12 @@ function display_error_form($text)
<script src="/ui/js/jquery-3.5.1.min.js"></script>
<!-- Auto Theme Switcher Support -->
<?php if (isset($config['theme']) && $config['theme'] === 'opnsense-auto'): ?>
<script>window.opnsenseAutoThemeEnabled = true;</script>
<?php endif; ?>
<script src="<?= cache_safe("/ui/js/opnsense_theme_auto.js") ?>"></script>
</head>
<body class="page-login">
<div id=container">
@ -381,6 +387,12 @@ function display_login_form($Login_Error)
<script src="<?= cache_safe(get_themed_filename('/js/theme.js')) ?>"></script>
<?php endif ?>
<!-- Auto Theme Switcher Support -->
<?php if (isset($config['theme']) && $config['theme'] === 'opnsense-auto'): ?>
<script>window.opnsenseAutoThemeEnabled = true;</script>
<?php endif; ?>
<script src="<?= cache_safe("/ui/js/opnsense_theme_auto.js") ?>"></script>
</head>
<body class="page-login">

View file

@ -32,6 +32,12 @@ $pagetitle .= html_safe(sprintf(' | %s.%s', $config['system']['hostname'], $conf
<!-- include (theme) style -->
<link rel="stylesheet" type="text/css" href="<?= cache_safe(get_themed_filename('/css/main.css')) ?>">
<!-- Auto Theme Switcher Support -->
<?php if (isset($config['theme']) && $config['theme'] === 'opnsense-auto'): ?>
<script>window.opnsenseAutoThemeEnabled = true;</script>
<?php endif; ?>
<script src="<?= cache_safe("/ui/js/opnsense_theme_auto.js") ?>"></script>
<!-- TODO: move to theme style -->
<style>
.menu-level-3-item {

View file

@ -388,10 +388,17 @@ $( document ).ready(function() {
<td>
<select name="theme" class="selectpicker">
<?php
foreach (glob('/usr/local/opnsense/www/themes/*', GLOB_ONLYDIR) as $file):
$file = basename($file);?>
<option <?= $file == $pconfig['theme'] ? 'selected="selected"' : '' ?>>
<?=$file;?>
$themes = [];
foreach (glob('/usr/local/opnsense/www/themes/*', GLOB_ONLYDIR) as $file) {
$themes[] = basename($file);
}
if (!in_array('opnsense-auto', $themes)) {
$themes[] = 'opnsense-auto';
}
sort($themes);
foreach ($themes as $theme_opt): ?>
<option <?= $theme_opt == $pconfig['theme'] ? 'selected="selected"' : '' ?>>
<?=$theme_opt;?>
</option>
<?php
endforeach; ?>