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",