assets: add basic export/import of assets in tar file (#6756)
This commit is contained in:
parent
ed5b366a6b
commit
080aa779c8
|
@ -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)
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
|
|
|
@ -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 %}
|
|
@ -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 = [
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue