From 9cf220f2c0a2eaf5ad332d6748d3251a08503f83 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Tue, 14 Jul 2020 11:58:15 +0200
Subject: [PATCH] search: add option to set custom title for subpages search
 engine (#43888)

---
 combo/apps/search/forms.py         |  3 ++-
 combo/apps/search/manager_views.py | 13 ++++++-----
 combo/apps/search/models.py        |  7 ++++++
 tests/test_search.py               | 35 ++++++++++++++++++++++++++----
 4 files changed, 48 insertions(+), 10 deletions(-)

diff --git a/combo/apps/search/forms.py b/combo/apps/search/forms.py
index 8b119c77..a7fd15aa 100644
--- a/combo/apps/search/forms.py
+++ b/combo/apps/search/forms.py
@@ -44,7 +44,7 @@ class SelectWithDisabled(forms.Select):
         return option_dict
 
 
-class SelectPageForm(forms.ModelForm):
+class TextEngineSettingsForm(forms.ModelForm):
     selected_page = forms.ModelChoiceField(
         label=_('Page'),
         required=False,
@@ -52,6 +52,7 @@ class SelectPageForm(forms.ModelForm):
         help_text=_("Select a page to limit the search on this page and sub pages contents."),
         widget=SelectWithDisabled(),
     )
+    title = forms.CharField(label=_('Custom Title'), required=False)
 
     class Meta:
         model = SearchCell
diff --git a/combo/apps/search/manager_views.py b/combo/apps/search/manager_views.py
index 273c07df..5d5a5060 100644
--- a/combo/apps/search/manager_views.py
+++ b/combo/apps/search/manager_views.py
@@ -21,7 +21,7 @@ from django.shortcuts import render
 from django.urls import reverse
 from django.utils.translation import ugettext_lazy as _
 
-from combo.apps.search.forms import SelectPageForm
+from combo.apps.search.forms import TextEngineSettingsForm
 from combo.apps.search.models import SearchCell
 from combo.data.models import PageSnapshot
 
@@ -29,11 +29,14 @@ from combo.data.models import PageSnapshot
 def page_search_cell_add_engine(request, page_pk, cell_reference, engine_slug):
     cell = get_object_or_404(SearchCell, pk=cell_reference.split('-')[1], page=page_pk)
 
-    def add_slug(slug):
+    def add_slug(slug, **options):
         if slug in cell.available_engines or slug.startswith('_text_page'):
             if not cell._search_services or not cell._search_services.get('data'):
                 cell._search_services = {'data': []}
+            if not cell._search_services.get('options'):
+                cell._search_services['options'] = {}
             cell._search_services['data'].append(slug)
+            cell._search_services['options'][slug] = options
             cell.save()
             PageSnapshot.take(cell.page, request=request, comment=_('changed cell "%s"') % cell)
         return HttpResponseRedirect('%s#cell-%s' % (
@@ -45,14 +48,14 @@ def page_search_cell_add_engine(request, page_pk, cell_reference, engine_slug):
         return add_slug(engine_slug)
 
     if request.method == 'POST':
-        form = SelectPageForm(instance=cell, data=request.POST)
+        form = TextEngineSettingsForm(instance=cell, data=request.POST)
         if form.is_valid():
             slug = '_text'
             if form.cleaned_data['selected_page'] is not None:
                 slug = '_text_page_%s' % form.cleaned_data['selected_page'].slug
-            return add_slug(slug)
+            return add_slug(slug, title=form.cleaned_data['title'])
     else:
-        form = SelectPageForm(instance=cell)
+        form = TextEngineSettingsForm(instance=cell)
     context = {
         'form': form,
         'cell': cell,
diff --git a/combo/apps/search/models.py b/combo/apps/search/models.py
index 0829b4b9..8c93554e 100644
--- a/combo/apps/search/models.py
+++ b/combo/apps/search/models.py
@@ -85,6 +85,7 @@ class SearchCell(CellBase):
                 service = engines.get('_text')
             if service and (service.get('url') or service.get('function')):
                 service['slug'] = service_slug
+                service['options'] = self._search_services.get('options', {}).get(service_slug)
                 services.append(service)
         return services
 
@@ -168,6 +169,12 @@ class SearchCell(CellBase):
             service_label = service.get('label')
             if pages:
                 service_label = _('Page "%(page)s" and sub pages Contents') % {'page': pages[0].title}
+            try:
+                # optional label defined with engine
+                if service['options']['title']:
+                    service_label = service['options']['title']
+            except (KeyError, TypeError):
+                pass
             context = {
                 'cell': cell,
                 'results': results,
diff --git a/tests/test_search.py b/tests/test_search.py
index a6282a12..7a9f80fd 100644
--- a/tests/test_search.py
+++ b/tests/test_search.py
@@ -449,6 +449,15 @@ def test_search_on_root_page_api(settings, app):
     cell.save()
     resp = app.get('/ajax/search/%s/_text_page_sub-second-page/?q=baz' % cell.pk, status=200)
     assert resp.text.count('<li') == 2
+    # search with custom title
+    cell._search_services = {
+        'data': ['_text', '_text_page_sub-second-page'],
+        'options': {'_text_page_sub-second-page': {'title': 'Custom Title'}},
+    }
+    cell.save()
+    resp = app.get('/ajax/search/%s/_text_page_sub-second-page/?q=baz' % cell.pk, status=200)
+    assert 'Custom Title' in resp.text
+    assert resp.text.count('<li') == 2
 
 
 def test_search_external_links(app):
@@ -519,7 +528,7 @@ def test_manager_search_cell(settings, app, admin_user):
     resp = app.get('/manage/pages/%s/' % page.pk)
     resp = resp.click(href='.*/search_searchcell-%s/engine/search_tmpl/add/' % cell.pk)
     cell.refresh_from_db()
-    assert cell._search_services == {'data': ['_text', 'search1', 'search_tmpl']}
+    assert cell._search_services['data'] == ['_text', 'search1', 'search_tmpl']
     resp = app.get('/manage/pages/%s/' % page.pk)
     # '_text' is always available
     assert '/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text/add/' % (page.pk, cell.pk) in resp.text
@@ -536,7 +545,7 @@ def test_manager_search_cell(settings, app, admin_user):
     assert resp.status_int == 302
     assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
     cell.refresh_from_db()
-    assert cell._search_services == {'data': ['search1', 'search_tmpl']}
+    assert cell._search_services['data'] == ['search1', 'search_tmpl']
 
     settings.COMBO_SEARCH_SERVICES = {}
     # check there's no crash if search engines are removed from config
@@ -555,7 +564,7 @@ def test_manager_search_cell(settings, app, admin_user):
     assert resp.status_int == 302
     assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
     cell.refresh_from_db()
-    assert cell._search_services == {'data': ['search1', 'search_tmpl', '_text']}
+    assert cell._search_services['data'] == ['search1', 'search_tmpl', '_text']
     resp = app.get('/manage/pages/%s/' % page.pk)
     assert '/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text/add/' % (page.pk, cell.pk) in resp.text
     assert '/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text/delete/' % (page.pk, cell.pk) in resp.text
@@ -566,12 +575,30 @@ def test_manager_search_cell(settings, app, admin_user):
     assert resp.status_int == 302
     assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
     cell.refresh_from_db()
-    assert cell._search_services == {'data': ['search1', 'search_tmpl', '_text', '_text_page_one']}
+    assert cell._search_services['data'] == ['search1', 'search_tmpl', '_text', '_text_page_one']
     resp = app.get('/manage/pages/%s/' % page.pk)
     assert '/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text/add/' % (page.pk, cell.pk) in resp.text
     assert '/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text/delete/' % (page.pk, cell.pk) in resp.text
     assert '/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text_page_one/delete/' % (page.pk, cell.pk) in resp.text
 
+    # remove engine
+    resp = resp.click(href='.*/search_searchcell-%s/engine/_text_page_one/delete/' % cell.pk)
+    assert resp.status_int == 302
+    assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
+    cell.refresh_from_db()
+
+    # add engine on page and sub pages, with a custom title
+    resp = app.get('/manage/pages/%s/' % page.pk)
+    resp = resp.click(href='.*/search_searchcell-%s/engine/_text/add/' % cell.pk)
+    resp.form['selected_page'] = page.pk
+    resp.form['title'] = 'Custom Title'
+    resp = resp.form.submit('submit')
+    assert resp.status_int == 302
+    assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
+    cell.refresh_from_db()
+    assert cell._search_services['data'] == ['search1', 'search_tmpl', '_text', '_text_page_one']
+    assert cell._search_services['options']['_text_page_one'] == {'title': 'Custom Title'}
+
 
 def test_manager_search_cell_order(settings, app, admin_user):
     settings.COMBO_SEARCH_SERVICES = SEARCH_SERVICES
-- 
2.28.0.rc0

