mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-02-11 08:23:49 +00:00
backups: New app to manage borgbackup archives
Reviewed-by: Joseph Nuthalapati <njoseph@thoughtworks.com>
This commit is contained in:
parent
7e2c9b1095
commit
c852cd824f
122
actions/backups
Executable file
122
actions/backups
Executable file
@ -0,0 +1,122 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- mode: python -*-
|
||||
#
|
||||
# This file is part of FreedomBox.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
"""
|
||||
Configuration helper for backups.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
REPOSITORY = '/tmp/freedombox-backups'
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
"""Return parsed command line arguments as dictionary."""
|
||||
parser = argparse.ArgumentParser()
|
||||
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
|
||||
|
||||
subparsers.add_parser(
|
||||
'setup', help='Create repository if it does not already exist')
|
||||
subparsers.add_parser('info', help='Show repository information')
|
||||
subparsers.add_parser('list', help='List repository contents')
|
||||
|
||||
create = subparsers.add_parser('create', help='Create archive')
|
||||
create.add_argument('--name', help='Archive name', required=True)
|
||||
create.add_argument('--path', help='Path to archive', required=True)
|
||||
|
||||
delete = subparsers.add_parser('delete', help='Delete archive')
|
||||
delete.add_argument('--name', help='Archive name', required=True)
|
||||
|
||||
extract = subparsers.add_parser('extract', help='Extract archive contents')
|
||||
extract.add_argument('--name', help='Archive name', required=True)
|
||||
extract.add_argument('--destination', help='Extract destination',
|
||||
required=True)
|
||||
|
||||
export = subparsers.add_parser('export',
|
||||
help='Export archive contents as tarball')
|
||||
export.add_argument('--name', help='Archive name', required=True)
|
||||
export.add_argument('--filename', help='Tarball file name', required=True)
|
||||
|
||||
subparsers.required = True
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def subcommand_setup(_):
|
||||
"""Create repository if it does not already exist."""
|
||||
try:
|
||||
subprocess.run(['borg', 'info', REPOSITORY], check=True)
|
||||
except:
|
||||
subprocess.run(['borg', 'init', '--encryption', 'none', REPOSITORY])
|
||||
|
||||
|
||||
def subcommand_info(_):
|
||||
"""Show repository information."""
|
||||
subprocess.run(['borg', 'info', '--json', REPOSITORY], check=True)
|
||||
|
||||
|
||||
def subcommand_list(_):
|
||||
"""List repository contents."""
|
||||
subprocess.run(['borg', 'list', '--json', REPOSITORY], check=True)
|
||||
|
||||
|
||||
def subcommand_create(arguments):
|
||||
"""Create archive."""
|
||||
subprocess.run([
|
||||
'borg', 'create', '--json', REPOSITORY + '::' + arguments.name,
|
||||
arguments.path
|
||||
], check=True)
|
||||
|
||||
|
||||
def subcommand_delete(arguments):
|
||||
"""Delete archive."""
|
||||
subprocess.run(['borg', 'delete', REPOSITORY + '::' + arguments.name],
|
||||
check=True)
|
||||
|
||||
|
||||
def subcommand_extract(arguments):
|
||||
"""Extract archive contents."""
|
||||
prev_dir = os.getcwd()
|
||||
try:
|
||||
os.chdir(os.path.expanduser(arguments.destination))
|
||||
subprocess.run(['borg', 'extract', REPOSITORY + '::' + arguments.name],
|
||||
check=True)
|
||||
finally:
|
||||
os.chdir(prev_dir)
|
||||
|
||||
|
||||
def subcommand_export(arguments):
|
||||
"""Export archive contents as tarball."""
|
||||
subprocess.run([
|
||||
'borg', 'export-tar', REPOSITORY + '::' + arguments.name,
|
||||
arguments.filename
|
||||
], check=True)
|
||||
|
||||
|
||||
def main():
|
||||
"""Parse arguments and perform all duties."""
|
||||
arguments = parse_arguments()
|
||||
|
||||
subcommand = arguments.subcommand.replace('-', '_')
|
||||
subcommand_method = globals()['subcommand_' + subcommand]
|
||||
subcommand_method(arguments)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
1
data/etc/plinth/modules-enabled/backups
Normal file
1
data/etc/plinth/modules-enabled/backups
Normal file
@ -0,0 +1 @@
|
||||
plinth.modules.backups
|
||||
85
plinth/modules/backups/__init__.py
Normal file
85
plinth/modules/backups/__init__.py
Normal file
@ -0,0 +1,85 @@
|
||||
#
|
||||
# This file is part of FreedomBox.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
"""
|
||||
FreedomBox app to manage backup archives.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from plinth import actions
|
||||
from plinth.menu import main_menu
|
||||
|
||||
version = 1
|
||||
|
||||
managed_packages = ['borgbackup']
|
||||
|
||||
name = _('Backups')
|
||||
|
||||
description = [_('Backups allows creating and managing backup archives.'), ]
|
||||
|
||||
service = None
|
||||
|
||||
|
||||
def init():
|
||||
"""Intialize the module."""
|
||||
menu = main_menu.get('system')
|
||||
menu.add_urlname(name, 'glyphicon-duplicate', 'backups:index')
|
||||
|
||||
|
||||
def setup(helper, old_version=None):
|
||||
"""Install and configure the module."""
|
||||
helper.install(managed_packages)
|
||||
helper.call('post', actions.superuser_run, 'backups', ['setup'])
|
||||
|
||||
|
||||
def get_info():
|
||||
output = actions.superuser_run('backups', ['info'])
|
||||
return json.loads(output)
|
||||
|
||||
|
||||
def list_archives():
|
||||
output = actions.superuser_run('backups', ['list'])
|
||||
return json.loads(output)['archives']
|
||||
|
||||
|
||||
def get_archive(name):
|
||||
for archive in list_archives():
|
||||
if archive['name'] == name:
|
||||
return archive
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def create_archive(name, path):
|
||||
actions.superuser_run('backups',
|
||||
['create', '--name', name, '--path', path])
|
||||
|
||||
|
||||
def delete_archive(name):
|
||||
actions.superuser_run('backups', ['delete', '--name', name])
|
||||
|
||||
|
||||
def extract_archive(name, destination):
|
||||
actions.superuser_run(
|
||||
'backups', ['extract', '--name', name, '--destination', destination])
|
||||
|
||||
|
||||
def export_archive(name, filename):
|
||||
actions.superuser_run('backups',
|
||||
['export', '--name', name, '--filename', filename])
|
||||
43
plinth/modules/backups/forms.py
Normal file
43
plinth/modules/backups/forms.py
Normal file
@ -0,0 +1,43 @@
|
||||
#
|
||||
# This file is part of FreedomBox.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
"""
|
||||
Forms for backups module.
|
||||
"""
|
||||
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class CreateArchiveForm(forms.Form):
|
||||
name = forms.CharField(label=_('Archive name'), strip=True,
|
||||
help_text=_('Name for new backup archive.'))
|
||||
|
||||
path = forms.CharField(label=_('Path'), strip=True, help_text=_(
|
||||
'Disk path to a folder on this server that will be archived into '
|
||||
'backup repository.'))
|
||||
|
||||
|
||||
class ExtractArchiveForm(forms.Form):
|
||||
path = forms.CharField(label=_('Path'), strip=True, help_text=_(
|
||||
'Disk path to a folder on this server where the archive will be '
|
||||
'extracted.'))
|
||||
|
||||
|
||||
class ExportArchiveForm(forms.Form):
|
||||
filename = forms.CharField(
|
||||
label=_('Exported filename'), strip=True,
|
||||
help_text=_('Name for the tar file exported from the archive.'))
|
||||
89
plinth/modules/backups/templates/backups.html
Normal file
89
plinth/modules/backups/templates/backups.html
Normal file
@ -0,0 +1,89 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
#
|
||||
# This file is part of FreedomBox.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
{% endcomment %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_head %}
|
||||
<style type="text/css">
|
||||
.share-operations form {
|
||||
display: inline-block;
|
||||
}
|
||||
.share-operations {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>{{ title }}</h2>
|
||||
|
||||
{% for paragraph in description %}
|
||||
<p>{{ paragraph|safe }}</p>
|
||||
{% endfor %}
|
||||
|
||||
<p>
|
||||
<a title="{% trans 'Create archive' %}"
|
||||
role="button" class="btn btn-primary"
|
||||
href="{% url 'backups:create' %}">
|
||||
{% trans 'Create archive' %}
|
||||
</a>
|
||||
</p>
|
||||
|
||||
{% if not archives %}
|
||||
<p>{% trans 'No archives currently exist.' %}</p>
|
||||
{% else %}
|
||||
<table class="table table-bordered table-condensed table-striped"
|
||||
id="archives-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Time" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for archive in archives %}
|
||||
<tr id="archive-{{ archive.name }}" class="archive">
|
||||
<td class="archive-name">{{ archive.name }}</td>
|
||||
<td class="archive-time">{{ archive.time }}</td>
|
||||
<td class="archive-operations">
|
||||
<a class="archive-extract btn btn-sm btn-default"
|
||||
href="{% url 'backups:extract' archive.name %}">
|
||||
{% trans "Extract" %}
|
||||
</a>
|
||||
<a class="archive-export btn btn-sm btn-default"
|
||||
href="{% url 'backups:export' archive.name %}">
|
||||
{% trans "Export" %}
|
||||
</a>
|
||||
<a class="archive-delete btn btn-sm btn-default"
|
||||
href="{% url 'backups:delete' archive.name %}">
|
||||
<span class="glyphicon glyphicon-trash" aria-hidden="true">
|
||||
</span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
57
plinth/modules/backups/templates/backups_delete.html
Normal file
57
plinth/modules/backups/templates/backups_delete.html
Normal file
@ -0,0 +1,57 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
#
|
||||
# This file is part of FreedomBox.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{{ title }}</h2>
|
||||
|
||||
<p>{% trans "Delete this archive permanently?" %}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<table class="table table-bordered table-condensed table-striped">
|
||||
<thead>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Time" %}</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ archive.name }}</td>
|
||||
<td>{{ archive.time }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
<input type="submit" class="btn btn-danger"
|
||||
value="{% blocktrans trimmed with name=archive.name %}
|
||||
Delete Archive {{ name }}
|
||||
{% endblocktrans %}"/>
|
||||
</form>
|
||||
</p>
|
||||
|
||||
{% endblock %}
|
||||
37
plinth/modules/backups/templates/backups_form.html
Normal file
37
plinth/modules/backups/templates/backups_form.html
Normal file
@ -0,0 +1,37 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
#
|
||||
# This file is part of FreedomBox.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h3>{{ title }}</h3>
|
||||
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
{{ form|bootstrap }}
|
||||
|
||||
<input type="submit" class="btn btn-primary"
|
||||
value="{% trans "Submit" %}"/>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
35
plinth/modules/backups/urls.py
Normal file
35
plinth/modules/backups/urls.py
Normal file
@ -0,0 +1,35 @@
|
||||
#
|
||||
# This file is part of FreedomBox.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
"""
|
||||
URLs for the backups module.
|
||||
"""
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from .views import IndexView, CreateArchiveView, DeleteArchiveView, \
|
||||
ExtractArchiveView, ExportArchiveView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^sys/backups/$', IndexView.as_view(), name='index'),
|
||||
url(r'^sys/backups/create/$', CreateArchiveView.as_view(), name='create'),
|
||||
url(r'^sys/backups/(?P<name>[a-z0-9]+)/delete/$',
|
||||
DeleteArchiveView.as_view(), name='delete'),
|
||||
url(r'^sys/backups/(?P<name>[a-z0-9]+)/extract/$',
|
||||
ExtractArchiveView.as_view(), name='extract'),
|
||||
url(r'^sys/backups/(?P<name>[a-z0-9]+)/export/$',
|
||||
ExportArchiveView.as_view(), name='export'),
|
||||
]
|
||||
136
plinth/modules/backups/views.py
Normal file
136
plinth/modules/backups/views.py
Normal file
@ -0,0 +1,136 @@
|
||||
#
|
||||
# This file is part of FreedomBox.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
"""
|
||||
Views for the backups app.
|
||||
"""
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.http import Http404
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.generic import FormView, TemplateView
|
||||
|
||||
from plinth.modules import backups
|
||||
|
||||
from .forms import CreateArchiveForm, ExtractArchiveForm, ExportArchiveForm
|
||||
|
||||
|
||||
class IndexView(TemplateView):
|
||||
"""View to show list of archives."""
|
||||
template_name = 'backups.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Return additional context for rendering the template."""
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = backups.name
|
||||
context['description'] = backups.description
|
||||
context['info'] = backups.get_info()
|
||||
context['archives'] = backups.list_archives()
|
||||
return context
|
||||
|
||||
|
||||
class CreateArchiveView(SuccessMessageMixin, FormView):
|
||||
"""View to create a new archive."""
|
||||
form_class = CreateArchiveForm
|
||||
prefix = 'backups'
|
||||
template_name = 'backups_form.html'
|
||||
success_url = reverse_lazy('backups:index')
|
||||
success_message = _('Archive created.')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Return additional context for rendering the template."""
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = _('Create Archive')
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
"""Create the archive on valid form submission."""
|
||||
backups.create_archive(form.cleaned_data['name'],
|
||||
form.cleaned_data['path'])
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class DeleteArchiveView(SuccessMessageMixin, TemplateView):
|
||||
"""View to delete an archive."""
|
||||
template_name = 'backups_delete.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Return additional context for rendering the template."""
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = _('Delete Archive')
|
||||
context['archive'] = backups.get_archive(self.kwargs['name'])
|
||||
if context['archive'] is None:
|
||||
raise Http404
|
||||
|
||||
return context
|
||||
|
||||
def post(self, request, name):
|
||||
"""Delete the archive."""
|
||||
backups.delete_archive(name)
|
||||
messages.success(request, _('Archive deleted.'))
|
||||
return redirect(reverse_lazy('backups:index'))
|
||||
|
||||
|
||||
class ExtractArchiveView(SuccessMessageMixin, FormView):
|
||||
"""View to extract an archive."""
|
||||
form_class = ExtractArchiveForm
|
||||
prefix = 'backups'
|
||||
template_name = 'backups_form.html'
|
||||
success_url = reverse_lazy('backups:index')
|
||||
success_message = _('Archive extracted.')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Return additional context for rendering the template."""
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = _('Extract Archive')
|
||||
context['archive'] = backups.get_archive(self.kwargs['name'])
|
||||
if context['archive'] is None:
|
||||
raise Http404
|
||||
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
"""Create the archive on valid form submission."""
|
||||
backups.extract_archive(self.kwargs['name'], form.cleaned_data['path'])
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class ExportArchiveView(SuccessMessageMixin, FormView):
|
||||
"""View to export an archive."""
|
||||
form_class = ExportArchiveForm
|
||||
prefix = 'backups'
|
||||
template_name = 'backups_form.html'
|
||||
success_url = reverse_lazy('backups:index')
|
||||
success_message = _('Archive exported.')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Return additional context for rendering the template."""
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = _('Export Archive')
|
||||
context['archive'] = backups.get_archive(self.kwargs['name'])
|
||||
if context['archive'] is None:
|
||||
raise Http404
|
||||
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
"""Create the archive on valid form submission."""
|
||||
backups.export_archive(self.kwargs['name'],
|
||||
form.cleaned_data['filename'])
|
||||
return super().form_valid(form)
|
||||
Loading…
x
Reference in New Issue
Block a user