mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
- Reduce the number of specialized messages to ease localization and clear way for generalized configuration change handler. Tests: - Update the one or two configuration setting at the same time and notice that a single message is shown. - When no setting is changed and form is submitted, 'settings unchanged' message is shown. - Raise error when enabling/disable auto updates and notice a proper HTML error shown. When other setting is also updated, then one error and one success message is shown. Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
221 lines
7.6 KiB
Python
221 lines
7.6 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
"""FreedomBox app for upgrades."""
|
|
|
|
import subprocess
|
|
|
|
from apt.cache import Cache
|
|
from django.contrib import messages
|
|
from django.http import HttpResponseRedirect
|
|
from django.shortcuts import redirect
|
|
from django.urls import reverse_lazy
|
|
from django.utils.translation import gettext as _
|
|
from django.views.generic import TemplateView
|
|
from django.views.generic.edit import FormView
|
|
|
|
from plinth import __version__
|
|
from plinth.modules import first_boot, upgrades
|
|
from plinth.privileged import packages as packages_privileged
|
|
from plinth.views import AppView, messages_error
|
|
|
|
from . import privileged
|
|
from .forms import BackportsFirstbootForm, ConfigureForm, UpdateFirstbootForm
|
|
|
|
|
|
class UpgradesConfigurationView(AppView):
|
|
"""Serve configuration page."""
|
|
|
|
form_class = ConfigureForm
|
|
success_url = reverse_lazy('upgrades:index')
|
|
template_name = "upgrades_configure.html"
|
|
app_id = 'upgrades'
|
|
|
|
def get_initial(self):
|
|
"""Return the initial values for the form."""
|
|
return {
|
|
'auto_upgrades_enabled': privileged.check_auto(),
|
|
'dist_upgrade_enabled': upgrades.is_dist_upgrade_enabled()
|
|
}
|
|
|
|
def get_context_data(self, *args, **kwargs):
|
|
"""Add additional context data for template."""
|
|
context = super().get_context_data(*args, **kwargs)
|
|
context['can_activate_backports'] = upgrades.can_activate_backports()
|
|
context['is_backports_requested'] = upgrades.is_backports_requested()
|
|
context['is_busy'] = (_is_updating()
|
|
or packages_privileged.is_package_manager_busy())
|
|
context['log'] = privileged.get_log()
|
|
context['refresh_page_sec'] = 3 if context['is_busy'] else None
|
|
context['version'] = __version__
|
|
context['new_version'] = is_newer_version_available()
|
|
context['os_release'] = get_os_release()
|
|
context['can_test_dist_upgrade'] = upgrades.can_test_dist_upgrade()
|
|
return context
|
|
|
|
def form_valid(self, form):
|
|
"""Apply the form changes."""
|
|
old_status = form.initial
|
|
new_status = form.cleaned_data
|
|
|
|
is_changed = False
|
|
|
|
if old_status['auto_upgrades_enabled'] \
|
|
!= new_status['auto_upgrades_enabled']:
|
|
|
|
try:
|
|
if new_status['auto_upgrades_enabled']:
|
|
privileged.enable_auto()
|
|
else:
|
|
privileged.disable_auto()
|
|
|
|
is_changed = True
|
|
except Exception as exception:
|
|
messages_error(self.request,
|
|
_('Error when configuring unattended-upgrades'),
|
|
exception)
|
|
|
|
if old_status['dist_upgrade_enabled'] \
|
|
!= new_status['dist_upgrade_enabled']:
|
|
upgrades.set_dist_upgrade_enabled(
|
|
new_status['dist_upgrade_enabled'])
|
|
is_changed = True
|
|
|
|
if is_changed:
|
|
messages.success(self.request, _('Configuration updated.'))
|
|
|
|
return super().form_valid(form)
|
|
|
|
|
|
def is_newer_version_available():
|
|
"""Return whether a newer Freedombox version is available."""
|
|
cache = Cache()
|
|
freedombox = cache['freedombox']
|
|
return not freedombox.candidate.is_installed
|
|
|
|
|
|
def get_os_release():
|
|
"""Return the Debian release number and name."""
|
|
output = 'Error: Cannot read PRETTY_NAME in /etc/os-release.'
|
|
with open('/etc/os-release', 'r', encoding='utf-8') as release_file:
|
|
for line in release_file:
|
|
if 'PRETTY_NAME=' in line:
|
|
line = line.replace('"', '').strip()
|
|
line = line.split('=')
|
|
output = line[1]
|
|
return output
|
|
|
|
|
|
def _is_updating():
|
|
"""Check if manually triggered update is running."""
|
|
command = ['systemctl', 'is-active', 'freedombox-manual-upgrade']
|
|
result = subprocess.run(command, capture_output=True, text=True,
|
|
check=False)
|
|
return str(result.stdout).startswith('activ') # 'active' or 'activating'
|
|
|
|
|
|
def upgrade(request):
|
|
"""Serve the upgrade page."""
|
|
if request.method == 'POST':
|
|
try:
|
|
privileged.run()
|
|
messages.success(request, _('Upgrade process started.'))
|
|
except Exception:
|
|
messages.error(request, _('Starting upgrade failed.'))
|
|
|
|
return redirect(reverse_lazy('upgrades:index'))
|
|
|
|
|
|
def activate_backports(request):
|
|
"""Activate backports."""
|
|
if request.method == 'POST':
|
|
upgrades.set_backports_requested(True)
|
|
upgrades.setup_repositories(None)
|
|
messages.success(request, _('Frequent feature updates activated.'))
|
|
|
|
return redirect(reverse_lazy('upgrades:index'))
|
|
|
|
|
|
class BackportsFirstbootView(FormView):
|
|
"""View to configure backports during first boot wizard."""
|
|
|
|
template_name = 'backports-firstboot.html'
|
|
form_class = BackportsFirstbootForm
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
"""Show backports configuration form only if it can be activated."""
|
|
if upgrades.is_backports_enabled():
|
|
# Backports is already enabled. Record this preference and
|
|
# skip first boot step.
|
|
upgrades.set_backports_requested(True)
|
|
first_boot.mark_step_done('backports_wizard')
|
|
return HttpResponseRedirect(reverse_lazy(first_boot.next_step()))
|
|
|
|
if not upgrades.can_activate_backports():
|
|
# Skip first boot step.
|
|
upgrades.set_backports_requested(False)
|
|
first_boot.mark_step_done('backports_wizard')
|
|
return HttpResponseRedirect(reverse_lazy(first_boot.next_step()))
|
|
|
|
return super().dispatch(request, *args, *kwargs)
|
|
|
|
def get_success_url(self):
|
|
"""Return next firstboot step."""
|
|
return reverse_lazy(first_boot.next_step())
|
|
|
|
def form_valid(self, form):
|
|
"""Mark the first wizard step as done, save value and redirect."""
|
|
enabled = form.cleaned_data['backports_enabled']
|
|
upgrades.set_backports_requested(enabled)
|
|
upgrades.setup_repositories(None)
|
|
first_boot.mark_step_done('backports_wizard')
|
|
return super().form_valid(form)
|
|
|
|
|
|
class UpdateFirstbootView(FormView):
|
|
"""View to run initial update during first boot wizard."""
|
|
|
|
template_name = 'update-firstboot.html'
|
|
form_class = UpdateFirstbootForm
|
|
|
|
def __init__(self):
|
|
"""Define instance attribute."""
|
|
self.update = True
|
|
|
|
def get_success_url(self):
|
|
"""Return next firstboot step."""
|
|
if self.update:
|
|
return reverse_lazy('upgrades:update-firstboot-progress')
|
|
|
|
return reverse_lazy(first_boot.next_step())
|
|
|
|
def form_valid(self, form):
|
|
"""Run update if selected, and mark step as done."""
|
|
self.update = form.cleaned_data['update_now']
|
|
if self.update:
|
|
privileged.run()
|
|
|
|
first_boot.mark_step_done('initial_update')
|
|
return super().form_valid(form)
|
|
|
|
|
|
class UpdateFirstbootProgressView(TemplateView):
|
|
"""View to show initial update progress."""
|
|
|
|
template_name = 'update-firstboot-progress.html'
|
|
|
|
def get_context_data(self, *args, **kwargs):
|
|
context = super().get_context_data(*args, **kwargs)
|
|
context['is_busy'] = (_is_updating()
|
|
or packages_privileged.is_package_manager_busy())
|
|
context['next_step'] = first_boot.next_step()
|
|
context['refresh_page_sec'] = 3 if context['is_busy'] else None
|
|
return context
|
|
|
|
|
|
def test_dist_upgrade(request):
|
|
"""Test dist-upgrade from stable to testing."""
|
|
if request.method == 'POST':
|
|
upgrades.test_dist_upgrade()
|
|
messages.success(request, _('Starting distribution upgrade test.'))
|
|
|
|
return redirect(reverse_lazy('upgrades:index'))
|