From 7da361bbca3bfada1cc586b8b95fab6a6e7ffcfe Mon Sep 17 00:00:00 2001 From: Michael Pimmer Date: Sun, 16 Sep 2018 15:15:56 +0000 Subject: [PATCH] Backup module: Implemented uploading files Reviewed-by: James Valleroy --- plinth/modules/backups/__init__.py | 23 ++++++----- plinth/modules/backups/forms.py | 37 ++++++++++++++++- plinth/modules/backups/templates/backups.html | 15 +++++-- .../backups/templates/backups_upload.html | 40 +++++++++++++++++++ plinth/modules/backups/urls.py | 3 +- plinth/modules/backups/views.py | 36 ++++++++++++++++- 6 files changed, 138 insertions(+), 16 deletions(-) create mode 100644 plinth/modules/backups/templates/backups_upload.html diff --git a/plinth/modules/backups/__init__.py b/plinth/modules/backups/__init__.py index b461213b1..249930f58 100644 --- a/plinth/modules/backups/__init__.py +++ b/plinth/modules/backups/__init__.py @@ -45,6 +45,8 @@ service = None MANIFESTS_FOLDER = '/var/lib/plinth/backups-manifests/' +BACKUP_FOLDER_NAME = 'FreedomBox-backups' + def init(): """Intialize the module.""" @@ -106,13 +108,10 @@ def delete_archive(name): def export_archive(name, location): - if location[-1] != '/': - location += '/' - - filename = location + 'FreedomBox-backups/' + get_valid_filename( - name) + '.tar.gz' + filepath = get_archive_path(location, get_valid_filename(name) + '.tar.gz') + # TODO: that's a full path, not a filename; rename argument actions.superuser_run('backups', - ['export', '--name', name, '--filename', filename]) + ['export', '--name', name, '--filename', filepath]) def get_export_locations(): @@ -140,13 +139,17 @@ def get_export_files(): return export_files +def get_archive_path(location, archive_name): + return os.path.join(location, BACKUP_FOLDER_NAME, archive_name) + + def find_exported_archive(disk_label, archive_name): """Return the full path for the exported archive file.""" locations = get_export_locations() - for location in locations: - if location[1] == disk_label: - return os.path.join(location[0], 'FreedomBox-backups', - archive_name) + for (location_path, location_name) in locations: + if location_name == disk_label: + return get_archive_path(location_path, archive_name) + raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), archive_name) diff --git a/plinth/modules/backups/forms.py b/plinth/modules/backups/forms.py index 5685a8529..b23fb8a3f 100644 --- a/plinth/modules/backups/forms.py +++ b/plinth/modules/backups/forms.py @@ -18,12 +18,15 @@ Forms for backups module. """ +import os + from django import forms from django.core import validators +from django.core.validators import FileExtensionValidator from django.utils.translation import ugettext_lazy as _ from . import backups as backups_api -from . import get_export_locations +from . import get_export_locations, get_archive_path class CreateArchiveForm(forms.Form): @@ -72,3 +75,35 @@ class RestoreForm(forms.Form): self.fields['selected_apps'].choices = [ (app[0], app[1].name) for app in apps] self.fields['selected_apps'].initial = [app[0] for app in apps] + + +class UploadForm(forms.Form): + location = forms.ChoiceField( + choices=(), + label=_('Location'), + initial='', + widget=forms.Select(), + required=True, + help_text=_('Location to upload the archive to')) + file = forms.FileField(label=_('Upload File'), required=True, + validators=[FileExtensionValidator(['gz'], + 'Backup files have to be in .tar.gz format')], + help_text=_('Select the backup file you want to upload')) + + def __init__(self, *args, **kwargs): + """Initialize the form with location choices.""" + super().__init__(*args, **kwargs) + # TODO: write a test that assures the format of get_export_locations + self.fields['location'].choices = get_export_locations() + + def clean(self): + """Check that the uploaded file does not exist""" + cleaned_data = super().clean() + file = cleaned_data.get('file') + location = cleaned_data.get('location') + if (file and file.name): + filepath = get_archive_path(location, file.name) + if os.path.exists(filepath): + raise forms.ValidationError("File %s already exists" % file.name) + else: + self.cleaned_data.update({'filepath': filepath}) diff --git a/plinth/modules/backups/templates/backups.html b/plinth/modules/backups/templates/backups.html index d40b03a1f..ecc03fbb6 100644 --- a/plinth/modules/backups/templates/backups.html +++ b/plinth/modules/backups/templates/backups.html @@ -39,6 +39,8 @@

{{ paragraph|safe }}

{% endfor %} +

{% trans 'Backup archives' %}

+ {% if available_apps %}

{% endif %} -

{% trans 'Backup archives' %}

{% if not archives %}

{% trans 'No archives currently exist.' %}

{% else %} @@ -97,9 +98,9 @@ {% endif %} -

{% trans 'Exported backup archives' %}

+

{% trans 'Existing backup files' %}

{% if not exports %} -

{% trans 'No exported backup archives were found.' %}

+

{% trans 'No existing backup files were found.' %}

{% else %} @@ -134,4 +135,12 @@
{% endif %} +

+ + {% trans 'Upload backup file' %} + +

+ {% endblock %} diff --git a/plinth/modules/backups/templates/backups_upload.html b/plinth/modules/backups/templates/backups_upload.html new file mode 100644 index 000000000..724449538 --- /dev/null +++ b/plinth/modules/backups/templates/backups_upload.html @@ -0,0 +1,40 @@ +{% 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 . +# +{% endcomment %} + +{% load bootstrap %} +{% load i18n %} + +{% block page_head %} +{% endblock %} + +{% block content %} + +

{{ title }}

+ +
+ {% csrf_token %} + + {{ form|bootstrap }} + + +
+ +{% endblock %} diff --git a/plinth/modules/backups/urls.py b/plinth/modules/backups/urls.py index 73e963da6..8b24714f5 100644 --- a/plinth/modules/backups/urls.py +++ b/plinth/modules/backups/urls.py @@ -21,7 +21,7 @@ URLs for the backups module. from django.conf.urls import url from .views import IndexView, CreateArchiveView, DownloadArchiveView, \ - ExportArchiveView, DeleteArchiveView, RestoreView + DeleteArchiveView, ExportArchiveView, RestoreView, UploadArchiveView urlpatterns = [ url(r'^sys/backups/$', IndexView.as_view(), name='index'), @@ -32,6 +32,7 @@ urlpatterns = [ DownloadArchiveView.as_view(), name='download'), url(r'^sys/backups/delete/(?P[^/]+)/$', DeleteArchiveView.as_view(), name='delete'), + url(r'^sys/backups/upload/$', UploadArchiveView.as_view(), name='upload'), url(r'^sys/backups/restore/(?P