From 53e97c704ab1bb73fd88ec0fef4c5c414ce2a5e9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Thu, 23 Jan 2020 16:18:51 +0100
Subject: [PATCH] placeholder: optional placeholders display and edition
 (#37135)

---
 combo/data/models.py                          |  5 +-
 combo/manager/templates/combo/page_view.html  | 14 ++++++
 combo/manager/views.py                        | 32 ++++++++++++-
 .../public/templates/combo/page_template.html |  2 +-
 tests/test_manager.py                         | 48 ++++++++++++++++++-
 5 files changed, 96 insertions(+), 5 deletions(-)

diff --git a/combo/data/models.py b/combo/data/models.py
index d4f7093..22e83dc 100644
--- a/combo/data/models.py
+++ b/combo/data/models.py
@@ -84,11 +84,12 @@ def element_is_visible(element, user=None):
 
 
 class Placeholder(object):
-    def __init__(self, key, name=None, acquired=False, render=True, cell=None,
-                 force_synchronous=False):
+    def __init__(self, key, name=None, acquired=False, optional=False,
+                 render=True, cell=None, force_synchronous=False):
         self.key = key
         self.name = name
         self.acquired = acquired
+        self.optional = optional
         self.render = render
         self.cell = cell
         self.force_synchronous = force_synchronous
diff --git a/combo/manager/templates/combo/page_view.html b/combo/manager/templates/combo/page_view.html
index 7623210..81d8bce 100644
--- a/combo/manager/templates/combo/page_view.html
+++ b/combo/manager/templates/combo/page_view.html
@@ -106,6 +106,20 @@
 </div>
 {% endif %}
 
+{% if optional_placeholders %}
+<div class="page-options">
+<h3>{% trans 'Optional sections' %}</h3>
+<ul>
+{% for placeholder in optional_placeholders %}
+  <li>
+    {{ placeholder.name }} ({% if placeholder.is_empty %}{% trans "empty" %}{% else %}{% trans "like parent" %}{% endif %})
+    <a href="{% url 'combo-manager-page-view' pk=object.id %}?include-section={{ placeholder.key }}">{% trans 'edit' %}</a>
+  </li>
+{% endfor %}
+</ul>
+</div>
+{% endif %}
+
 </aside>
 {% endblock %} {# sidebar #}
 
diff --git a/combo/manager/views.py b/combo/manager/views.py
index 5992fa2..f846c27 100644
--- a/combo/manager/views.py
+++ b/combo/manager/views.py
@@ -124,7 +124,6 @@ class PageAddChildView(PageAddView):
         return super(CreateView, self).get_initial()
 
     def dispatch(self, request, *args, **kwargs):
-        print(request.POST)
         self.parent = get_object_or_404(Page, pk=kwargs['pk'])
         return super(PageAddChildView, self).dispatch(request, *args, **kwargs)
 
@@ -265,6 +264,7 @@ class PageView(DetailView):
         cells = CellBase.get_cells(page_id=self.object.id)
         template = self.object.template_name
         placeholders = []
+        optional_placeholders = []
         combo_template = settings.COMBO_PUBLIC_TEMPLATES.get(template)
 
         for placeholder in self.object.get_placeholders(request=self.request, traverse_cells=True):
@@ -273,9 +273,39 @@ class PageView(DetailView):
                 'name': placeholder.get_name(),
             }
             placeholder_dict['cells'] = [x for x in cells if x.placeholder == placeholder.key]
+
+            # not optional placeholder
+            if not placeholder.optional:
+                placeholders.append(placeholder_dict)
+                continue
+
+            # optional placeholder, but requested for edition
+            if self.request.GET.get('include-section') == placeholder.key:
+                placeholders.append(placeholder_dict)
+                continue
+
+            # empty optional placeholder: in optional sections
+            if not placeholder_dict['cells']:
+                placeholder_dict['is_empty'] = True
+                optional_placeholders.append(placeholder_dict)
+                continue
+
+            # optional placeholder with at least 2 cells: in page edition
+            if len(placeholder_dict['cells']) > 1:
+                placeholders.append(placeholder_dict)
+                continue
+
+            # optional placeholder with only one ParentContentCell: in optional sections
+            if isinstance(placeholder_dict['cells'][0], ParentContentCell):
+                placeholder_dict['is_empty'] = False
+                optional_placeholders.append(placeholder_dict)
+                continue
+
+            # other cases: in page edition
             placeholders.append(placeholder_dict)
 
         context['placeholders'] = placeholders
+        context['optional_placeholders'] = optional_placeholders
 
         context.update({
             'previous_page': self.object.get_previous_page(check_visibility=False),
diff --git a/combo/public/templates/combo/page_template.html b/combo/public/templates/combo/page_template.html
index 5d830ad..f3c481e 100644
--- a/combo/public/templates/combo/page_template.html
+++ b/combo/public/templates/combo/page_template.html
@@ -38,7 +38,7 @@
  <div id="footer">
   {% block footer %}
     {% trans "Footer" as name %}
-    {% placeholder "footer" name=name acquired=True %}
+    {% placeholder "footer" name=name acquired=True optional=True %}
     {% skeleton_extra_placeholder footer %}
     <span style="display: none">{% now "Y-m-d H:i:s" %}</span> {# generation time #}
     {% end_skeleton_extra_placeholder %}
diff --git a/tests/test_manager.py b/tests/test_manager.py
index 0b9b7ae..c391c3a 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -21,7 +21,7 @@ from webtest import TestApp
 from webtest import Upload
 
 from combo.wsgi import application
-from combo.data.models import Page, CellBase, TextCell, LinkCell, ConfigJsonCell, JsonCell, PageSnapshot
+from combo.data.models import Page, CellBase, TextCell, LinkCell, ConfigJsonCell, JsonCell, PageSnapshot, ParentContentCell
 from combo.apps.assets.models import Asset
 from combo.apps.family.models import FamilyInfosCell
 from combo.apps.search.models import SearchCell
@@ -139,6 +139,52 @@ def test_edit_page(app, admin_user):
     resp = resp.follow()
     assert Page.objects.all()[0].exclude_from_navigation is True
 
+
+def test_edit_page_optional_placeholder(app, admin_user):
+    Page.objects.all().delete()
+    page = Page.objects.create(title='One', slug='one', template_name='standard')
+    app = login(app)
+
+    # empty placeholder footer
+    resp = app.get('/manage/pages/%s/' % page.pk)
+    assert len(resp.context['optional_placeholders']) == 1
+    assert resp.context['optional_placeholders'][0]['key'] == 'footer'
+    assert resp.context['optional_placeholders'][0]['name'] == 'Footer'
+    assert resp.context['optional_placeholders'][0]['is_empty'] is True
+    assert '/manage/pages/%s/?include-section=footer' % page.pk in resp.text
+    assert 'Footer (empty)' in resp.text
+    assert '<div class="placeholder" data-placeholder-key="footer">' not in resp.text
+
+    # add a ParentContentCell
+    like_parent_cell = ParentContentCell.objects.create(page=page, placeholder='footer', order=0)
+    resp = app.get('/manage/pages/%s/' % page.pk)
+    assert len(resp.context['optional_placeholders']) == 1
+    assert resp.context['optional_placeholders'][0]['key'] == 'footer'
+    assert resp.context['optional_placeholders'][0]['name'] == 'Footer'
+    assert resp.context['optional_placeholders'][0]['is_empty'] is False
+    assert '/manage/pages/%s/?include-section=footer' % page.pk in resp.text
+    assert 'Footer (like parent)' in resp.text
+    assert '<div class="placeholder" data-placeholder-key="footer">' not in resp.text
+
+    # click on the edit link
+    resp = resp.click(href='.*?include-section=footer')
+    assert len(resp.context['optional_placeholders']) == 0
+    assert '<div class="placeholder" data-placeholder-key="footer">' in resp.text
+    assert '<option data-add-url="/manage/pages/%s/add-cell-to-footer/data_textcell/default/"' % page.pk in resp.text
+
+    # add a cell (so there is 2 cells)
+    resp = app.get('/manage/pages/%s/add-cell-to-footer/data_textcell/default/' % page.pk)
+    resp = app.get('/manage/pages/%s/' % page.pk)
+    assert len(resp.context['optional_placeholders']) == 0
+    assert '<div class="placeholder" data-placeholder-key="footer">' in resp.text
+
+    # remove parent content cell
+    like_parent_cell.delete()
+    resp = app.get('/manage/pages/%s/' % page.pk)
+    assert len(resp.context['optional_placeholders']) == 0
+    assert '<div class="placeholder" data-placeholder-key="footer">' in resp.text
+
+
 def test_edit_page_unknown_template(app, admin_user):
     Page.objects.all().delete()
     page = Page(title='One', slug='one', template_name='broken')
-- 
2.20.1

