manager: add tar format for site export/import (#45128)
This commit is contained in:
parent
0f856f1334
commit
c0158d6172
|
@ -196,4 +196,8 @@ class CellVisibilityForm(forms.Form):
|
|||
|
||||
|
||||
class SiteImportForm(forms.Form):
|
||||
site_json = forms.FileField(label=_('Site Export File'))
|
||||
site_file = forms.FileField(label=_('Site Export File'))
|
||||
|
||||
|
||||
class SiteExportForm(forms.Form):
|
||||
include_asset = forms.BooleanField(label=_('Include assets into the export'), required=False)
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<a class="extra-actions-menu-opener"></a>
|
||||
<a rel="popup" href="{% url 'combo-manager-page-add' %}">{% trans 'New' %}</a>
|
||||
<ul class="extra-actions-menu">
|
||||
<li><a download href="{% url 'combo-manager-site-export' %}">{% trans 'Export Site' %}</a></li>
|
||||
<li><a href="{% url 'combo-manager-site-export' %}" rel="popup" data-autoclose-dialog="true">{% trans 'Export Site' %}</a></li>
|
||||
<li><a href="{% url 'combo-manager-site-import' %}">{% trans 'Import Site' %}</a></li>
|
||||
<li><a href="{% url 'combo-manager-invalid-cell-report' %}">{% trans 'Anomaly report' %}</a></li>
|
||||
{% for extra_action in extra_actions %}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
{% extends "combo/manager_base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block appbar %}
|
||||
<h2>{% trans "Site Export" %}</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<div class="buttons">
|
||||
<button>{% trans 'Export' %}</button>
|
||||
<a class="cancel" href="{% url 'combo-manager-homepage' %}">{% trans 'Cancel' %}</a>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -18,6 +18,7 @@ import hashlib
|
|||
import datetime
|
||||
import json
|
||||
from operator import attrgetter
|
||||
import tarfile
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
|
@ -30,6 +31,7 @@ from django.shortcuts import get_object_or_404
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.encoding import force_text, force_bytes
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.six import BytesIO
|
||||
from django.utils.timezone import localtime
|
||||
from django.views.decorators.csrf import requires_csrf_token
|
||||
from django.views.generic import (RedirectView, DetailView,
|
||||
|
@ -37,10 +39,11 @@ from django.views.generic import (RedirectView, DetailView,
|
|||
|
||||
from combo.data.models import Page, CellBase, ParentContentCell, PageSnapshot, LinkListCell
|
||||
from combo.data.library import get_cell_class
|
||||
from combo.data.utils import export_site, import_site, MissingGroups
|
||||
from combo.data.utils import (
|
||||
export_site, export_site_tar, import_site, import_site_tar, MissingGroups)
|
||||
from combo import plugins
|
||||
|
||||
from .forms import (PageEditTitleForm, PageVisibilityForm, SiteImportForm,
|
||||
from .forms import (PageEditTitleForm, PageVisibilityForm, SiteImportForm, SiteExportForm,
|
||||
PageEditRedirectionForm, PageSelectTemplateForm, PageEditSlugForm,
|
||||
PageEditPictureForm, PageEditIncludeInNavigationForm,
|
||||
PageEditDescriptionForm, CellVisibilityForm, PageDuplicateForm, PageExportForm)
|
||||
|
@ -61,14 +64,23 @@ class HomepageView(ListView):
|
|||
homepage = HomepageView.as_view()
|
||||
|
||||
|
||||
class SiteExportView(ListView):
|
||||
model = Page
|
||||
class SiteExportView(FormView):
|
||||
form_class = SiteExportForm
|
||||
template_name = 'combo/site_export.html'
|
||||
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
response = HttpResponse(content_type='application/json')
|
||||
json.dump(export_site(), response, indent=2)
|
||||
def post(self, request, *args, **kwargs):
|
||||
if request.POST.get('include_asset'):
|
||||
fd = BytesIO()
|
||||
export_site_tar(fd)
|
||||
response = HttpResponse(content=fd.getvalue(), content_type='application/x-tar')
|
||||
response['Content-Disposition'] = 'attachment; filename="site-export.tar"'
|
||||
else:
|
||||
response = HttpResponse(content_type='application/json')
|
||||
response['Content-Disposition'] = 'attachment; filename="site-export.json"'
|
||||
json.dump(export_site(), response, indent=2)
|
||||
return response
|
||||
|
||||
|
||||
site_export = SiteExportView.as_view()
|
||||
|
||||
|
||||
|
@ -78,16 +90,28 @@ class SiteImportView(FormView):
|
|||
success_url = reverse_lazy('combo-manager-homepage')
|
||||
|
||||
def form_valid(self, form):
|
||||
fd = self.request.FILES['site_file'].file
|
||||
try:
|
||||
json_site = json.loads(force_text(self.request.FILES['site_json'].read()))
|
||||
except ValueError:
|
||||
form.add_error('site_json', _('File is not in the expected JSON format.'))
|
||||
return self.form_invalid(form)
|
||||
|
||||
tarfile.open(mode='r', fileobj=fd)
|
||||
except tarfile.TarError as e:
|
||||
try:
|
||||
fd.seek(0)
|
||||
json_site = json.loads(force_text(fd.read()))
|
||||
except ValueError:
|
||||
form.add_error('site_file', _('File is not in the expected TAR or JSON format.'))
|
||||
return self.form_invalid(form)
|
||||
else:
|
||||
format = 'json'
|
||||
else:
|
||||
format = 'tar'
|
||||
fd.seek(0)
|
||||
try:
|
||||
import_site(json_site, request=self.request)
|
||||
if format == 'json':
|
||||
import_site(json_site, request=self.request)
|
||||
else:
|
||||
import_site_tar(fd, request=self.request)
|
||||
except MissingGroups as e:
|
||||
form.add_error('site_json', force_text(e))
|
||||
form.add_error('site_file', force_text(e))
|
||||
return self.form_invalid(form)
|
||||
|
||||
return super(SiteImportView, self).form_valid(form)
|
||||
|
|
|
@ -6,6 +6,7 @@ import os
|
|||
import re
|
||||
import shutil
|
||||
|
||||
from django.core.files import File
|
||||
from django.core.files.storage import default_storage
|
||||
from django.urls import reverse
|
||||
from django.conf import settings
|
||||
|
@ -608,7 +609,7 @@ def test_export_page_order():
|
|||
assert ordered_list[3] == page3
|
||||
|
||||
|
||||
def test_site_export_import(app, admin_user):
|
||||
def test_site_export_import_json(app, admin_user):
|
||||
Page.objects.all().delete()
|
||||
page1 = Page(title='One', slug='one', template_name='standard')
|
||||
page1.save()
|
||||
|
@ -631,6 +632,7 @@ def test_site_export_import(app, admin_user):
|
|||
app = login(app)
|
||||
resp = app.get('/manage/')
|
||||
resp = resp.click('Export Site')
|
||||
resp = resp.form.submit()
|
||||
assert resp.headers['content-type'] == 'application/json'
|
||||
site_export = resp.body
|
||||
|
||||
|
@ -639,7 +641,7 @@ def test_site_export_import(app, admin_user):
|
|||
app = login(app)
|
||||
resp = app.get('/manage/')
|
||||
resp = resp.click('Import Site')
|
||||
resp.form['site_json'] = Upload('site-export.json', site_export, 'application/json')
|
||||
resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json')
|
||||
resp = resp.form.submit()
|
||||
assert Page.objects.count() == 4
|
||||
assert LinkCell.objects.count() == 2
|
||||
|
@ -649,9 +651,52 @@ def test_site_export_import(app, admin_user):
|
|||
# check with invalid file
|
||||
resp = app.get('/manage/')
|
||||
resp = resp.click('Import Site')
|
||||
resp.form['site_json'] = Upload('site-export.json', b'invalid content', 'application/json')
|
||||
resp.form['site_file'] = Upload('site-export.json', b'invalid content', 'application/json')
|
||||
resp = resp.form.submit()
|
||||
assert 'File is not in the expected JSON format.' in resp.text
|
||||
assert 'File is not in the expected TAR or JSON format.' in resp.text
|
||||
|
||||
|
||||
def test_site_export_import_tar(app, admin_user):
|
||||
Page.objects.all().delete()
|
||||
page1 = Page(title='One', slug='one', template_name='standard')
|
||||
page1.save()
|
||||
cell = TextCell(page=page1, placeholder='content', text='Foobar', order=0)
|
||||
cell.save()
|
||||
Asset(key='collectivity:banner', asset=File(BytesIO(b'test'), 'test.png')).save()
|
||||
path = default_storage.path('')
|
||||
assert open('%s/assets/test.png' % path, 'r').read() == 'test'
|
||||
|
||||
app = login(app)
|
||||
resp = app.get('/manage/')
|
||||
resp = resp.click('Export Site')
|
||||
resp.form['include_asset'] = True
|
||||
resp = resp.form.submit()
|
||||
assert resp.headers['content-type'] == 'application/x-tar'
|
||||
site_export = resp.body
|
||||
|
||||
Page.objects.all().delete()
|
||||
Asset.objects.filter(key='collectivity:banner').delete()
|
||||
assert Page.objects.count() == 0
|
||||
assert TextCell.objects.count() == 0
|
||||
assert Asset.objects.filter(key='collectivity:banner').count() == 0
|
||||
open('%s/assets/test.png' % path, 'w').write('foo')
|
||||
app = login(app)
|
||||
resp = app.get('/manage/')
|
||||
resp = resp.click('Import Site')
|
||||
resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json')
|
||||
resp = resp.form.submit()
|
||||
assert Page.objects.count() == 1
|
||||
assert TextCell.objects.count() == 1
|
||||
assert Asset.objects.filter(key='collectivity:banner').count() == 1
|
||||
assert open('%s/assets/test.png' % path, 'r').read() == 'foo'
|
||||
|
||||
os.remove('%s/assets/test.png' % path)
|
||||
app = login(app)
|
||||
resp = app.get('/manage/')
|
||||
resp = resp.click('Import Site')
|
||||
resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json')
|
||||
resp = resp.form.submit()
|
||||
assert open('%s/assets/test.png' % path, 'r').read() == 'test'
|
||||
|
||||
|
||||
def test_site_export_import_missing_group(app, admin_user):
|
||||
|
@ -664,6 +709,7 @@ def test_site_export_import_missing_group(app, admin_user):
|
|||
app = login(app)
|
||||
resp = app.get('/manage/')
|
||||
resp = resp.click('Export Site')
|
||||
resp = resp.form.submit()
|
||||
assert resp.headers['content-type'] == 'application/json'
|
||||
site_export = resp.body
|
||||
|
||||
|
@ -673,7 +719,7 @@ def test_site_export_import_missing_group(app, admin_user):
|
|||
app = login(app)
|
||||
resp = app.get('/manage/')
|
||||
resp = resp.click('Import Site')
|
||||
resp.form['site_json'] = Upload('site-export.json', site_export, 'application/json')
|
||||
resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json')
|
||||
resp = resp.form.submit()
|
||||
assert 'Missing groups: foobar' in resp.text
|
||||
|
||||
|
@ -685,13 +731,14 @@ def test_site_export_import_unknown_parent(app, admin_user):
|
|||
app = login(app)
|
||||
resp = app.get('/manage/')
|
||||
resp = resp.click('Export Site')
|
||||
resp = resp.form.submit()
|
||||
payload = json.loads(force_str(resp.body))
|
||||
payload['pages'][0]['fields']['exclude_from_navigation'] = False
|
||||
payload['pages'][0]['fields']['parent'] = ['unknown-parent']
|
||||
|
||||
resp = app.get('/manage/')
|
||||
resp = resp.click('Import Site')
|
||||
resp.form['site_json'] = Upload('site-export.json', force_bytes(json.dumps(payload)), 'application/json')
|
||||
resp.form['site_file'] = Upload('site-export.json', force_bytes(json.dumps(payload)), 'application/json')
|
||||
resp = resp.form.submit().follow()
|
||||
assert 'Unknown parent for page "One"; parent has been reset and page was excluded from navigation.' in resp.text
|
||||
|
||||
|
|
Loading…
Reference in New Issue