From 5ddd7251bc24a5e0a0c339a6b31cc535f42da34e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Tue, 10 Mar 2020 16:39:04 +0100
Subject: [PATCH 3/3] assets: display assets related to cell in popup (#40223)

---
 .../templates/combo/manager_assets.html       | 36 +------------
 .../combo/manager_assets_fragment.html        | 35 ++++++++++++
 .../templates/combo/manager_slot_assets.html  | 19 +++++++
 combo/apps/assets/urls.py                     |  3 +-
 combo/apps/assets/views.py                    | 54 ++++++++++++++++++-
 combo/manager/static/css/combo.manager.css    | 16 +++---
 .../combo/manager_edit_cell_block.html        |  1 +
 tests/test_manager.py                         | 51 ++++++++++++++++++
 8 files changed, 170 insertions(+), 45 deletions(-)
 create mode 100644 combo/apps/assets/templates/combo/manager_assets_fragment.html
 create mode 100644 combo/apps/assets/templates/combo/manager_slot_assets.html

diff --git a/combo/apps/assets/templates/combo/manager_assets.html b/combo/apps/assets/templates/combo/manager_assets.html
index 1c8d9de..fa80f1f 100644
--- a/combo/apps/assets/templates/combo/manager_assets.html
+++ b/combo/apps/assets/templates/combo/manager_assets.html
@@ -38,41 +38,7 @@
 
 <div id="assets-browser">
   <div id="assets-listing">
-  <table class="main">
-    <thead>
-      <tr>
-        <th>{% trans "Name" %}</th>
-        <th>{% trans "Size" %}</th>
-        <th></th>
-        <th></th>
-      </tr>
-    </thead>
-    <tbody>
-      {% for asset in object_list %}
-      <tr class="{{ asset.css_classes }}">
-        <td><a href="{{ asset.src }}">{{ asset.name }}</a></td>
-        <td>{% if asset.size %}{{ asset.size|filesizeformat }}{% else %}-{% endif %}</td>
-        <td class="image">{% if asset.is_image %}<img data-href="{{ asset.src }}" src="{{ asset.thumb }}"/>{% endif %}</td>
-        <td class="actions">
-          {% if asset.key %}{# theme asset #}
-          <a href="{% url 'combo-manager-slot-asset-upload' key=asset.key %}"
-             class="overwrite" rel="popup">{% trans 'Overwrite' %}</a>
-          {% if asset.asset %}
-          <a href="{% url 'combo-manager-slot-asset-delete' key=asset.key %}"
-             class="delete" rel="popup">{% trans 'Delete' %}</a>
-          {% endif %}
-          {% else %}
-          <a href="{% url 'combo-manager-asset-overwrite' %}?img={{asset.filepath|iriencode}}"
-             class="overwrite" rel="popup">{% trans 'Overwrite' %}</a>
-          <a href="{% url 'combo-manager-asset-delete' %}?img={{asset.filepath|iriencode}}"
-             class="delete" rel="popup">{% trans 'Delete' %}</a>
-          {% endif %}
-        </td>
-        </td>
-      </tr>
-      {% endfor %}
-    </tbody>
-  </table>
+    {% include "combo/manager_assets_fragment.html" %}
   </div>
   <div id="asset-preview"></div>
 </div>
diff --git a/combo/apps/assets/templates/combo/manager_assets_fragment.html b/combo/apps/assets/templates/combo/manager_assets_fragment.html
new file mode 100644
index 0000000..ccb9661
--- /dev/null
+++ b/combo/apps/assets/templates/combo/manager_assets_fragment.html
@@ -0,0 +1,35 @@
+{% load i18n %}
+<table class="main">
+  <thead>
+    <tr>
+      <th>{% trans "Name" %}</th>
+      <th>{% trans "Size" %}</th>
+      <th></th>
+      <th></th>
+    </tr>
+  </thead>
+  <tbody>
+    {% for asset in object_list %}
+    <tr class="{{ asset.css_classes }}">
+      <td><a href="{{ asset.src }}">{{ asset.name }}</a></td>
+      <td>{% if asset.size %}{{ asset.size|filesizeformat }}{% else %}-{% endif %}</td>
+      <td class="image">{% if asset.is_image %}<img data-href="{{ asset.src }}" src="{{ asset.thumb }}"/>{% endif %}</td>
+      <td class="actions">
+        {% if asset.key %}{# theme asset #}
+        <a href="{% url 'combo-manager-slot-asset-upload' key=asset.key %}{% if cell_reference %}?cell_reference={{ cell_reference }}{% endif %}"
+           class="overwrite" rel="popup">{% trans 'Overwrite' %}</a>
+        {% if asset.asset %}
+        <a href="{% url 'combo-manager-slot-asset-delete' key=asset.key %}{% if cell_reference %}?cell_reference={{ cell_reference }}{% endif %}"
+           class="delete" rel="popup">{% trans 'Delete' %}</a>
+        {% endif %}
+        {% else %}
+        <a href="{% url 'combo-manager-asset-overwrite' %}?img={{asset.filepath|iriencode}}"
+           class="overwrite" rel="popup">{% trans 'Overwrite' %}</a>
+        <a href="{% url 'combo-manager-asset-delete' %}?img={{asset.filepath|iriencode}}"
+           class="delete" rel="popup">{% trans 'Delete' %}</a>
+        {% endif %}
+      </td>
+    </tr>
+    {% endfor %}
+  </tbody>
+</table>
diff --git a/combo/apps/assets/templates/combo/manager_slot_assets.html b/combo/apps/assets/templates/combo/manager_slot_assets.html
new file mode 100644
index 0000000..6905430
--- /dev/null
+++ b/combo/apps/assets/templates/combo/manager_slot_assets.html
@@ -0,0 +1,19 @@
+{% extends "combo/manager_base.html" %}
+{% load i18n %}
+
+{% block appbar %}
+<h2>{% trans 'Assets' %}</h2>
+{% endblock %}
+
+{% block breadcrumb %}
+{{ block.super }}
+<a href="{% url 'combo-manager-assets' %}">{% trans 'Assets' %}</a>
+{% endblock %}
+
+{% block content %}
+
+<div id="assets-listing">
+{% include "combo/manager_assets_fragment.html" %}
+</div>
+
+{% endblock %}
diff --git a/combo/apps/assets/urls.py b/combo/apps/assets/urls.py
index ebdb849..1f94789 100644
--- a/combo/apps/assets/urls.py
+++ b/combo/apps/assets/urls.py
@@ -23,6 +23,7 @@ from . import api_views
 
 assets_manager_urls = [
     url(r'^$', views.assets, name='combo-manager-assets'),
+    url(r'^slots/(?P<cell_reference>[\w_-]+)/$', views.slot_assets, name='combo-manager-slot-assets'),
     url(r'^delete$', views.asset_delete, name='combo-manager-asset-delete'),
     url(r'^overwrite/$', views.asset_overwrite, name='combo-manager-asset-overwrite'),
     url(r'^upload/$', views.asset_upload, name='combo-manager-asset-upload'),
@@ -37,6 +38,6 @@ urlpatterns = [
     url(r'^manage/assets/', decorated_includes(manager_required,
         include(assets_manager_urls))),
 
-    url('^api/assets/set/(?P<key>[\w_:-]+)/$', api_views.view_set,
+    url(r'^api/assets/set/(?P<key>[\w_:-]+)/$', api_views.view_set,
         name='api-assets-set'),
 ]
diff --git a/combo/apps/assets/views.py b/combo/apps/assets/views.py
index f9005b5..d7b0432 100644
--- a/combo/apps/assets/views.py
+++ b/combo/apps/assets/views.py
@@ -20,7 +20,7 @@ import os
 
 from django.conf import settings
 from django.contrib import messages
-from django.core.exceptions import PermissionDenied
+from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
 from django.core.files.storage import default_storage
 from django.core.urlresolvers import reverse, reverse_lazy
 from django.http import Http404, HttpResponse
@@ -230,9 +230,41 @@ class AssetDelete(TemplateView):
                 Assets(request=self.request).get_anchored_url(
                     name=os.path.basename(img_orig)))
 
+
 asset_delete = AssetDelete.as_view()
 
 
+class SlotAssets(ListView):
+    template_name = 'combo/manager_slot_assets.html'
+
+    def get_assets(self, cell):
+        asset_slots = cell.get_asset_slots()
+        assets = dict([(x.key, x) for x in Asset.objects.filter(key__in=asset_slots.keys())])
+        for key, value in asset_slots.items():
+            yield SlotAsset(
+                key,
+                name=value.get('label'),
+                asset_type=value.get('asset-type', 'image'),
+                asset=assets.get(key))
+
+    def get_queryset(self):
+        cell_reference = self.kwargs['cell_reference']
+        try:
+            cell = CellBase.get_cell(cell_reference)
+        except ObjectDoesNotExist:
+            raise Http404()
+
+        return self.get_assets(cell)
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        context['cell_reference'] = self.kwargs['cell_reference']
+        return context
+
+
+slot_assets = SlotAssets.as_view()
+
+
 class SlotAssetUpload(FormView):
     form_class = AssetUploadForm
     template_name = 'combo/manager_asset_upload.html'
@@ -248,8 +280,18 @@ class SlotAssetUpload(FormView):
         return super(SlotAssetUpload, self).form_valid(form)
 
     def get_success_url(self):
+        if self.request.GET.get('cell_reference'):
+            cell_reference = self.request.GET['cell_reference']
+            try:
+                cell = CellBase.get_cell(cell_reference)
+            except ObjectDoesNotExist:
+                pass
+            else:
+                return reverse('combo-manager-page-view', kwargs={'pk': cell.page_id}) + '#cell-' + cell_reference
+
         return Assets(request=self.request).get_anchored_url(key=self.kwargs['key'])
 
+
 slot_asset_upload = SlotAssetUpload.as_view()
 
 
@@ -258,8 +300,18 @@ class SlotAssetDelete(TemplateView):
 
     def post(self, request, *args, **kwargs):
         Asset.objects.filter(key=kwargs['key']).delete()
+        if self.request.GET.get('cell_reference'):
+            cell_reference = self.request.GET['cell_reference']
+            try:
+                cell = CellBase.get_cell(cell_reference)
+            except ObjectDoesNotExist:
+                pass
+            else:
+                return redirect(
+                    reverse('combo-manager-page-view', kwargs={'pk': cell.page_id}) + '#cell-' + cell_reference)
         return redirect(Assets(request=self.request).get_anchored_url(key=kwargs['key']))
 
+
 slot_asset_delete = SlotAssetDelete.as_view()
 
 
diff --git a/combo/manager/static/css/combo.manager.css b/combo/manager/static/css/combo.manager.css
index 5a31fd0..57b48d9 100644
--- a/combo/manager/static/css/combo.manager.css
+++ b/combo/manager/static/css/combo.manager.css
@@ -258,23 +258,23 @@ div.navigation li.nav-up::before {
 	width: 80%;
 }
 
-#assets-browser #assets-listing table th,
-#assets-browser #assets-listing table td {
+#assets-listing table th,
+#assets-listing table td {
 	text-align: left;
 	padding-left: 1ex;
 }
 
-#assets-browser #assets-listing table td.image {
+#assets-listing table td.image {
 	padding: 0;
 	text-align: center;
 }
 
-#assets-browser #assets-listing table td.actions {
+#assets-listing table td.actions {
 	width: 80px;
 }
 
-#assets-browser #assets-listing table td.actions a.overwrite,
-#assets-browser #assets-listing table td.actions a.delete {
+#assets-listing table td.actions a.overwrite,
+#assets-listing table td.actions a.delete {
 	display: inline-block;
 	border: none;
 	overflow: hidden;
@@ -283,7 +283,7 @@ div.navigation li.nav-up::before {
 	line-height: 30px;
 }
 
-#assets-browser #assets-listing table td.actions a.delete::before {
+#assets-listing table td.actions a.delete::before {
 	text-align: center;
 	font-family: FontAwesome;
 	content: "\f057"; /* remove-sign */
@@ -291,7 +291,7 @@ div.navigation li.nav-up::before {
 	width: 30px;
 }
 
-#assets-browser #assets-listing table td.actions a.overwrite::before {
+#assets-listing table td.actions a.overwrite::before {
 	text-align: center;
 	font-family: FontAwesome;
 	content: "\f093"; /* upload */
diff --git a/combo/manager/templates/combo/manager_edit_cell_block.html b/combo/manager/templates/combo/manager_edit_cell_block.html
index 337d7a3..4b716a3 100644
--- a/combo/manager/templates/combo/manager_edit_cell_block.html
+++ b/combo/manager/templates/combo/manager_edit_cell_block.html
@@ -17,6 +17,7 @@
 <a rel="popup" href="{% url 'combo-manager-page-delete-cell' page_pk=page.id cell_reference=cell.get_reference %}">{% trans 'Delete' %}</a> |
 <a rel="popup" href="{% url 'combo-manager-page-visibility-cell' page_pk=page.id cell_reference=cell.get_reference %}">{% trans 'Visibility' %}</a> |
 <a rel="popup" href="{% url 'combo-manager-page-options-cell' page_pk=page.id cell_reference=cell.get_reference %}">{% trans 'Options' %}</a> |
+{% if cell.can_have_assets %}<a rel="popup" data-selector="div#assets-listing" href="{% url 'combo-manager-slot-assets' cell_reference=cell.get_reference %}">{% trans 'Assets' %}</a> |{% endif %}
 <a class="close-button" href="#">{% trans 'Close' %}</a>
 {% if form %}
 <button class="save submit-button">{% trans 'Save' %}</button>
diff --git a/tests/test_manager.py b/tests/test_manager.py
index 0a76ba0..a09ae27 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -1136,6 +1136,7 @@ def test_asset_management_search(app, admin_user):
     resp = resp.form.submit()
     assert resp.text.count('<tr class="asset') == 2
 
+
 def test_asset_slots_management(app, admin_user):
     app = login(app)
     assert Asset.objects.count() == 0
@@ -1198,6 +1199,56 @@ def test_asset_slots_management(app, admin_user):
         assert '>CGU<' in resp.text
 
 
+def test_asset_slots_for_cells_management(settings, app, admin_user):
+    app = login(app)
+    assert Asset.objects.count() == 0
+
+    settings.COMBO_CELL_ASSET_SLOTS = {'data_textcell': {'picture': {'prefix': 'Picture'}}}
+    page = Page.objects.create(title='xxx', slug='test_cell_assets', template_name='standard')
+    cell = TextCell.objects.create(page=page, order=0, slug='foo')
+
+    resp = app.get('/manage/assets/slots/%s/' % cell.get_reference())
+    assert '>Overwrite<' in resp.text
+    assert '>Delete<' not in resp.text
+
+    resp = resp.click('Overwrite')
+    resp.form['upload'] = Upload(
+        'test.png',
+        base64.decodebytes(b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAgAABAADRWoApgAA\nAABJRU5ErkJggg=='),
+        'image/png')
+    resp = resp.form.submit()
+    assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
+    assert Asset.objects.filter(key=cell.get_asset_slot_key('picture')).count() == 1
+
+    assert app.get('/assets/%s' % cell.get_asset_slot_key('picture'), status=302)['location'].endswith('test.png')
+
+    # upload a new version of image
+    resp = app.get('/manage/assets/slots/%s/' % cell.get_reference())
+    resp = resp.click('Overwrite')
+    resp.form['upload'] = Upload(
+        'test2.png',
+        base64.decodebytes(b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAgAABAADRWoApgAA\nAABJRU5ErkJggg=='),
+        'image/png')
+    resp = resp.form.submit()
+    resp = app.get('/manage/assets/slots/%s/' % cell.get_reference())
+    assert 'test2.png' in resp.text
+    assert '<img data-href' in resp.text  # thumbnail
+    assert '>Delete<' in resp.text
+    assert Asset.objects.filter(key=cell.get_asset_slot_key('picture')).count() == 1
+
+    # delete the image
+    resp = app.get('/manage/assets/slots/%s/' % cell.get_reference())
+    resp = resp.click('Delete')
+    resp = resp.form.submit()
+    assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
+    resp = app.get('/manage/assets/slots/%s/' % cell.get_reference())
+    assert '>Overwrite<' in resp.text
+    assert '>Delete<' not in resp.text
+    assert Asset.objects.filter(key=cell.get_asset_slot_key('picture')).count() == 0
+
+    assert app.get('/assets/%s' % cell.get_asset_slot_key('picture'), status=404)
+
+
 def test_asset_slots_export(app, admin_user):
     app = login(app)
 
-- 
2.20.1

