Projet

Général

Profil

0001-assets-add-basic-export-import-of-assets-in-tar-file.patch

Frédéric Péters, 02 mai 2019 12:41

Télécharger (9,39 ko)

Voir les différences:

Subject: [PATCH] assets: add basic export/import of assets in tar file (#6756)

 combo/apps/assets/forms.py                    |  5 ++
 .../templates/combo/manager_assets.html       |  5 ++
 .../combo/manager_assets_import.html          | 18 +++++
 combo/apps/assets/urls.py                     |  2 +
 combo/apps/assets/views.py                    | 45 ++++++++++-
 tests/test_manager.py                         | 75 +++++++++++++++++++
 6 files changed, 148 insertions(+), 2 deletions(-)
 create mode 100644 combo/apps/assets/templates/combo/manager_assets_import.html
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
-