diff --git a/plinth/setup.py b/plinth/setup.py index dddbe40bf..6d4ed71ab 100644 --- a/plinth/setup.py +++ b/plinth/setup.py @@ -29,12 +29,12 @@ _is_shutting_down = False _force_upgrader = None -def run_setup_on_app(app_id, allow_install=True): +def run_setup_on_app(app_id, allow_install=True, rerun=False): """Execute the setup process in a thread.""" # App is already up-to-date app = app_module.App.get(app_id) current_version = app.get_setup_version() - if current_version >= app.info.version: + if not rerun and current_version >= app.info.version: return if not current_version: diff --git a/plinth/templates/toolbar.html b/plinth/templates/toolbar.html index ffd03b7a1..26d19b314 100644 --- a/plinth/templates/toolbar.html +++ b/plinth/templates/toolbar.html @@ -45,6 +45,14 @@ {% trans "Restore" %} {% endif %} + {% if show_rerun_setup %} +
+ {% csrf_token %} + +
+ {% endif %} {% if show_uninstall %} [1-9a-z\-_]+)/$', views.UninstallView.as_view(), name='uninstall'), + re_path(r'^rerun-setup/(?P[1-9a-z\-_]+)/$', views.rerun_setup_view, + name='rerun-setup'), # captcha urls are public re_path(r'^captcha/image/(?P\w+)/$', public(cviews.captcha_image), diff --git a/plinth/views.py b/plinth/views.py index 6d6e295de..8cb5d4c20 100644 --- a/plinth/views.py +++ b/plinth/views.py @@ -10,9 +10,11 @@ import urllib.parse from django.contrib import messages from django.core.exceptions import ImproperlyConfigured from django.http import Http404, HttpResponseBadRequest, HttpResponseRedirect +from django.shortcuts import redirect from django.template.response import TemplateResponse from django.urls import reverse from django.utils.translation import gettext as _ +from django.views.decorators.http import require_POST from django.views.generic import TemplateView from django.views.generic.edit import FormView from stronghold.decorators import public @@ -275,6 +277,7 @@ class AppView(FormView): context['has_diagnostics'] = self.app.has_diagnostics() context['port_forwarding_info'] = get_port_forwarding_info(self.app) context['app_enable_disable_form'] = self.get_enable_disable_form() + context['show_rerun_setup'] = True context['show_uninstall'] = not self.app.info.is_essential context['refresh_page_sec'] = None @@ -335,6 +338,7 @@ class SetupView(TemplateView): setup_state = app.get_setup_state() context['setup_state'] = setup_state context['operations'] = operation.manager.filter(app.app_id) + context['show_rerun_setup'] = False context['show_uninstall'] = ( not app.info.is_essential and setup_state != app_module.App.SetupState.NEEDS_SETUP) @@ -399,6 +403,24 @@ class SetupView(TemplateView): if component.has_unavailable_packages()) +@require_POST +def rerun_setup_view(request, app_id): + """Re-run setup on an app. + + This should be safe to perform on an already setup/running app. This may be + useful in situations where the app is broken for unknown reason as notified + by the diagnostics tests. + """ + # Start the application setup, and refresh the page every few seconds to + # keep displaying the status. + setup.run_setup_on_app(app_id, rerun=True) + + # Give a moment for the setup process to start and show meaningful status. + time.sleep(1) + + return redirect(reverse(f'{app_id}:index')) + + class UninstallView(FormView): """View to uninstall apps."""