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 %}
+
+
+
+
+ {% trans "Caution:" %}
+
+
+ {% blocktrans trimmed %}
+ This application is currently not available in your distribution.
+ {% endblocktrans %}
+
+
+
+
+ {% blocktrans trimmed %}
+ Checking app availability...
+ {% endblocktrans %}
+
+
+
+ {% blocktrans trimmed %}
+ Error checking app availability. Please refresh page.
+ {% endblocktrans %}
+
+