From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [45.144.208.40]) by lore.proxmox.com (Postfix) with ESMTPS id AA13A1FF0AA for ; Tue, 20 Oct 2026 13:23:41 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 3707621654; Tue, 20 Oct 2026 13:23:30 +0200 (CEST) From: Thomas Ellmenreich To: pve-devel@lists.proxmox.com Subject: [PATCH manager v6 3/4] fix #5475: ui: enable configurable window title Date: Tue, 20 Oct 2026 13:23:19 +0200 Message-ID: <20261020112321.249890-4-t.ellmenreich@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20261020112321.249890-1-t.ellmenreich@proxmox.com> References: <20261020112321.249890-1-t.ellmenreich@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1792495403602 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.411 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: 7733V22Z5L2YJX6PMH7I36RBHII2DLOB X-Message-ID-Hash: 7733V22Z5L2YJX6PMH7I36RBHII2DLOB X-MailFrom: t.ellmenreich@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Thomas Ellmenreich X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: By setting the title property in the cluster ui-settings, the window title displayed for PVE instances can be changed. The setting is evaluated on every fetch of the index template, although not every evaluation causes a refresh of the title as the fqdn is only renewed every 24h. Fixes: https://bugzilla.proxmox.com/show_bug.cgi?id=5475 Signed-off-by: Thomas Ellmenreich --- PVE/Service/pveproxy.pm | 45 +++++++++++++++++++++++++++++++++++ www/index.html.tpl | 2 +- www/manager6/UIOptions.js | 6 +++++ www/manager6/dc/OptionView.js | 21 ++++++++++++++-- 4 files changed, 71 insertions(+), 3 deletions(-) diff --git a/PVE/Service/pveproxy.pm b/PVE/Service/pveproxy.pm index dfdd014c..397e1490 100755 --- a/PVE/Service/pveproxy.pm +++ b/PVE/Service/pveproxy.pm @@ -202,6 +202,46 @@ my sub get_path_mtime { return $mtime; } +my $FQDN_CACHE = { + next_refresh => undef, + fqdn => undef, +}; + +my sub get_window_title_prefix { + my ($title_enum, $nodename) = @_; + + if (!defined($title_enum)) { + return; + } + + my $prefix = eval { + if ($title_enum eq "node-and-cluster") { + my $clinfo = PVE::Cluster::get_clinfo(); + my $clustername = $clinfo->{cluster}->{name}; + + return "$nodename - $clustername" if $clustername; + } elsif ($title_enum eq "fqdn") { + my $now = time(); + if (!$FQDN_CACHE->{next_refresh} || $now >= $FQDN_CACHE->{next_refresh}) { + $FQDN_CACHE->{fqdn} = eval { PVE::Tools::get_fqdn($nodename); }; + if (my $err = $@) { + $FQDN_CACHE->{next_refresh} = $now + 60; + die $err; + } + $FQDN_CACHE->{next_refresh} = $now + 24 * 60 * 60; + } + + return $FQDN_CACHE->{fqdn}; + } + }; + + if (my $err = $@) { + chomp $err; + die "failed to create '$title_enum' window title: $err"; + } + return $prefix; +} + # NOTE: Requests to those pages are not authenticated so we must be very careful here sub get_index { my ($nodename, $server, $r, $args) = @_; @@ -232,6 +272,7 @@ sub get_index { } } + my $window_title_prefix; my $consent_text; eval { my $dc_conf = PVE::Cluster::cfs_read_file('datacenter.cfg'); @@ -240,6 +281,8 @@ sub get_index { if (!$lang) { $lang = $dc_conf->{language} // 'en'; } + $window_title_prefix = + get_window_title_prefix($dc_conf->{'ui-settings'}->{'title'}, $nodename); }; warn "$@\n" if $@; @@ -277,6 +320,8 @@ sub get_index { console => $args->{console}, nodename => $nodename, arch => PVE::Tools::get_host_dpkg_arch(), + window_title => ($window_title_prefix // "$nodename") + . " - Proxmox Virtual Environment", debug => $debug, version => "$version", wtversion => $wtversion, diff --git a/www/index.html.tpl b/www/index.html.tpl index c18e6411..a45bad39 100644 --- a/www/index.html.tpl +++ b/www/index.html.tpl @@ -4,7 +4,7 @@ - [% nodename %] - Proxmox Virtual Environment + [% window_title %] diff --git a/www/manager6/UIOptions.js b/www/manager6/UIOptions.js index 8c4674af..e94a75df 100644 --- a/www/manager6/UIOptions.js +++ b/www/manager6/UIOptions.js @@ -90,6 +90,12 @@ Ext.define('PVE.UIOptions', { alphabetical: gettext('Alphabetical'), }, + titleOptions: { + __default__: gettext('Node Name (Default)'), + 'node-and-cluster': gettext('Node and Cluster Name'), + fqdn: gettext('Fully Qualified Domain Name (FQDN)'), + }, + shouldSortTags: function () { return !(PVE.UIOptions.options['tag-style']?.ordering === 'config'); }, diff --git a/www/manager6/dc/OptionView.js b/www/manager6/dc/OptionView.js index 4fc18ae7..ebf61270 100644 --- a/www/manager6/dc/OptionView.js +++ b/www/manager6/dc/OptionView.js @@ -97,7 +97,14 @@ Ext.define('PVE.dc.OptionView', { if (value === undefined) { return gettext('No Overrides'); } - return ''; + let txt = ''; + if (value.title) { + txt += Ext.String.format( + gettext('Title: {0}'), + PVE.UIOptions.titleOptions[value.title], + ); + } + return txt; }, header: gettext('UI Settings'), editor: { @@ -116,6 +123,7 @@ Ext.define('PVE.dc.OptionView', { return undefined; } values = values?.['ui-settings'] ?? {}; + values.title = values.title || '__default__'; return Proxmox.panel.InputPanel.prototype.setValues.call(this, values); }, onGetValues: function (values) { @@ -133,7 +141,16 @@ Ext.define('PVE.dc.OptionView', { 'ui-settings': value, }; }, - items: [], + items: [ + { + xtype: 'proxmoxKVComboBox', + name: 'title', + fieldLabel: gettext('Title'), + comboItems: Object.entries(PVE.UIOptions.titleOptions), + deleteEmpty: true, + defaultValue: '__default__', + }, + ], }, ], }, -- 2.47.3