From 123eecf2578699b42c16d67baba3510e2dd9777b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Fri, 7 Jan 2022 11:13:19 +0100
Subject: [PATCH 5/5] wcs: add link entry in card custom schema (#60370)

---
 combo/apps/wcs/models.py                      | 35 ++++++++---
 combo/apps/wcs/templates/combo/wcs/card.html  |  4 ++
 .../wcs/manager/card-infos-cell-form.html     | 22 +++++++
 combo/manager/static/js/combo.manager.js      | 42 +++++++++++++-
 tests/test_wcs.py                             | 58 +++++++++++++++++++
 5 files changed, 151 insertions(+), 10 deletions(-)

diff --git a/combo/apps/wcs/models.py b/combo/apps/wcs/models.py
index 940dcd03..2bdf8a25 100644
--- a/combo/apps/wcs/models.py
+++ b/combo/apps/wcs/models.py
@@ -1058,19 +1058,40 @@ class WcsCardInfosCell(CardMixin, CellBase):
             if self.title_type == 'auto' or self.title_type == 'manual' and not extra_context['title']:
                 extra_context['title'] = '%s - %s' % (self.cached_title, extra_context['card'].get('text'))
 
+            def render_template(item, template_key, template_context, target_key, target_context):
+                if not item.get(template_key):
+                    return
+                try:
+                    target_context['card'][target_key][item[template_key]] = Template(
+                        item[template_key]
+                    ).render(template_context)
+                except (VariableDoesNotExist, TemplateSyntaxError):
+                    return
+
             extra_context['card']['custom_fields'] = {}
+            extra_context['card']['urls'] = {}
             if self.custom_schema:
                 for item in self.custom_schema.get('cells') or []:
-                    if item.get('varname') != '@custom@':
+                    if item.get('varname') not in ['@custom@', '@link@']:
                         continue
                     if not item.get('template'):
                         continue
-                    try:
-                        extra_context['card']['custom_fields'][item['template']] = Template(
-                            item['template']
-                        ).render(custom_context)
-                    except (VariableDoesNotExist, TemplateSyntaxError):
-                        pass
+                    render_template(
+                        item=item,
+                        template_key='template',
+                        template_context=custom_context,
+                        target_key='custom_fields',
+                        target_context=extra_context,
+                    )
+
+                    if item['varname'] == '@link@':
+                        render_template(
+                            item=item,
+                            template_key='url_template',
+                            template_context=custom_context,
+                            target_key='urls',
+                            target_context=extra_context,
+                        )
 
         return extra_context
 
diff --git a/combo/apps/wcs/templates/combo/wcs/card.html b/combo/apps/wcs/templates/combo/wcs/card.html
index f3b7bd32..903e46f2 100644
--- a/combo/apps/wcs/templates/combo/wcs/card.html
+++ b/combo/apps/wcs/templates/combo/wcs/card.html
@@ -28,6 +28,10 @@
             <div class="value">{{ value }}</div>
           {% endif %}
           {% endwith %}
+        {% elif item.varname == "@link@" and item.url_template and item.template %}
+          {% with card.custom_fields|get:item.template|force_escape as link_label and card.urls|get:item.url_template|force_escape as link_url %}
+          <div class="value"><a href="{{ link_url }}"{% if item.display_mode == 'button' %} class="pk-button"{% endif %}>{{ link_label }}</a></div>
+          {% endwith %}
         {% else %}
           {% with fields_by_varnames|get:item.varname as field %}
           {% with card.fields|get:item.varname as value %}
diff --git a/combo/apps/wcs/templates/combo/wcs/manager/card-infos-cell-form.html b/combo/apps/wcs/templates/combo/wcs/manager/card-infos-cell-form.html
index aa9f5bb9..6f53651e 100644
--- a/combo/apps/wcs/templates/combo/wcs/manager/card-infos-cell-form.html
+++ b/combo/apps/wcs/templates/combo/wcs/manager/card-infos-cell-form.html
@@ -44,6 +44,7 @@
         <select name="entry_type" data-dynamic-display-parent="true">
           <option value="@field@">{% trans "Card field" %}</option>
           <option value="@custom@">{% trans "Custom" %}</option>
+          <option value="@link@">{% trans "Link" %}</option>
         </select>
       </label>
     </p>
@@ -90,6 +91,27 @@
         </select>
       </label>
     </p>
+    <p>
+      <label>
+        {% trans "Label template" %}
+        <textarea name="link_label_template" data-dynamic-display-child-of="entry_type" data-dynamic-display-value="@link@" style="resize: vertical;"></textarea>
+      </label>
+    </p>
+    <p>
+      <label>
+        {% trans "URL template" %}
+        <textarea name="link_url_template" data-dynamic-display-child-of="entry_type" data-dynamic-display-value="@link@" style="resize: vertical;"></textarea>
+      </label>
+    </p>
+    <p>
+      <label>
+        {% trans "Display mode" %}
+        <select name="link_display_mode" data-dynamic-display-child-of="entry_type" data-dynamic-display-value="@link@">
+          <option value="link">{% trans "Link" %}</option>
+          <option value="button">{% trans "Button" %}</option>
+        </select>
+      </label>
+    </p>
     <p>
       <label>
         {% trans "Size" %}
diff --git a/combo/manager/static/js/combo.manager.js b/combo/manager/static/js/combo.manager.js
index 2e6de3d7..99bb94d5 100644
--- a/combo/manager/static/js/combo.manager.js
+++ b/combo/manager/static/js/combo.manager.js
@@ -495,7 +495,10 @@ Card_cell_custom.prototype = {
     this.grid_cell_form.field_display_mode = this.grid_cell_form[3];
     this.grid_cell_form.custom_template = this.grid_cell_form[4];
     this.grid_cell_form.custom_display_mode = this.grid_cell_form[5];
-    this.grid_cell_form.size = this.grid_cell_form[6];
+    this.grid_cell_form.link_label_template = this.grid_cell_form[6];
+    this.grid_cell_form.link_url_template = this.grid_cell_form[7];
+    this.grid_cell_form.link_display_mode = this.grid_cell_form[8];
+    this.grid_cell_form.size = this.grid_cell_form[9];
     const varname_select = this.grid_cell_form.field_varname;
     this.cardSchema.fields.forEach(function(el, id) {
       if (el.varname) {
@@ -567,13 +570,22 @@ Card_cell_custom.prototype = {
     // set cell text
     let schema_field = _self.field_with_varname(schema_cell.varname);
     let cell_text = "";
-    if (schema_field || schema_cell.varname == '@custom@') {
-      const cell_content = schema_cell.varname == '@custom@' ? (schema_cell.template || '') + ' (' + gettext('Custom') + ')' : schema_field.label;
+    if (schema_field || schema_cell.varname == '@custom@' || schema_cell.varname == '@link@') {
+      let cell_content = "";
+      if (schema_cell.varname == '@custom@') {
+        cell_content = (schema_cell.template || '') + ' (' + gettext('Custom') + ')';
+      } else if (schema_cell.varname == '@link@') {
+        cell_content = (schema_cell.template || '') + ': ' + (schema_cell.url_template || '') + ' (' + gettext('Link') + ')';
+      } else {
+        cell_content = schema_field.label;
+      }
       cell_text += $('<span/>').addClass(schema_cell.display_mode).text(cell_content).html();
       cell_text += '<span class="cell-meta">';
       let cell_display_mode_label = '';
       if (schema_cell.varname == '@custom@') {
         cell_display_mode_label = $(this.grid_cell_form).find('select[name="custom_display_mode"] option[value="' + schema_cell.display_mode + '"]').text();
+      } else if (schema_cell.varname == '@link@') {
+        cell_display_mode_label = $(this.grid_cell_form).find('select[name="link_display_mode"] option[value="' + schema_cell.display_mode + '"]').text();
       } else {
         cell_display_mode_label = $(this.grid_cell_form).find('select[name="field_content"] option[value="' + schema_cell.field_content + '"]').text();
         if (schema_cell.field_content != 'label-and-value') {
@@ -615,6 +627,19 @@ Card_cell_custom.prototype = {
       this.grid_cell_form.field_display_mode.value = '';
       this.grid_cell_form.custom_template.value = grid_cell.dataset.template || '';
       this.grid_cell_form.custom_display_mode.value = grid_cell.dataset.display_mode;
+      this.grid_cell_form.link_label_template.value = '';
+      this.grid_cell_form.link_url_template.value = '';
+      this.grid_cell_form.link_display_mode.value = '';
+    } else if (grid_cell.dataset.varname == '@link@') {
+      this.grid_cell_form.entry_type.value = '@link@';
+      this.grid_cell_form.field_varname.value = '';
+      this.grid_cell_form.field_content.value = '';
+      this.grid_cell_form.field_display_mode.value = '';
+      this.grid_cell_form.custom_template.value = '';
+      this.grid_cell_form.custom_display_mode.value = '';
+      this.grid_cell_form.link_label_template.value = grid_cell.dataset.template || '';
+      this.grid_cell_form.link_url_template.value = grid_cell.dataset.url_template || '';
+      this.grid_cell_form.link_display_mode.value = grid_cell.dataset.display_mode;
     } else {
       this.grid_cell_form.entry_type.value = '@field@';
       this.grid_cell_form.field_varname.value = grid_cell.dataset.varname;
@@ -622,6 +647,9 @@ Card_cell_custom.prototype = {
       this.grid_cell_form.field_display_mode.value = grid_cell.dataset.display_mode;
       this.grid_cell_form.custom_template.value = '';
       this.grid_cell_form.custom_display_mode.value = '';
+      this.grid_cell_form.link_label_template.value = '';
+      this.grid_cell_form.link_url_template.value = '';
+      this.grid_cell_form.link_display_mode.value = '';
     }
     this.grid_cell_form.size.value = grid_cell.dataset.cell_size;
     const cell_id = $(grid_cell).index();
@@ -638,11 +666,19 @@ Card_cell_custom.prototype = {
       delete schema_cell.field_content;
       schema_cell.display_mode = form_datas.custom_display_mode;
       schema_cell.template = form_datas.custom_template;
+      delete schema_cell.url_template;
+    } else if (form_datas.entry_type == '@link@') {
+      schema_cell.varname = '@link@';
+      delete schema_cell.field_content;
+      schema_cell.display_mode = form_datas.link_display_mode;
+      schema_cell.template = form_datas.link_label_template;
+      schema_cell.url_template = form_datas.link_url_template;
     } else {
       schema_cell.varname = form_datas.field_varname;
       schema_cell.field_content = form_datas.field_content;
       schema_cell.display_mode = form_datas.field_display_mode;
       delete schema_cell.template;
+      delete schema_cell.url_template;
     }
     schema_cell.cell_size = form_datas.cell_size;
     return schema_cell
diff --git a/tests/test_wcs.py b/tests/test_wcs.py
index 62cd4221..2840ab74 100644
--- a/tests/test_wcs.py
+++ b/tests/test_wcs.py
@@ -2299,6 +2299,64 @@ def test_card_cell_render_custom_schema_custom_entry(mock_send, context, app):
         assert '<p class="label">Foo bar baz X%sY</p>' % i in cell_resp
 
 
+@mock.patch('combo.apps.wcs.utils.requests.send', side_effect=mocked_requests_send)
+def test_card_cell_render_custom_schema_link_entry(mock_send, context, app):
+    page = Page.objects.create(title='xxx', template_name='standard')
+    cell = WcsCardInfosCell.objects.create(
+        page=page,
+        placeholder='content',
+        order=0,
+        carddef_reference='default:card_model_1',
+        custom_schema={
+            'cells': [
+                {
+                    'varname': '@link@',
+                    'url_template': '/foo/bar/{{ card.fields.related_structured.id }}/',
+                    'template': '{{ card.fields.fielda }} - {{ card.fields.related }}',
+                    'display_mode': 'link',
+                },
+            ]
+        },
+    )
+
+    context['card_model_1_id'] = 11
+    request = RequestFactory().get('/')
+    cell.modify_global_context(context, request)
+    cell.repeat_index = 0
+    context['synchronous'] = True  # to get fresh content
+
+    result = cell.render(context)
+    assert PyQuery(result).find('div.value a').text() == 'a - Foo Bar'
+    assert PyQuery(result).find('div.value a').attr['href'] == '/foo/bar/42/'
+    assert PyQuery(result).find('div.value a').attr['class'] is None
+
+    cell.custom_schema['cells'][0]['display_mode'] = 'button'
+    cell.save()
+    result = cell.render(context)
+    assert PyQuery(result).find('div.value a').text() == 'a - Foo Bar'
+    assert PyQuery(result).find('div.value a').attr['href'] == '/foo/bar/42/'
+    assert PyQuery(result).find('div.value a').attr['class'] == 'pk-button'
+
+    cell.custom_schema['cells'][0][
+        'url_template'
+    ] = '{{ site_base }}/foo/bar/{{ card.fields.related_structured.id }}/'
+    cell.custom_schema['cells'][0]['template'] = '<b>{{ card.fields.fielda }}</b> - {{ card.fields.related }}'
+    cell.card_ids = '11'
+    cell.save()
+    resp = app.get(page.get_online_url())
+    assert len(resp.context['cells']) == 1
+    extra_ctx = re.findall(r'data-extra-context="(.*)"', resp.text)
+    cell_url = reverse(
+        'combo-public-ajax-page-cell',
+        kwargs={'page_pk': page.pk, 'cell_reference': cell.get_reference()},
+    )
+    cell_resp = app.get(cell_url + '?ctx=' + extra_ctx[0])
+    assert (
+        '<div class="value"><a href="http://testserver/foo/bar/42/" class="pk-button">&lt;b&gt;a&lt;/b&gt; - Foo Bar</a></div>'
+        in cell_resp
+    )
+
+
 @mock.patch('combo.apps.wcs.utils.requests.send', side_effect=mocked_requests_send)
 def test_card_cell_render_identifier(mock_send, nocache, app):
     page = Page.objects.create(
-- 
2.34.1

