views: Implement a view to uninstall an app

- Take backup of the app before uninstall.

- Allow selecting the backup repository for backup.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Sunil Mohan Adapa 2022-08-18 22:16:42 -07:00 committed by James Valleroy
parent 811e95f561
commit e1b4c2e52b
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
3 changed files with 93 additions and 0 deletions

View File

@ -0,0 +1,39 @@
{% extends "base.html" %}
{% comment %}
# SPDX-License-Identifier: AGPL-3.0-or-later
{% endcomment %}
{% load bootstrap %}
{% load i18n %}
{% block content %}
<h2>
{% blocktrans trimmed with app_name=app_info.name %}
Uninstall App <em>{{ app_name }}</em>?
{% endblocktrans %}
</h2>
<div class="alert alert-warning" role="alert">
{% blocktrans trimmed %}
Uninstalling an app is an exprimental feature.
{% endblocktrans %}
</div>
<p>
{% blocktrans trimmed %}
All app data and configuration will be permanently lost. App may be
installed freshly again.
{% endblocktrans %}
</p>
<p>
<form class="form form-uninstall" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-danger" name="uninstall_confirm"
value="{% trans 'Uninstall' %}"/>
</form>
</p>
{% endblock %}

View File

@ -15,6 +15,8 @@ urlpatterns = [
name='language-selection'),
re_path(r'^apps/$', views.AppsIndexView.as_view(), name='apps'),
re_path(r'^sys/$', views.system_index, name='system'),
re_path(r'^uninstall/(?P<app_id>[1-9a-z\-_]+)/$',
views.UninstallView.as_view(), name='uninstall'),
# captcha urls are public
re_path(r'^captcha/image/(?P<key>\w+)/$', public(cviews.captcha_image),

View File

@ -3,6 +3,7 @@
Main FreedomBox views.
"""
import datetime
import time
import urllib.parse
@ -349,6 +350,57 @@ class SetupView(TemplateView):
if component.has_unavailable_packages())
class UninstallView(FormView):
"""View to uninstall apps."""
form_class = forms.UninstallForm
template_name = 'uninstall.html'
def dispatch(self, request, *args, **kwargs):
"""Don't allow the view to be used on essential apps."""
app_id = self.kwargs['app_id']
app = app_module.App.get(app_id)
if app.info.is_essential:
raise Http404
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, *args, **kwargs):
"""Add app information to the context data."""
context = super().get_context_data(*args, **kwargs)
app_id = self.kwargs['app_id']
app = app_module.App.get(app_id)
context['app_info'] = app.info
return context
def get_success_url(self):
"""Return the URL to redirect to after uninstall."""
return reverse(self.kwargs['app_id'] + ':index')
def form_valid(self, form):
"""Uninstall the app."""
app_id = self.kwargs['app_id']
# Backup the app
if form.cleaned_data['should_backup']:
repository_id = form.cleaned_data['repository']
import plinth.modules.backups.repository as repository_module
repository = repository_module.get_instance(repository_id)
if repository.flags.get('mountable'):
repository.mount()
name = datetime.datetime.now().strftime(
'%Y-%m-%d:%H:%M:%S') + ' ' + str(
_('before uninstall of {app_id}')).format(app_id=app_id)
repository.create_archive(name, [app_id])
# Uninstall
setup.run_uninstall_on_app(app_id)
return super().form_valid(form)
def notification_dismiss(request, id):
"""Dismiss a notification."""
from .notification import Notification