diff --git a/qui/eol.json b/qui/eol.json
new file mode 100644
index 00000000..795743f9
--- /dev/null
+++ b/qui/eol.json
@@ -0,0 +1,22 @@
+{
+"fedora-36": "2023-05-16",
+"fedora-35": "2022-12-13",
+"fedora-34": "2022-06-07",
+"fedora-33": "2021-11-30",
+"fedora-32": "2021-05-25",
+"fedora-31": "2020-11-24",
+"fedora-30": "2020-05-26",
+"fedora-29": "2019-11-26",
+"fedora-28": "2019-05-28",
+"fedora-27": "2018-11-30",
+"fedora-26": "2018-05-29",
+"fedora-25": "2017-12-12",
+"fedora-24": "2017-08-08",
+"debian-12": "2026-06-10",
+"debian-11": "2024-07-01",
+"debian-10": "2022-09-10",
+"debian-9": "2020-07-18",
+"debian-8": "2018-06-17",
+"debian-7": "2016-04-25",
+"debian-6": "2014-05-31"
+}
diff --git a/qui/tray/updates.py b/qui/tray/updates.py
index 455e83b9..f5e636f4 100644
--- a/qui/tray/updates.py
+++ b/qui/tray/updates.py
@@ -24,6 +24,36 @@
_ = t.gettext
+class TextItem(Gtk.MenuItem):
+ def __init__(self, text):
+ super().__init__()
+ title_label = Gtk.Label()
+ title_label.set_markup(text)
+ title_label.set_halign(Gtk.Align.CENTER)
+ title_label.set_justify(Gtk.Justification.CENTER)
+ self.set_margin_left(10)
+ self.set_margin_right(10)
+ self.set_margin_top(5)
+ self.add(title_label)
+ self.set_sensitive(False)
+ self.show_all()
+
+
+class RunItem(Gtk.MenuItem):
+ def __init__(self, text, command):
+ super().__init__()
+ title_label = Gtk.Label()
+ title_label.set_markup(text)
+ title_label.set_halign(Gtk.Align.CENTER)
+ title_label.set_justify(Gtk.Justification.CENTER)
+ self.set_margin_left(10)
+ self.set_margin_right(10)
+ self.set_margin_bottom(5)
+ self.add(title_label)
+ self.show_all()
+ self.connect('activate', command)
+
+
class UpdatesTray(Gtk.Application):
def __init__(self, app_name, qapp, dispatcher):
super().__init__()
@@ -43,6 +73,7 @@ def __init__(self, app_name, qapp, dispatcher):
'Qubes Update\nUpdates are available.'))
self.vms_needing_update = set()
+ self.obsolete_vms = set()
self.tray_menu = Gtk.Menu()
@@ -53,31 +84,24 @@ def run(self): # pylint: disable=arguments-differ
self.update_indicator_state()
def setup_menu(self):
- title_label = Gtk.Label(xalign=0)
- title_label.set_markup(_("Qube updates available"))
- title_menu_item = Gtk.MenuItem()
- title_menu_item.add(title_label)
- title_menu_item.set_sensitive(False)
-
- subtitle_label = Gtk.Label(xalign=0)
- subtitle_label.set_markup(
- _("Updates available for {} qubes").format(
- len(self.vms_needing_update)))
- subtitle_menu_item = Gtk.MenuItem()
- subtitle_menu_item.set_margin_left(10)
- subtitle_menu_item.add(subtitle_label)
- subtitle_menu_item.set_sensitive(False)
-
- run_label = Gtk.Label(xalign=0)
- run_label.set_text(_("Launch updater"))
- run_menu_item = Gtk.MenuItem()
- run_menu_item.set_margin_left(10)
- run_menu_item.add(run_label)
- run_menu_item.connect('activate', self.launch_updater)
-
- self.tray_menu.append(title_menu_item)
- self.tray_menu.append(subtitle_menu_item)
- self.tray_menu.append(run_menu_item)
+ self.tray_menu.set_reserve_toggle_size(False)
+
+ if self.vms_needing_update:
+ self.tray_menu.append(TextItem(_("Qube updates available!")))
+ self.tray_menu.append(RunItem(
+ _("Updates for {} qubes are available!\n"
+ "Launch updater").format(
+ len(self.vms_needing_update)), self.launch_updater))
+
+ if self.obsolete_vms:
+ self.tray_menu.append(TextItem(
+ _("Some qubes are no longer supported!")))
+ obsolete_text = _("The following qubes are based on distributions "
+ "that are no longer supported:\n")\
+ + ", ".join([str(vm) for vm in self.obsolete_vms])\
+ + _("\nInstall new templates with Template Manager")
+ self.tray_menu.append(
+ RunItem(obsolete_text, self.launch_template_manager))
self.tray_menu.show_all()
@@ -93,8 +117,14 @@ def launch_updater(*_args, **_kwargs):
# pylint: disable=consider-using-with
subprocess.Popen(['qubes-update-gui'])
+ @staticmethod
+ def launch_template_manager(*_args, **_kwargs):
+ # pylint: disable=consider-using-with
+ subprocess.Popen(['qvm-template-gui'])
+
def check_vms_needing_update(self):
self.vms_needing_update.clear()
+ self.obsolete_vms.clear()
for vm in self.qapp.domains:
try:
updates_available = vm.features.get('updates-available', False)
@@ -102,7 +132,13 @@ def check_vms_needing_update(self):
updates_available = False
if updates_available and \
(getattr(vm, 'updateable', False) or vm.klass == 'AdminVM'):
- self.vms_needing_update.add(vm.name)
+ self.vms_needing_update.add(vm)
+ try:
+ supported = qui.utils.check_support(vm)
+ except exc.QubesDaemonCommunicationError:
+ supported = True
+ if not supported:
+ self.obsolete_vms.add(vm.name)
def connect_events(self):
self.dispatcher.add_handler('domain-feature-set:updates-available',
@@ -155,7 +191,7 @@ def feature_set(self, vm, event, feature, value, **_kwargs):
self.update_indicator_state()
def update_indicator_state(self):
- if self.vms_needing_update:
+ if self.vms_needing_update or self.obsolete_vms:
self.widget_icon.set_visible(True)
else:
self.widget_icon.set_visible(False)
diff --git a/qui/utils.py b/qui/utils.py
index 4176cff8..8a865fe3 100644
--- a/qui/utils.py
+++ b/qui/utils.py
@@ -20,11 +20,16 @@
"""helper functions for various qui tools"""
# pylint: disable=wrong-import-position,import-error
import asyncio
+import json
import sys
import traceback
from html import escape
import gettext
+
+import pkg_resources
+from datetime import datetime
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
@@ -32,6 +37,7 @@
gi.require_version('Gtk', '3.0') # isort:skip
from gi.repository import Gtk # isort:skip
+EOL_DATES = json.load(pkg_resources.resource_stream(__name__, 'eol.json'))
def run_asyncio_and_show_errors(loop, tasks, name, restart=True):
"""
@@ -70,3 +76,16 @@ def run_asyncio_and_show_errors(loop, tasks, name, restart=True):
dialog.run()
exit_code = 1
return exit_code
+
+
+def check_support(vm):
+ """Return true if the given template/standalone vm is still supported, by
+ default returns true"""
+ template_name = vm.features.get('template-name', '')
+ if not template_name:
+ return True
+ eol = EOL_DATES.get(template_name, None)
+ if not eol:
+ return True
+ eol = datetime.strptime(eol, '%Y-%m-%d')
+ return eol > datetime.now()
diff --git a/rpm_spec/qubes-desktop-linux-manager.spec.in b/rpm_spec/qubes-desktop-linux-manager.spec.in
index cd3f2061..f58e77ad 100644
--- a/rpm_spec/qubes-desktop-linux-manager.spec.in
+++ b/rpm_spec/qubes-desktop-linux-manager.spec.in
@@ -110,6 +110,7 @@ gtk-update-icon-cache %{_datadir}/icons/Adwaita &>/dev/null || :
%{python3_sitelib}/qui/qubes-updater-base.css
%{python3_sitelib}/qui/qubes-updater-light.css
%{python3_sitelib}/qui/qubes-updater-dark.css
+%{python3_sitelib}/qui/eol.json
%{python3_sitelib}/qui/styles/qubes-colors-light.css
%{python3_sitelib}/qui/styles/qubes-colors-dark.css
diff --git a/setup.py b/setup.py
index 6ae60963..410490ed 100644
--- a/setup.py
+++ b/setup.py
@@ -67,6 +67,7 @@ def run(self):
"styles/qubes-colors-light.css",
"styles/qubes-colors-dark.css",
"styles/qubes-widgets-base.css",
+ "eol.json"
],
'qubes_config': ["new_qube.glade",
"global_config.glade",