assets: add basic export/import of assets in tar file (#6756)

This commit is contained in:
Frédéric Péters 2019-04-28 10:48:32 +02:00
parent ed5b366a6b
commit 080aa779c8
6 changed files with 148 additions and 2 deletions

View File

@ -20,3 +20,8 @@ from django.utils.translation import ugettext_lazy as _
class AssetUploadForm(forms.Form):
upload = forms.FileField(label=_('File'))
class AssetsImportForm(forms.Form):
assets_file = forms.FileField(label=_('Assets File'))
overwrite = forms.BooleanField(label=_('Overwrite Existing Files'), required=False)

View File

@ -4,7 +4,12 @@
{% block appbar %}
<h2>{% trans 'Assets' %}</h2>
<span class="actions">
<a class="extra-actions-menu-opener"></a>
<a href="{% url 'combo-manager-asset-upload' %}" rel="popup">{% trans 'Upload' %}</a>
<ul class="extra-actions-menu">
<li><a href="{% url 'combo-manager-assets-export' %}">{% trans 'Export Assets' %}</a></li>
<li><a rel="popup" href="{% url 'combo-manager-assets-import' %}">{% trans 'Import Assets' %}</a></li>
</ul>
</span>
{% endblock %}

View File

@ -0,0 +1,18 @@
{% extends "combo/manager_base.html" %}
{% load i18n %}
{% block appbar %}
<h2>{% trans "Assets Import" %}</h2>
{% endblock %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<div class="buttons">
<button class="submit-button">{% trans "Import" %}</button>
<a class="cancel" href="{% url 'combo-manager-assets' %}">{% trans 'Cancel' %}</a>
</div>
</form>
{% endblock %}

View File

@ -27,6 +27,8 @@ assets_manager_urls = [
url(r'^upload/$', views.asset_upload, name='combo-manager-asset-upload'),
url(r'^upload/(?P<key>[\w_:-]+)/$', views.slot_asset_upload, name='combo-manager-slot-asset-upload'),
url(r'^delete/(?P<key>[\w_:-]+)/$', views.slot_asset_delete, name='combo-manager-slot-asset-delete'),
url(r'^export/$', views.assets_export, name='combo-manager-assets-export'),
url(r'^import/$', views.assets_import, name='combo-manager-assets-import'),
]
urlpatterns = [

View File

@ -14,14 +14,18 @@
# 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/>.
import tarfile
import os
from django.conf import settings
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.core.files.storage import default_storage
from django.core.urlresolvers import reverse, reverse_lazy
from django.http import Http404
from django.http import Http404, HttpResponse
from django.shortcuts import redirect
from django.utils.six import BytesIO
from django.utils.translation import ugettext_lazy as _
from django.views.generic import TemplateView, ListView, FormView
import ckeditor
@ -29,7 +33,7 @@ from sorl.thumbnail.shortcuts import get_thumbnail
from combo.data.models import CellBase
from .forms import AssetUploadForm
from .forms import AssetUploadForm, AssetsImportForm
from .models import Asset
@ -240,6 +244,43 @@ class SlotAssetDelete(TemplateView):
slot_asset_delete = SlotAssetDelete.as_view()
class AssetsImport(FormView):
form_class = AssetsImportForm
template_name = 'combo/manager_assets_import.html'
success_url = reverse_lazy('combo-manager-assets')
def form_valid(self, form):
overwrite = form.cleaned_data.get('overwrite')
try:
assets = tarfile.open(fileobj=form.cleaned_data['assets_file'])
except tarfile.TarError:
messages.error(self.request, _('The assets file is not valid.'))
return super(AssetsImport, self).form_valid(form)
media_prefix = default_storage.path('')
for tarinfo in assets.getmembers():
filepath = default_storage.path(tarinfo.name)
if not overwrite and os.path.exists(filepath):
continue
assets.extract(tarinfo, path=media_prefix)
messages.success(self.request, _('The assets file has been imported.'))
return super(AssetsImport, self).form_valid(form)
assets_import = AssetsImport.as_view()
def assets_export(request, *args, **kwargs):
fd = BytesIO()
assets_file = tarfile.open('assets.tar', 'w', fileobj=fd)
media_prefix = default_storage.path('')
for basedir, dirnames, filenames in os.walk(media_prefix):
for filename in filenames:
assets_file.add(
os.path.join(basedir, filename),
os.path.join(basedir, filename)[len(media_prefix):])
assets_file.close()
return HttpResponse(fd.getvalue(), content_type='application/x-tar')
def serve_asset(request, key):
try:
asset = Asset.objects.get(key=key)

View File

@ -2,6 +2,7 @@ import base64
import json
import os
import re
import shutil
import mock
@ -823,6 +824,80 @@ def test_asset_slots_management(app, admin_user):
assert '>Delete<' in resp.text
assert Asset.objects.filter(key='collectivity:cgu').count() == 1
def test_asset_export_import(app, admin_user):
for path in ('uploads', 'assets', 'cache'):
if os.path.exists(default_storage.path(path)):
shutil.rmtree(default_storage.path(path))
app = login(app)
# upload a file
resp = app.get('/manage/assets/')
resp = resp.click('Upload')
resp.form['upload'] = Upload('test.png',
base64.decodestring(b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg=='),
'image/png')
resp = resp.form.submit()
resp = app.get('/manage/assets/')
resp = resp.click('Export')
assert resp.content_type == 'application/x-tar'
content = resp.content
for path in ('uploads', 'assets'):
if os.path.exists(default_storage.path(path)):
shutil.rmtree(default_storage.path(path))
resp = app.get('/manage/assets/')
assert 'have any asset yet.' in resp.text
resp = resp.click('Import')
resp.form['assets_file'] = Upload('test.tar', content)
resp = resp.form.submit()
assert sum([len(x[2]) for x in os.walk(default_storage.path(''))]) == 2
resp = resp.follow()
assert 'The assets file has been imported.' in resp.text
# test no overwrite
filename = re.findall('data-href="(.*?)"', resp.text)[0][7:] # strip /media/
with open(default_storage.path(filename), 'w') as fd:
fd.write('test') # 4 bytes
assert os.stat(default_storage.path(filename)).st_size == 4
resp = app.get('/manage/assets/')
resp = resp.click('Import')
resp.form['assets_file'] = Upload('test.tar', content)
resp = resp.form.submit()
resp = resp.follow()
assert 'The assets file has been imported.' in resp.text
assert os.stat(default_storage.path(filename)).st_size == 4
# test overwrite
resp = app.get('/manage/assets/')
resp = resp.click('Import')
resp.form['overwrite'] = True
resp.form['assets_file'] = Upload('test.tar', content)
resp = resp.form.submit()
resp = resp.follow()
assert 'The assets file has been imported.' in resp.text
assert os.stat(default_storage.path(filename)).st_size == 67
# test uploading garbage
for path in ('uploads', 'assets'):
if os.path.exists(default_storage.path(path)):
shutil.rmtree(default_storage.path(path))
resp = app.get('/manage/assets/')
resp = resp.click('Import')
resp.form['assets_file'] = Upload('test.tar', b'garbage')
resp = resp.form.submit()
assert sum([len(x[2]) for x in os.walk(default_storage.path(''))]) == 0
resp = resp.follow()
assert 'The assets file is not valid.' in resp.text
def test_menu_json(app, admin_user):
app.get('/manage/menu.json', status=302)