From 1f98dfcad195d9fff0a44d7b6d1995375bae353b Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Wed, 30 Jul 2025 19:14:21 -0700 Subject: [PATCH] setup: Perform a check for app availability after the page loads - Using AJAX request instead of loading the initial page slowly. Tests: - Unit tests passes. - Deluge app is not available in bookworm and is available in Trixie. - When app is available, no message is shown. Install button is enabled. - When app is not available a proper warning alert message is shown. Install button is disabled. - During check for the availability, the progress message is shown. Install button is disabled. - When Javascript is disabled on the page, no availability check is performed. Install button is enabled. - When an exception is raised in the is-available view, error message is shown. Install button is enabled. - When is-available view return HTML response, error message is shown. Install button is enabled. - When is-available view invalid JSON response, error message is shown. Install button is enabled. Signed-off-by: Sunil Mohan Adapa Reviewed-by: Joseph Nuthalapati --- plinth/app.py | 12 ++++++ plinth/modules/deluge/__init__.py | 6 +-- plinth/package.py | 17 ++++---- plinth/templates/setup.html | 46 +++++++++++++-------- plinth/tests/test_app.py | 20 +++++++++ plinth/tests/test_package.py | 10 ++--- plinth/urls.py | 2 + plinth/views.py | 24 +++++------ static/themes/default/js/main.js | 69 +++++++++++++++++++++++++++++++ 9 files changed, 158 insertions(+), 48 deletions(-) diff --git a/plinth/app.py b/plinth/app.py index f9d562e91..0d4a62f4b 100644 --- a/plinth/app.py +++ b/plinth/app.py @@ -140,6 +140,14 @@ class App: """ return self.get_component(self.app_id + '-info') + def is_available(self) -> bool: + """Return whether the app is available to install.""" + for component in self.components.values(): + if not component.is_available(): + return False + + return True + def setup(self, old_version): """Install and configure the app and its components.""" for component in self.components.values(): @@ -330,6 +338,10 @@ class Component: """ return App.get(self.app_id) + def is_available(self) -> bool: + """Return whether the app is available to install.""" + return True + def setup(self, old_version): """Run operations to install and configure the component.""" diff --git a/plinth/modules/deluge/__init__.py b/plinth/modules/deluge/__init__.py index 58d25ea69..e9215fc8c 100644 --- a/plinth/modules/deluge/__init__.py +++ b/plinth/modules/deluge/__init__.py @@ -34,11 +34,11 @@ class DelugePackages(Packages): https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1031593 """ - def has_unavailable_packages(self) -> bool | None: + def is_available(self) -> bool: if get_current_release()[1] == 'bookworm': - return True + return False - return super().has_unavailable_packages() + return super().is_available() class DelugeApp(app_module.App): diff --git a/plinth/package.py b/plinth/package.py index 8abe8ba7d..e4cfcf551 100644 --- a/plinth/package.py +++ b/plinth/package.py @@ -249,27 +249,26 @@ class Packages(app_module.FollowerComponent): return packages_installed(self.conflicts) - def has_unavailable_packages(self) -> bool | None: - """Return whether any of the packages are not available. + def is_available(self) -> bool: + """Return whether all of the packages are available. - Returns True if one or more of the packages is not available in the - user's Debian distribution or False otherwise. Returns None if it - cannot be reliably determined whether the packages are available or - not. + Returns True if all of the packages are available in the user's Debian + distribution or False otherwise. Returns True if it cannot be reliably + determined whether the packages are available or not. """ apt_lists_dir = pathlib.Path('/var/lib/apt/lists/') num_files = len( [child for child in apt_lists_dir.iterdir() if child.is_file()]) if num_files < 2: # not counting the lock file - return None + return True # Don't know, package cache is not available # List of all packages from all Package components try: self.get_actual_packages() except MissingPackageError: - return True + return False - return False + return True def _filter_packages_to_keep(self, packages: list[str]) -> list[str]: """Filter out the list of packages to keep from given list. diff --git a/plinth/templates/setup.html b/plinth/templates/setup.html index b9feb345d..cbf6e316f 100644 --- a/plinth/templates/setup.html +++ b/plinth/templates/setup.html @@ -33,24 +33,36 @@ {% endif %}

+
+ +
+

+ {% blocktrans trimmed %} + Checking app availability... + {% endblocktrans %} + +

+

+ {% blocktrans trimmed %} + Error checking app availability. Please refresh page. + {% endblocktrans %} +

+
{% csrf_token %} {% if has_unavailable_packages %} - {% elif package_conflicts and package_conflicts_action.value != 'ignore' %}