Skip to content

Commit

Permalink
Add filtering options to Qubes Update GUI
Browse files Browse the repository at this point in the history
  • Loading branch information
alimirjamali committed Oct 13, 2024
1 parent a3f61ca commit 05541e8
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 11 deletions.
5 changes: 4 additions & 1 deletion qui/updater.glade
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
<column type="PyObject"/>
</columns>
</object>
<object class="GtkTreeModelFilter" id="list_store_filter">
<property name="child-model">list_store</property>
</object>
<object class="GtkListStore" id="progress_store">
<columns>
<!-- column-name gint -->
Expand Down Expand Up @@ -141,7 +144,7 @@
<child>
<object class="GtkTreeView" id="vm_list">
<property name="can-focus">False</property>
<property name="model">list_store</property>
<property name="model">list_store_filter</property>
<property name="rules-hint">True</property>
<property name="activate-on-single-click">True</property>
<child internal-child="selection">
Expand Down
47 changes: 39 additions & 8 deletions qui/updater/intro_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,17 @@ def __init__(self, builder, log, next_button):
self.next_button = next_button
self.disable_checkboxes = False
self.active = True
self.hide_skipped: Optional[bool] = True
self.hide_updated: Optional[bool] = False

self.page: Gtk.Box = self.builder.get_object("list_page")
self.stack: Gtk.Stack = self.builder.get_object("main_stack")
self.vm_list: Gtk.TreeView = self.builder.get_object("vm_list")
self.list_store_raw: Gtk.ListStore = self.builder.get_object(
"list_store")
self.list_store_filter: Gtk.TreeModelFilter = self.builder.get_object(
"list_store_filter")
self.list_store_filter.set_visible_func(self.filter_func)
self.list_store: Optional[ListWrapper] = None

checkbox_column: Gtk.TreeViewColumn = self.builder.get_object(
Expand All @@ -82,14 +89,16 @@ def __init__(self, builder, log, next_button):
MAYBE=f'<span foreground="{label_color_theme("orange")}">'
'<b>MAYBE</b></span>',
OBSOLETE=f'<span foreground="{label_color_theme("red")}">'
'<b>OBSOLETE</b></span>'
'<b>OBSOLETE</b></span>',
SKIP=f'<span foreground="{label_color_theme("red")}">'
'<b>SKIP</b></span>'
))

def populate_vm_list(self, qapp, settings):
"""Adds to list any updatable vms with update info."""
self.log.debug("Populate update list")
self.list_store = ListWrapper(
UpdateRowWrapper, self.vm_list.get_model())
UpdateRowWrapper, self.list_store_raw)

for vm in qapp.domains:
if vm.klass == 'AdminVM':
Expand All @@ -103,9 +112,10 @@ def populate_vm_list(self, qapp, settings):
if getattr(vm, 'updateable', False) and vm.klass != 'AdminVM':
self.list_store.append_vm(vm)

self.refresh_update_list(settings.update_if_stale)
self.refresh_update_list(settings.update_if_stale, \
settings.hide_skipped, settings.hide_updated)

def refresh_update_list(self, update_if_stale):
def refresh_update_list(self, update_if_stale, hide_skipped, hide_updated):
"""
Refreshes "Updates Available" column if settings changed.
"""
Expand All @@ -124,6 +134,10 @@ def refresh_update_list(self, update_if_stale):
row.updates_available = bool(row.vm.name in to_update)
row.selected = bool(row.vm.name in to_update)

self.hide_skipped = hide_skipped
self.hide_updated = hide_updated
self.list_store_filter.refilter()

def get_vms_to_update(self) -> ListWrapper:
"""Returns list of vms selected to be updated"""
return self.list_store.get_selected()
Expand Down Expand Up @@ -250,6 +264,12 @@ def _handle_cli_dom0(dom0, to_update, cliargs):
to_update = to_update.difference({"dom0"})
return to_update

def filter_func(self, model, iter, data):
if self.hide_skipped and 'SKIP' in str(model[iter][4]):
return False
if self.hide_updated and 'NO' in str(model[iter][4]):
return False
return True

def is_stale(vm, expiration_period):
if expiration_period is None:
Expand Down Expand Up @@ -278,6 +298,7 @@ class UpdateRowWrapper(RowWrapper):

def __init__(self, list_store, vm, to_update: bool):
updates_available = bool(vm.features.get('updates-available', False))
skip = bool(vm.features.get('skip-update', False))
if to_update and not updates_available:
updates_available = None
selected = updates_available is True
Expand All @@ -293,7 +314,7 @@ def __init__(self, list_store, vm, to_update: bool):
selected,
icon,
name,
UpdatesAvailable.from_features(updates_available, supported),
UpdatesAvailable.from_features(updates_available, supported, skip),
Date(last_updates_check),
Date(last_update),
0,
Expand Down Expand Up @@ -335,12 +356,14 @@ def updates_available(self):
def updates_available(self, value):
updates_available = bool(
self.vm.features.get('updates-available', False))
skip = bool(
self.vm.features.get('skip-update', False))
supported = check_support(self.vm)

if value and not updates_available:
updates_available = None
self.raw_row[self._UPDATES_AVAILABLE] = \
UpdatesAvailable.from_features(updates_available, supported)
UpdatesAvailable.from_features(updates_available, supported, skip)

@property
def last_updates_check(self):
Expand Down Expand Up @@ -438,10 +461,14 @@ class UpdatesAvailable(Enum):
MAYBE = 1
NO = 2
EOL = 3
SKIP = 4

@staticmethod
def from_features(updates_available: Optional[bool],
supported: Optional[str]=None) -> "UpdatesAvailable":
supported: Optional[str]=None,
skip: Optional[bool]=False) -> "UpdatesAvailable":
if skip:
return UpdatesAvailable.SKIP
if not supported:
return UpdatesAvailable.EOL
if updates_available:
Expand All @@ -460,9 +487,13 @@ def color(self):
return label_color_theme("black")
if self is UpdatesAvailable.EOL:
return label_color_theme('red')
if self is UpdatesAvailable.SKIP:
return label_color_theme('red')

def __str__(self):
if self is UpdatesAvailable.YES:
if self is UpdatesAvailable.SKIP:
name = "SKIP"
elif self is UpdatesAvailable.YES:
name = "YES"
elif self is UpdatesAvailable.MAYBE:
name = "MAYBE"
Expand Down
44 changes: 43 additions & 1 deletion qui/updater/updater_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class Settings:
MAX_UPDATE_IF_STALE = 99
DEFAULT_RESTART_SERVICEVMS = True
DEFAULT_RESTART_OTHER_VMS = False
DEFAULT_HIDE_SKIPPED = True
DEFAULT_HIDE_UPDATED = False

def __init__(
self,
Expand Down Expand Up @@ -108,6 +110,12 @@ def __init__(
self.restart_other_checkbox.connect(
"toggled", self._show_restart_exceptions)

self.hide_skipped_checkbox: Gtk.CheckButton = \
self.builder.get_object("hide_skipped")

self.hide_updated_checkbox: Gtk.CheckButton = \
self.builder.get_object("hide_updated")

self.available_vms = [
vm for vm in self.qapp.domains
if vm.klass == 'DispVM' and not vm.auto_cleanup
Expand Down Expand Up @@ -138,6 +146,8 @@ def __init__(
self._init_restart_other_vms: Optional[bool] = None
self._init_limit_concurrency: Optional[bool] = None
self._init_max_concurrency: Optional[int] = None
self._init_hide_skipped: Optional[bool] = None
self._init_hide_updated: Optional[bool] = None

@property
def update_if_stale(self) -> int:
Expand Down Expand Up @@ -176,6 +186,18 @@ def restart_other_vms(self) -> bool:
self.vm, "qubes-vm-update-restart-other",
Settings.DEFAULT_RESTART_OTHER_VMS)

@property
def hide_skipped(self) -> bool:
return get_boolean_feature(
self.vm, "qubes-vm-update-hide-skipped",
Settings.DEFAULT_HIDE_SKIPPED)

@property
def hide_updated(self) -> bool:
return get_boolean_feature(
self.vm, "qubes-vm-update-hide-updated",
Settings.DEFAULT_HIDE_UPDATED)

@property
def max_concurrency(self) -> Optional[int]:
"""Return the current (set by this window or manually) option value."""
Expand Down Expand Up @@ -210,6 +232,11 @@ def load_settings(self):
if self._init_limit_concurrency:
self.max_concurrency_button.set_value(self._init_max_concurrency)

self._init_hide_skipped = self.hide_skipped
self._init_hide_updated = self.hide_updated
self.hide_skipped_checkbox.set_active(self._init_hide_skipped)
self.hide_updated_checkbox.set_active(self._init_hide_updated)

def _show_restart_exceptions(self, _emitter=None):
if self.restart_other_checkbox.get_active():
self.restart_exceptions_page.show_all()
Expand Down Expand Up @@ -262,6 +289,20 @@ def save_and_close(self, _emitter):
default=Settings.DEFAULT_RESTART_OTHER_VMS
)

self._save_option(
name="hide-skipped",
value=self.hide_skipped_checkbox.get_active(),
init=self._init_hide_skipped,
default=Settings.DEFAULT_HIDE_SKIPPED
)

self._save_option(
name="hide-updated",
value=self.hide_updated_checkbox.get_active(),
init=self._init_hide_updated,
default=Settings.DEFAULT_HIDE_UPDATED
)

limit_concurrency = self.limit_concurrency_checkbox.get_active()
if self._init_limit_concurrency or limit_concurrency:
if limit_concurrency:
Expand All @@ -279,7 +320,8 @@ def save_and_close(self, _emitter):
apply_feature_change(vm, 'restart-after-update', None)
self.exceptions.save()

self.refresh_callback(self.update_if_stale)
self.refresh_callback(self.update_if_stale, self.hide_skipped, \
self.hide_updated)
self.settings_window.close()

def _save_option(
Expand Down
66 changes: 65 additions & 1 deletion qui/updater_settings.glade
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<property name="title" translatable="yes">Qubes OS Updater Settings</property>
<property name="resizable">False</property>
<property name="default-width">458</property>
<property name="default-height">571</property>
<property name="default-height">640</property>
<child>
<!-- n-columns=3 n-rows=2 -->
<object class="GtkGrid">
Expand Down Expand Up @@ -495,6 +495,70 @@
<property name="position">10</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Filtering Options</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="margin-top">18</property>
<property name="hexpand">True</property>
<property name="use-markup">True</property>
<style>
<class name="section_title"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">11</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="hide_skipped">
<property name="label" translatable="yes">Hide qubes with 'skip-update' feature from selection page.</property>
<property name="visible">False</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="margin-top">5</property>
<property name="use-underline">True</property>
<property name="active">True</property>
<property name="draw-indicator">True</property>
<style>
<class name="explanation_text"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">12</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="hide_updated">
<property name="label" translatable="yes">Hide already updated qubes from selection page.</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="margin-top">5</property>
<property name="use-underline">True</property>
<property name="active">False</property>
<property name="draw-indicator">True</property>
<style>
<class name="explanation_text"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">13</property>
</packing>
</child>
</object>
</child>
</object>
Expand Down

0 comments on commit 05541e8

Please sign in to comment.