0001-assets-add-basic-export-import-of-assets-in-tar-file.patch
combo/apps/assets/forms.py | ||
---|---|---|
20 | 20 | |
21 | 21 |
class AssetUploadForm(forms.Form): |
22 | 22 |
upload = forms.FileField(label=_('File')) |
23 | ||
24 | ||
25 |
class AssetsImportForm(forms.Form): |
|
26 |
assets_file = forms.FileField(label=_('Assets File')) |
|
27 |
overwrite = forms.BooleanField(label=_('Overwrite Existing Files'), required=False) |
combo/apps/assets/templates/combo/manager_assets.html | ||
---|---|---|
4 | 4 |
{% block appbar %} |
5 | 5 |
<h2>{% trans 'Assets' %}</h2> |
6 | 6 |
<span class="actions"> |
7 |
<a class="extra-actions-menu-opener"></a> |
|
7 | 8 |
<a href="{% url 'combo-manager-asset-upload' %}" rel="popup">{% trans 'Upload' %}</a> |
9 |
<ul class="extra-actions-menu"> |
|
10 |
<li><a href="{% url 'combo-manager-assets-export' %}">{% trans 'Export Assets' %}</a></li> |
|
11 |
<li><a rel="popup" href="{% url 'combo-manager-assets-import' %}">{% trans 'Import Assets' %}</a></li> |
|
12 |
</ul> |
|
8 | 13 |
</span> |
9 | 14 |
{% endblock %} |
10 | 15 |
combo/apps/assets/templates/combo/manager_assets_import.html | ||
---|---|---|
1 |
{% extends "combo/manager_base.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block appbar %} |
|
5 |
<h2>{% trans "Assets Import" %}</h2> |
|
6 |
{% endblock %} |
|
7 | ||
8 |
{% block content %} |
|
9 | ||
10 |
<form method="post" enctype="multipart/form-data"> |
|
11 |
{% csrf_token %} |
|
12 |
{{ form.as_p }} |
|
13 |
<div class="buttons"> |
|
14 |
<button class="submit-button">{% trans "Import" %}</button> |
|
15 |
<a class="cancel" href="{% url 'combo-manager-assets' %}">{% trans 'Cancel' %}</a> |
|
16 |
</div> |
|
17 |
</form> |
|
18 |
{% endblock %} |
combo/apps/assets/urls.py | ||
---|---|---|
27 | 27 |
url(r'^upload/$', views.asset_upload, name='combo-manager-asset-upload'), |
28 | 28 |
url(r'^upload/(?P<key>[\w_:-]+)/$', views.slot_asset_upload, name='combo-manager-slot-asset-upload'), |
29 | 29 |
url(r'^delete/(?P<key>[\w_:-]+)/$', views.slot_asset_delete, name='combo-manager-slot-asset-delete'), |
30 |
url(r'^export/$', views.assets_export, name='combo-manager-assets-export'), |
|
31 |
url(r'^import/$', views.assets_import, name='combo-manager-assets-import'), |
|
30 | 32 |
] |
31 | 33 | |
32 | 34 |
urlpatterns = [ |
combo/apps/assets/views.py | ||
---|---|---|
14 | 14 |
# You should have received a copy of the GNU Affero General Public License |
15 | 15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
import tarfile |
|
17 | 18 |
import os |
18 | 19 | |
19 | 20 |
from django.conf import settings |
21 |
from django.contrib import messages |
|
20 | 22 |
from django.core.exceptions import PermissionDenied |
21 | 23 |
from django.core.files.storage import default_storage |
22 | 24 |
from django.core.urlresolvers import reverse, reverse_lazy |
23 |
from django.http import Http404 |
|
25 |
from django.http import Http404, HttpResponse
|
|
24 | 26 |
from django.shortcuts import redirect |
27 |
from django.utils.six import BytesIO |
|
28 |
from django.utils.translation import ugettext_lazy as _ |
|
25 | 29 |
from django.views.generic import TemplateView, ListView, FormView |
26 | 30 | |
27 | 31 |
import ckeditor |
... | ... | |
29 | 33 | |
30 | 34 |
from combo.data.models import CellBase |
31 | 35 | |
32 |
from .forms import AssetUploadForm |
|
36 |
from .forms import AssetUploadForm, AssetsImportForm
|
|
33 | 37 |
from .models import Asset |
34 | 38 | |
35 | 39 | |
... | ... | |
240 | 244 |
slot_asset_delete = SlotAssetDelete.as_view() |
241 | 245 | |
242 | 246 | |
247 |
class AssetsImport(FormView): |
|
248 |
form_class = AssetsImportForm |
|
249 |
template_name = 'combo/manager_assets_import.html' |
|
250 |
success_url = reverse_lazy('combo-manager-assets') |
|
251 | ||
252 |
def form_valid(self, form): |
|
253 |
overwrite = form.cleaned_data.get('overwrite') |
|
254 |
try: |
|
255 |
assets = tarfile.open(fileobj=form.cleaned_data['assets_file']) |
|
256 |
except tarfile.TarError: |
|
257 |
messages.error(self.request, _('The assets file is not valid.')) |
|
258 |
return super(AssetsImport, self).form_valid(form) |
|
259 |
media_prefix = default_storage.path('') |
|
260 |
for tarinfo in assets.getmembers(): |
|
261 |
filepath = default_storage.path(tarinfo.name) |
|
262 |
if not overwrite and os.path.exists(filepath): |
|
263 |
continue |
|
264 |
assets.extract(tarinfo, path=media_prefix) |
|
265 |
messages.success(self.request, _('The assets file has been imported.')) |
|
266 |
return super(AssetsImport, self).form_valid(form) |
|
267 | ||
268 |
assets_import = AssetsImport.as_view() |
|
269 | ||
270 | ||
271 |
def assets_export(request, *args, **kwargs): |
|
272 |
fd = BytesIO() |
|
273 |
assets_file = tarfile.open('assets.tar', 'w', fileobj=fd) |
|
274 |
media_prefix = default_storage.path('') |
|
275 |
for basedir, dirnames, filenames in os.walk(media_prefix): |
|
276 |
for filename in filenames: |
|
277 |
assets_file.add( |
|
278 |
os.path.join(basedir, filename), |
|
279 |
os.path.join(basedir, filename)[len(media_prefix):]) |
|
280 |
assets_file.close() |
|
281 |
return HttpResponse(fd.getvalue(), content_type='application/x-tar') |
|
282 | ||
283 | ||
243 | 284 |
def serve_asset(request, key): |
244 | 285 |
try: |
245 | 286 |
asset = Asset.objects.get(key=key) |
tests/test_manager.py | ||
---|---|---|
2 | 2 |
import json |
3 | 3 |
import os |
4 | 4 |
import re |
5 |
import shutil |
|
5 | 6 | |
6 | 7 |
import mock |
7 | 8 | |
... | ... | |
823 | 824 |
assert '>Delete<' in resp.text |
824 | 825 |
assert Asset.objects.filter(key='collectivity:cgu').count() == 1 |
825 | 826 | |
827 | ||
828 |
def test_asset_export_import(app, admin_user): |
|
829 |
for path in ('uploads', 'assets', 'cache'): |
|
830 |
if os.path.exists(default_storage.path(path)): |
|
831 |
shutil.rmtree(default_storage.path(path)) |
|
832 | ||
833 |
app = login(app) |
|
834 | ||
835 |
# upload a file |
|
836 |
resp = app.get('/manage/assets/') |
|
837 |
resp = resp.click('Upload') |
|
838 |
resp.form['upload'] = Upload('test.png', |
|
839 |
base64.decodestring(b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg=='), |
|
840 |
'image/png') |
|
841 |
resp = resp.form.submit() |
|
842 | ||
843 |
resp = app.get('/manage/assets/') |
|
844 |
resp = resp.click('Export') |
|
845 |
assert resp.content_type == 'application/x-tar' |
|
846 |
content = resp.content |
|
847 | ||
848 |
for path in ('uploads', 'assets'): |
|
849 |
if os.path.exists(default_storage.path(path)): |
|
850 |
shutil.rmtree(default_storage.path(path)) |
|
851 | ||
852 |
resp = app.get('/manage/assets/') |
|
853 |
assert 'have any asset yet.' in resp.text |
|
854 |
resp = resp.click('Import') |
|
855 |
resp.form['assets_file'] = Upload('test.tar', content) |
|
856 |
resp = resp.form.submit() |
|
857 |
assert sum([len(x[2]) for x in os.walk(default_storage.path(''))]) == 2 |
|
858 |
resp = resp.follow() |
|
859 |
assert 'The assets file has been imported.' in resp.text |
|
860 | ||
861 |
# test no overwrite |
|
862 |
filename = re.findall('data-href="(.*?)"', resp.text)[0][7:] # strip /media/ |
|
863 |
with open(default_storage.path(filename), 'w') as fd: |
|
864 |
fd.write('test') # 4 bytes |
|
865 |
assert os.stat(default_storage.path(filename)).st_size == 4 |
|
866 | ||
867 |
resp = app.get('/manage/assets/') |
|
868 |
resp = resp.click('Import') |
|
869 |
resp.form['assets_file'] = Upload('test.tar', content) |
|
870 |
resp = resp.form.submit() |
|
871 |
resp = resp.follow() |
|
872 |
assert 'The assets file has been imported.' in resp.text |
|
873 | ||
874 |
assert os.stat(default_storage.path(filename)).st_size == 4 |
|
875 | ||
876 |
# test overwrite |
|
877 |
resp = app.get('/manage/assets/') |
|
878 |
resp = resp.click('Import') |
|
879 |
resp.form['overwrite'] = True |
|
880 |
resp.form['assets_file'] = Upload('test.tar', content) |
|
881 |
resp = resp.form.submit() |
|
882 |
resp = resp.follow() |
|
883 |
assert 'The assets file has been imported.' in resp.text |
|
884 | ||
885 |
assert os.stat(default_storage.path(filename)).st_size == 67 |
|
886 | ||
887 |
# test uploading garbage |
|
888 |
for path in ('uploads', 'assets'): |
|
889 |
if os.path.exists(default_storage.path(path)): |
|
890 |
shutil.rmtree(default_storage.path(path)) |
|
891 | ||
892 |
resp = app.get('/manage/assets/') |
|
893 |
resp = resp.click('Import') |
|
894 |
resp.form['assets_file'] = Upload('test.tar', b'garbage') |
|
895 |
resp = resp.form.submit() |
|
896 |
assert sum([len(x[2]) for x in os.walk(default_storage.path(''))]) == 0 |
|
897 |
resp = resp.follow() |
|
898 |
assert 'The assets file is not valid.' in resp.text |
|
899 | ||
900 | ||
826 | 901 |
def test_menu_json(app, admin_user): |
827 | 902 |
app.get('/manage/menu.json', status=302) |
828 | 903 | |
829 |
- |