0
0
mirror of https://github.com/wagtail/wagtail.git synced 2024-12-01 11:41:20 +01:00

Merge pull request #1235 from kaedroho/searchpicks-module

Search promotions module
This commit is contained in:
Matt Westcott 2015-07-29 11:52:42 +01:00
commit 9dff7a21ef
32 changed files with 698 additions and 487 deletions

View File

@ -12,7 +12,7 @@
"**/*.min.js",
"**/vendor/**/*.js",
"./wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/inline_panel.js",
"./wagtail/wagtailsearch/templates/wagtailsearch/editorspicks/includes/editorspicks_formset.js",
"./wagtail/contrib/wagtailsearchpromotions/templates/wagtailsearchpromotions/includes/searchpromotions_formset.js",
"./wagtail/wagtailusers/templates/wagtailusers/groups/includes/page_permissions_formset.js",
"./wagtail/wagtailsnippets/templates/wagtailsnippets/chooser/chosen.js",
"./wagtail/wagtailimages/templates/wagtailimages/chooser/image_chosen.js",

View File

@ -13,6 +13,7 @@ Wagtail ships with a variety of extra optional modules.
frontendcache
routablepage
api/index
searchpromotions
:doc:`forms`
@ -49,3 +50,9 @@ Provides a way of embedding Django URLconfs into pages.
----------------
A module for adding a read only, JSON based web API to your Wagtail site
:doc:`searchpromotions`
-----------------------
A module for managing "Promoted Search Results"

View File

@ -0,0 +1,63 @@
.. _editors-picks:
=======================
Promoted search results
=======================
.. module:: wagtail.contrib.wagtailsearchpromotions
.. versionchanged:: 1.1
Before Wagtail 1.1, promoted search results were implemented in the :mod:`wagtail.wagtailsearch` core module and called "editors picks".
The ``searchpromotions`` module provides the models and user interface for managing "Promoted search results" and displaying them in a search results page.
"Promoted search results" allow editors to explicitly link relevant content to search terms, so results pages can contain curated content in addition to results from the search engine.
Installation
============
The ``searchpromotions`` module is not enabled by default. To install it, add ``wagtail.contrib.wagtailsearchpromotions`` to ``INSTALLED_APPS`` in your project's Django settings file.
.. code-block:: python
INSTALLED_APPS = [
...
'wagtail.contrib.wagtailsearchpromotions',
]
This app contains migrations so make sure you run the ``migrate`` django-admin command after installing.
Usage
=====
Once installed, a new menu item called "Promoted search results" should appear in the "Settings" menu. This is where you can assign pages to popular search terms.
Displaying on a search results page
-----------------------------------
To retrieve a list of promoted search results for a particular search query, you can use the ``{% get_search_promotions %}`` template tag from the ``wagtailsearchpromotions_tags`` templatetag library:
.. code-block:: HTML+Django
{% load wagtailcore_tags wagtailsearchpromotions_tags %}
...
{% get_search_promotions search_query as search_promotions %}
<ul>
{% for search_promotion in search_promotions %}
<li>
<a href="{% pageurl search_promotion.page %}">
<h2>{{ search_promotion.page.title }}</h2>
<p>{{ search_promotion.description }}</p>
</a>
</li>
{% endfor %}
</ul>

View File

@ -100,28 +100,12 @@ And here's a template to go with it:
{% endblock %}
.. _editors-picks:
Promoted search results
=======================
"Promoted search results" allow editors to explicitly link relevant content to search terms, so results pages can contain curated content in addition to results from the search engine.
Editor's picks
==============
Editor's picks are a way of explicitly linking relevant content to search terms, so results pages can contain curated content in addition to results from the search algorithm.
You can get a list of editors picks for a particular query using the ``Query`` class:
.. code-block:: python
editors_picks = Query.get(search_query).editors_picks.all()
Each editors pick contains the following fields:
``page``
The page object associated with the pick. Use ``{% pageurl editors_pick.page %}`` to generate a URL or provide other properties of the page object.
``description``
The description entered when choosing the pick, perhaps explaining why the page is relevant to the search terms.
This functionality is provided by the :mod:`~wagtail.contrib.wagtailsearchpromotions` contrib module.
Searching Images, Documents and custom models

View File

@ -0,0 +1 @@
default_app_config = 'wagtail.contrib.wagtailsearchpromotions.apps.WagtailSearchPromotionsAppConfig'

View File

@ -0,0 +1,10 @@
from django.conf.urls import url
from wagtail.contrib.wagtailsearchpromotions import views
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^add/$', views.add, name='add'),
url(r'^(\d+)/$', views.edit, name='edit'),
url(r'^(\d+)/delete/$', views.delete, name='delete'),
]

View File

@ -0,0 +1,7 @@
from django.apps import AppConfig
class WagtailSearchPromotionsAppConfig(AppConfig):
name = 'wagtail.contrib.wagtailsearchpromotions'
label = 'wagtailsearchpromotions'
verbose_name = "Wagtail search promotions"

View File

@ -0,0 +1,58 @@
from django import forms
from django.forms.models import inlineformset_factory
from django.utils.translation import ugettext_lazy as _
from wagtail.wagtailadmin.widgets import AdminPageChooser
from wagtail.wagtailsearch.models import Query
from wagtail.contrib.wagtailsearchpromotions.models import SearchPromotion
class SearchPromotionForm(forms.ModelForm):
sort_order = forms.IntegerField(required=False)
def __init__(self, *args, **kwargs):
super(SearchPromotionForm, self).__init__(*args, **kwargs)
self.fields['page'].widget = AdminPageChooser()
class Meta:
model = SearchPromotion
fields = ('query', 'page', 'description')
widgets = {
'description': forms.Textarea(attrs=dict(rows=3)),
}
SearchPromotionsFormSetBase = inlineformset_factory(Query, SearchPromotion, form=SearchPromotionForm, can_order=True, can_delete=True, extra=0)
class SearchPromotionsFormSet(SearchPromotionsFormSetBase):
minimum_forms = 1
minimum_forms_message = _("Please specify at least one recommendation for this search term.")
def add_fields(self, form, *args, **kwargs):
super(SearchPromotionsFormSet, self).add_fields(form, *args, **kwargs)
# Hide delete and order fields
form.fields['DELETE'].widget = forms.HiddenInput()
form.fields['ORDER'].widget = forms.HiddenInput()
# Remove query field
del form.fields['query']
def clean(self):
# Search pick must have at least one recommended page to be valid
# Check there is at least one non-deleted form.
non_deleted_forms = self.total_form_count()
non_empty_forms = 0
for i in range(0, self.total_form_count()):
form = self.forms[i]
if self.can_delete and self._should_delete_form(form):
non_deleted_forms -= 1
if not (form.instance.id is None and not form.has_changed()):
non_empty_forms += 1
if (
non_deleted_forms < self.minimum_forms
or non_empty_forms < self.minimum_forms
):
raise forms.ValidationError(self.minimum_forms_message)

View File

@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('wagtailcore', '0015_add_more_verbose_names'),
('wagtailsearch', '0003_remove_editors_pick'),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.CreateModel(
name='EditorsPick',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('sort_order', models.IntegerField(editable=False, null=True, blank=True)),
('description', models.TextField(verbose_name='Description', blank=True)),
('page', models.ForeignKey(verbose_name='Page', to='wagtailcore.Page')),
('query', models.ForeignKey(to='wagtailsearch.Query', related_name='editors_picks')),
],
options={
'db_table': 'wagtailsearch_editorspick',
'verbose_name': "Editor's Pick",
'ordering': ('sort_order',),
},
),
],
database_operations=[]
),
migrations.AlterModelTable(
name='editorspick',
table=None,
),
migrations.RenameModel(
old_name='EditorsPick',
new_name='SearchPromotion'
),
migrations.AlterModelOptions(
name='searchpromotion',
options={'ordering': ('sort_order',), 'verbose_name': 'Search promotion'},
),
]

View File

@ -0,0 +1,18 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
from wagtail.wagtailsearch.models import Query
class SearchPromotion(models.Model):
query = models.ForeignKey(Query, db_index=True, related_name='editors_picks')
page = models.ForeignKey('wagtailcore.Page', verbose_name=_('Page'))
sort_order = models.IntegerField(null=True, blank=True, editable=False)
description = models.TextField(verbose_name=_('Description'), blank=True)
def __repr__(self):
return 'SearchPromotion(query="' + self.query.query_string + '", page="' + self.page.title + '")'
class Meta:
ordering = ('sort_order', )
verbose_name = _("Search promotion")

View File

@ -1,8 +1,8 @@
{% extends "wagtailadmin/base.html" %}
{% load i18n %}
{% block titletag %}{% trans "Add editor's pick" %}{% endblock %}
{% block titletag %}{% trans "Add search pick" %}{% endblock %}
{% block content %}
{% trans "Add editor's pick" as add_str %}
{% trans "Add search pick" as add_str %}
{% include "wagtailadmin/shared/header.html" with title=add_str icon="pick" %}
<div class="nice-padding">
@ -15,7 +15,7 @@
<p>The "Search term(s)/phrase" field below must contain the full and exact search for which you wish to provide recommended results, <em>including</em> any misspellings/user error. To help, you can choose from search terms that have been popular with users of your site.</p>
{% endblocktrans %}
</div>
<form action="{% url 'wagtailsearch_editorspicks_add' %}" method="POST">
<form action="{% url 'wagtailsearchpromotions:add' %}" method="POST">
{% csrf_token %}
<ul class="fields">
@ -23,7 +23,7 @@
{% include "wagtailsearch/queries/chooser_field.html" with field=query_form.query_string only %}
</li>
<li>
{% include "wagtailsearch/editorspicks/includes/editorspicks_formset.html" with formset=editors_pick_formset only %}
{% include "wagtailsearchpromotions/includes/searchpromotions_formset.html" with formset=searchpicks_formset only %}
</li>
<li><input type="submit" value="{% trans 'Save' %}" /></li>
</ul>
@ -38,7 +38,7 @@
{% include "wagtailadmin/pages/_editor_js.html" %}
<script type="text/javascript">
{% include "wagtailsearch/editorspicks/includes/editorspicks_formset.js" with formset=editors_pick_formset only %}
{% include "wagtailsearchpromotions/includes/searchpromotions_formset.js" with formset=searchpicks_formset only %}
{% include "wagtailsearch/queries/chooser_field.js" only %}
$(function() {

View File

@ -7,7 +7,7 @@
<div class="nice-padding">
<p>{% trans "Are you sure you want to delete all promoted results for this search term?" %}</p>
<form action="{% url 'wagtailsearch_editorspicks_delete' query.id %}" method="POST">
<form action="{% url 'wagtailsearchpromotions:delete' query.id %}" method="POST">
{% csrf_token %}
<input type="submit" value="{% trans 'Yes, delete' %}" class="serious" />
</form>

View File

@ -5,7 +5,7 @@
{% trans "Editing" as editing_str %}
{% include "wagtailadmin/shared/header.html" with title=editing_str subtitle=query.query_string icon="pick" %}
<form action="{% url 'wagtailsearch_editorspicks_edit' query.id %}" method="POST" class="nice-padding">
<form action="{% url 'wagtailsearchpromotions:edit' query.id %}" method="POST" class="nice-padding">
{% csrf_token %}
<ul class="fields">
@ -13,11 +13,11 @@
{% include "wagtailsearch/queries/chooser_field.html" with field=query_form.query_string only %}
</li>
<li>
{% include "wagtailsearch/editorspicks/includes/editorspicks_formset.html" with formset=editors_pick_formset only %}
{% include "wagtailsearchpromotions/includes/searchpromotions_formset.html" with formset=searchpicks_formset only %}
</li>
<li>
<input type="submit" value="{% trans 'Save' %}" />
<a href="{% url 'wagtailsearch_editorspicks_delete' query.id %}" class="button button-secondary no">{% trans "Delete" %}</a>
<a href="{% url 'wagtailsearchpromotions:delete' query.id %}" class="button button-secondary no">{% trans "Delete" %}</a>
</li>
</ul>
</form>
@ -30,11 +30,11 @@
{% include "wagtailadmin/pages/_editor_js.html" %}
<script type="text/javascript">
{% include "wagtailsearch/editorspicks/includes/editorspicks_formset.js" with formset=editors_pick_formset only %}
{% include "wagtailsearchpromotions/includes/searchpromotions_formset.js" with formset=searchpicks_formset only %}
{% include "wagtailsearch/queries/chooser_field.js" only %}
$(function() {
createQueryChooser('{{ query_form.query_string.auto_id }}');
});
</script>
{% endblock %}
{% endblock %}

View File

@ -2,13 +2,13 @@
{{ formset.management_form }}
<ul class="multiple" id="id_{{ formset.prefix }}-FORMS">
{% for form in formset.forms %}
{% include "wagtailsearch/editorspicks/includes/editorspicks_form.html" with form=form only %}
{% include "wagtailsearchpromotions/includes/searchpromotion_form.html" with form=form only %}
{% endfor %}
</ul>
<script type="text/django-form-template" id="id_{{ formset.prefix }}-EMPTY_FORM_TEMPLATE">
{% escapescript %}
{% include "wagtailsearch/editorspicks/includes/editorspicks_form.html" with form=formset.empty_form only %}
{% include "wagtailsearchpromotions/includes/searchpromotion_form.html" with form=formset.empty_form only %}
{% endescapescript %}
</script>

View File

@ -6,7 +6,7 @@
{% block extra_js %}
<script>
window.headerSearch = {
url: "{% url 'wagtailsearch_editorspicks_index' %}",
url: "{% url 'wagtailsearchpromotions:index' %}",
termInput: "#id_q",
targetOutput: "#editorspicks-results"
}
@ -16,11 +16,11 @@
{% block content %}
{% trans "Promoted search results" as sp_title_str %}
{% trans "Add new promoted result" as sp_text_str %}
{% include "wagtailadmin/shared/header.html" with title=sp_title_str add_link="wagtailsearch_editorspicks_add" icon="pick" add_text=sp_text_str search_url="wagtailsearch_editorspicks_index" %}
{% include "wagtailadmin/shared/header.html" with title=sp_title_str add_link="wagtailsearchpromotions:add" icon="pick" add_text=sp_text_str search_url="wagtailsearchpromotions:index" %}
<div class="nice-padding">
<div id="editorspicks-results" class="redirects">
{% include "wagtailsearch/editorspicks/results.html" %}
{% include "wagtailsearchpromotions/results.html" %}
</div>
</div>
{% endblock %}
{% endblock %}

View File

@ -14,11 +14,11 @@
{% for query in queries %}
<tr>
<td class="title">
<h2><a href="{% url 'wagtailsearch_editorspicks_edit' query.id %}" title="{% trans 'Edit this pick' %}">{{ query.query_string }}</a></h2>
<h2><a href="{% url 'wagtailsearchpromotions:edit' query.id %}" title="{% trans 'Edit this pick' %}">{{ query.query_string }}</a></h2>
</td>
<td>
{% for editors_pick in query.editors_picks.all %}
<a href="{% url 'wagtailadmin_pages:edit' editors_pick.page.id %}" class="nolink">{{ editors_pick.page.title }}</a>{% if not forloop.last %}, {% endif %}
{% for searchpick in query.editors_picks.all %}
<a href="{% url 'wagtailadmin_pages:edit' searchpick.page.id %}" class="nolink">{{ searchpick.page.title }}</a>{% if not forloop.last %}, {% endif %}
{% empty %}
{% trans "None" %}
{% endfor %}
@ -27,4 +27,4 @@
</tr>
{% endfor %}
</tbody>
</table>
</table>

View File

@ -10,14 +10,14 @@
</h2>
{% endif %}
{% include "wagtailsearch/editorspicks/list.html" %}
{% include "wagtailsearchpromotions/list.html" %}
{% include "wagtailadmin/shared/pagination_nav.html" with items=queries is_searching=is_searching linkurl="wagtailsearch_editorspicks_index" %}
{% include "wagtailadmin/shared/pagination_nav.html" with items=queries is_searching=is_searching linkurl="wagtailsearchpromotions:index" %}
{% else %}
{% if is_searching %}
<p>{% blocktrans %}Sorry, no promoted results match "<em>{{ query_string }}</em>"{% endblocktrans %}</p>
{% else %}
{% url 'wagtailsearch_editorspicks_add' as wagtailsearch_editorspicks_add_url %}
<p>{% blocktrans %}No promoted results have been created. Why not <a href="{{ wagtailsearch_editorspicks_add_url }}">add one</a>?{% endblocktrans %}</p>
{% url 'wagtailsearchpromotions:add' as wagtailsearchpromotions_add_url %}
<p>{% blocktrans %}No promoted results have been created. Why not <a href="{{ wagtailsearchpromotions_add_url }}">add one</a>?{% endblocktrans %}</p>
{% endif %}
{% endif %}

View File

@ -0,0 +1,11 @@
from django import template
from wagtail.wagtailsearch.models import Query
register = template.Library()
@register.assignment_tag()
def get_search_promotions(search_query):
return Query.get(search_query).editors_picks.all()

View File

@ -0,0 +1,336 @@
from django.test import TestCase
from django.core.urlresolvers import reverse
from wagtail.tests.utils import WagtailTestUtils
from wagtail.wagtailsearch.models import Query
from wagtail.contrib.wagtailsearchpromotions.models import SearchPromotion
from wagtail.contrib.wagtailsearchpromotions.templatetags.wagtailsearchpromotions_tags import get_search_promotions
class TestSearchPromotions(TestCase):
def test_search_pick_create(self):
# Create a search pick to the root page
SearchPromotion.objects.create(
query=Query.get("root page"),
page_id=1,
sort_order=0,
description="First search pick",
)
# Check
self.assertEqual(Query.get("root page").editors_picks.count(), 1)
self.assertEqual(Query.get("root page").editors_picks.first().page_id, 1)
def test_search_pick_ordering(self):
# Add 3 search picks in a different order to their sort_order values
# They should be ordered by their sort order values and not their insertion order
SearchPromotion.objects.create(
query=Query.get("root page"),
page_id=1,
sort_order=0,
description="First search pick",
)
SearchPromotion.objects.create(
query=Query.get("root page"),
page_id=1,
sort_order=2,
description="Last search pick",
)
SearchPromotion.objects.create(
query=Query.get("root page"),
page_id=1,
sort_order=1,
description="Middle search pick",
)
# Check
self.assertEqual(Query.get("root page").editors_picks.count(), 3)
self.assertEqual(Query.get("root page").editors_picks.first().description, "First search pick")
self.assertEqual(Query.get("root page").editors_picks.last().description, "Last search pick")
class TestGetSearchPromotionsTemplateTag(TestCase):
def test_get_search_promotions_template_tag(self):
# Create a search pick to the root page
pick = SearchPromotion.objects.create(
query=Query.get("root page"),
page_id=1,
sort_order=0,
description="First search pick",
)
# Create another search pick against a different query
SearchPromotion.objects.create(
query=Query.get("root page again"),
page_id=1,
sort_order=0,
description="Second search pick",
)
# Check
search_picks = list(get_search_promotions("root page"))
self.assertEqual(search_picks, [pick])
class TestSearchPromotionsIndexView(TestCase, WagtailTestUtils):
def setUp(self):
self.login()
def test_simple(self):
response = self.client.get(reverse('wagtailsearchpromotions:index'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearchpromotions/index.html')
def test_search(self):
response = self.client.get(reverse('wagtailsearchpromotions:index'), {'q': "Hello"})
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['query_string'], "Hello")
def make_search_picks(self):
for i in range(50):
SearchPromotion.objects.create(
query=Query.get("query " + str(i)),
page_id=1,
sort_order=0,
description="First search pick",
)
def test_pagination(self):
self.make_search_picks()
response = self.client.get(reverse('wagtailsearchpromotions:index'), {'p': 2})
# Check response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearchpromotions/index.html')
# Check that we got the correct page
self.assertEqual(response.context['queries'].number, 2)
def test_pagination_invalid(self):
self.make_search_picks()
response = self.client.get(reverse('wagtailsearchpromotions:index'), {'p': 'Hello World!'})
# Check response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearchpromotions/index.html')
# Check that we got page one
self.assertEqual(response.context['queries'].number, 1)
def test_pagination_out_of_range(self):
self.make_search_picks()
response = self.client.get(reverse('wagtailsearchpromotions:index'), {'p': 99999})
# Check response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearchpromotions/index.html')
# Check that we got the last page
self.assertEqual(response.context['queries'].number, response.context['queries'].paginator.num_pages)
class TestSearchPromotionsAddView(TestCase, WagtailTestUtils):
def setUp(self):
self.login()
def test_simple(self):
response = self.client.get(reverse('wagtailsearchpromotions:add'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearchpromotions/add.html')
def test_post(self):
# Submit
post_data = {
'query_string': "test",
'editors_picks-TOTAL_FORMS': 1,
'editors_picks-INITIAL_FORMS': 0,
'editors_picks-MAX_NUM_FORMS': 1000,
'editors_picks-0-DELETE': '',
'editors_picks-0-ORDER': 0,
'editors_picks-0-page': 1,
'editors_picks-0-description': "Hello",
}
response = self.client.post(reverse('wagtailsearchpromotions:add'), post_data)
# User should be redirected back to the index
self.assertRedirects(response, reverse('wagtailsearchpromotions:index'))
# Check that the search pick was created
self.assertTrue(Query.get('test').editors_picks.filter(page_id=1).exists())
def test_post_without_recommendations(self):
# Submit
post_data = {
'query_string': "test",
'editors_picks-TOTAL_FORMS': 0,
'editors_picks-INITIAL_FORMS': 0,
'editors_picks-MAX_NUM_FORMS': 1000,
}
response = self.client.post(reverse('wagtailsearchpromotions:add'), post_data)
# User should be given an error
self.assertEqual(response.status_code, 200)
self.assertFormsetError(response, 'searchpicks_formset', None, None, "Please specify at least one recommendation for this search term.")
class TestSearchPromotionsEditView(TestCase, WagtailTestUtils):
def setUp(self):
self.login()
# Create an search pick to edit
self.query = Query.get("Hello")
self.search_pick = self.query.editors_picks.create(page_id=1, description="Root page")
self.search_pick_2 = self.query.editors_picks.create(page_id=2, description="Homepage")
def test_simple(self):
response = self.client.get(reverse('wagtailsearchpromotions:edit', args=(self.query.id, )))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearchpromotions/edit.html')
def test_post(self):
# Submit
post_data = {
'query_string': "Hello",
'editors_picks-TOTAL_FORMS': 2,
'editors_picks-INITIAL_FORMS': 2,
'editors_picks-MAX_NUM_FORMS': 1000,
'editors_picks-0-id': self.search_pick.id,
'editors_picks-0-DELETE': '',
'editors_picks-0-ORDER': 0,
'editors_picks-0-page': 1,
'editors_picks-0-description': "Description has changed", # Change
'editors_picks-1-id': self.search_pick_2.id,
'editors_picks-1-DELETE': '',
'editors_picks-1-ORDER': 1,
'editors_picks-1-page': 2,
'editors_picks-1-description': "Homepage",
}
response = self.client.post(reverse('wagtailsearchpromotions:edit', args=(self.query.id, )), post_data)
# User should be redirected back to the index
self.assertRedirects(response, reverse('wagtailsearchpromotions:index'))
# Check that the search pick description was edited
self.assertEqual(SearchPromotion.objects.get(id=self.search_pick.id).description, "Description has changed")
def test_post_reorder(self):
# Check order before reordering
self.assertEqual(Query.get("Hello").editors_picks.all()[0], self.search_pick)
self.assertEqual(Query.get("Hello").editors_picks.all()[1], self.search_pick_2)
# Submit
post_data = {
'query_string': "Hello",
'editors_picks-TOTAL_FORMS': 2,
'editors_picks-INITIAL_FORMS': 2,
'editors_picks-MAX_NUM_FORMS': 1000,
'editors_picks-0-id': self.search_pick.id,
'editors_picks-0-DELETE': '',
'editors_picks-0-ORDER': 1, # Change
'editors_picks-0-page': 1,
'editors_picks-0-description': "Root page",
'editors_picks-1-id': self.search_pick_2.id,
'editors_picks-1-DELETE': '',
'editors_picks-1-ORDER': 0, # Change
'editors_picks-1-page': 2,
'editors_picks-1-description': "Homepage",
}
response = self.client.post(reverse('wagtailsearchpromotions:edit', args=(self.query.id, )), post_data)
# User should be redirected back to the index
self.assertRedirects(response, reverse('wagtailsearchpromotions:index'))
# Check that the ordering has been saved correctly
self.assertEqual(SearchPromotion.objects.get(id=self.search_pick.id).sort_order, 1)
self.assertEqual(SearchPromotion.objects.get(id=self.search_pick_2.id).sort_order, 0)
# Check that the recommendations were reordered
self.assertEqual(Query.get("Hello").editors_picks.all()[0], self.search_pick_2)
self.assertEqual(Query.get("Hello").editors_picks.all()[1], self.search_pick)
def test_post_delete_recommendation(self):
# Submit
post_data = {
'query_string': "Hello",
'editors_picks-TOTAL_FORMS': 2,
'editors_picks-INITIAL_FORMS': 2,
'editors_picks-MAX_NUM_FORMS': 1000,
'editors_picks-0-id': self.search_pick.id,
'editors_picks-0-DELETE': '',
'editors_picks-0-ORDER': 0,
'editors_picks-0-page': 1,
'editors_picks-0-description': "Root page",
'editors_picks-1-id': self.search_pick_2.id,
'editors_picks-1-DELETE': 1,
'editors_picks-1-ORDER': 1,
'editors_picks-1-page': 2,
'editors_picks-1-description': "Homepage",
}
response = self.client.post(reverse('wagtailsearchpromotions:edit', args=(self.query.id, )), post_data)
# User should be redirected back to the index
self.assertRedirects(response, reverse('wagtailsearchpromotions:index'))
# Check that the recommendation was deleted
self.assertFalse(SearchPromotion.objects.filter(id=self.search_pick_2.id).exists())
# The other recommendation should still exist
self.assertTrue(SearchPromotion.objects.filter(id=self.search_pick.id).exists())
def test_post_without_recommendations(self):
# Submit
post_data = {
'query_string': "Hello",
'editors_picks-TOTAL_FORMS': 2,
'editors_picks-INITIAL_FORMS': 2,
'editors_picks-MAX_NUM_FORMS': 1000,
'editors_picks-0-id': self.search_pick.id,
'editors_picks-0-DELETE': 1,
'editors_picks-0-ORDER': 0,
'editors_picks-0-page': 1,
'editors_picks-0-description': "Description has changed", # Change
'editors_picks-1-id': self.search_pick_2.id,
'editors_picks-1-DELETE': 1,
'editors_picks-1-ORDER': 1,
'editors_picks-1-page': 2,
'editors_picks-1-description': "Homepage",
}
response = self.client.post(reverse('wagtailsearchpromotions:edit', args=(self.query.id, )), post_data)
# User should be given an error
self.assertEqual(response.status_code, 200)
self.assertFormsetError(response, 'searchpicks_formset', None, None, "Please specify at least one recommendation for this search term.")
class TestSearchPromotionsDeleteView(TestCase, WagtailTestUtils):
def setUp(self):
self.login()
# Create an search pick to delete
self.query = Query.get("Hello")
self.search_pick = self.query.editors_picks.create(page_id=1, description="Root page")
self.search_pick_2 = self.query.editors_picks.create(page_id=2, description="Homepage")
def test_simple(self):
response = self.client.get(reverse('wagtailsearchpromotions:delete', args=(self.query.id, )))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearchpromotions/confirm_delete.html')
def test_post(self):
# Submit
post_data = {
'foo': 'bar',
}
response = self.client.post(reverse('wagtailsearchpromotions:delete', args=(self.query.id, )), post_data)
# User should be redirected back to the index
self.assertRedirects(response, reverse('wagtailsearchpromotions:index'))
# Check that both recommendations were deleted
self.assertFalse(SearchPromotion.objects.filter(id=self.search_pick_2.id).exists())
# The other recommendation should still exist
self.assertFalse(SearchPromotion.objects.filter(id=self.search_pick.id).exists())

View File

@ -5,10 +5,13 @@ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.utils.translation import ugettext as _
from django.views.decorators.vary import vary_on_headers
from wagtail.wagtailsearch import models, forms
from wagtail.wagtailsearch import forms as search_forms
from wagtail.wagtailsearch.models import Query
from wagtail.wagtailadmin.forms import SearchForm
from wagtail.wagtailadmin import messages
from wagtail.contrib.wagtailsearchpromotions import forms
@vary_on_headers('X-Requested-With')
def index(request):
@ -16,7 +19,7 @@ def index(request):
page = request.GET.get('p', 1)
query_string = request.GET.get('q', "")
queries = models.Query.objects.filter(editors_picks__isnull=False).distinct()
queries = Query.objects.filter(editors_picks__isnull=False).distinct()
# Search
if query_string:
@ -33,13 +36,13 @@ def index(request):
queries = paginator.page(paginator.num_pages)
if request.is_ajax():
return render(request, "wagtailsearch/editorspicks/results.html", {
return render(request, "wagtailsearchpromotions/results.html", {
'is_searching': is_searching,
'queries': queries,
'query_string': query_string,
})
else:
return render(request, 'wagtailsearch/editorspicks/index.html', {
return render(request, 'wagtailsearchpromotions/index.html', {
'is_searching': is_searching,
'queries': queries,
'query_string': query_string,
@ -47,21 +50,21 @@ def index(request):
})
def save_editorspicks(query, new_query, editors_pick_formset):
def save_searchpicks(query, new_query, searchpicks_formset):
# Save
if editors_pick_formset.is_valid():
if searchpicks_formset.is_valid():
# Set sort_order
for i, form in enumerate(editors_pick_formset.ordered_forms):
for i, form in enumerate(searchpicks_formset.ordered_forms):
form.instance.sort_order = i
# Make sure the form is marked as changed so it gets saved with the new order
form.has_changed = lambda: True
editors_pick_formset.save()
searchpicks_formset.save()
# If query was changed, move all editors picks to the new query
# If query was changed, move all search picks to the new query
if query != new_query:
editors_pick_formset.get_queryset().update(query=new_query)
searchpicks_formset.get_queryset().update(query=new_query)
return True
else:
@ -71,77 +74,77 @@ def save_editorspicks(query, new_query, editors_pick_formset):
def add(request):
if request.POST:
# Get query
query_form = forms.QueryForm(request.POST)
query_form = search_forms.QueryForm(request.POST)
if query_form.is_valid():
query = models.Query.get(query_form['query_string'].value())
query = Query.get(query_form['query_string'].value())
# Save editors picks
editors_pick_formset = forms.EditorsPickFormSet(request.POST, instance=query)
if save_editorspicks(query, query, editors_pick_formset):
# Save search picks
searchpicks_formset = forms.SearchPromotionsFormSet(request.POST, instance=query)
if save_searchpicks(query, query, searchpicks_formset):
messages.success(request, _("Editor's picks for '{0}' created.").format(query), buttons=[
messages.button(reverse('wagtailsearch_editorspicks_edit', args=(query.id,)), _('Edit'))
messages.button(reverse('wagtailsearchpromotions:edit', args=(query.id,)), _('Edit'))
])
return redirect('wagtailsearch_editorspicks_index')
return redirect('wagtailsearchpromotions:index')
else:
if len(editors_pick_formset.non_form_errors()):
messages.error(request, " ".join(error for error in editors_pick_formset.non_form_errors())) # formset level error (e.g. no forms submitted)
if len(searchpicks_formset.non_form_errors()):
messages.error(request, " ".join(error for error in searchpicks_formset.non_form_errors())) # formset level error (e.g. no forms submitted)
else:
messages.error(request, _("Recommendations have not been created due to errors")) # specific errors will be displayed within form fields
else:
editors_pick_formset = forms.EditorsPickFormSet()
searchpicks_formset = forms.SearchPromotionsFormSet()
else:
query_form = forms.QueryForm()
editors_pick_formset = forms.EditorsPickFormSet()
query_form = search_forms.QueryForm()
searchpicks_formset = forms.SearchPromotionsFormSet()
return render(request, 'wagtailsearch/editorspicks/add.html', {
return render(request, 'wagtailsearchpromotions/add.html', {
'query_form': query_form,
'editors_pick_formset': editors_pick_formset,
'searchpicks_formset': searchpicks_formset,
})
def edit(request, query_id):
query = get_object_or_404(models.Query, id=query_id)
query = get_object_or_404(Query, id=query_id)
if request.POST:
# Get query
query_form = forms.QueryForm(request.POST)
query_form = search_forms.QueryForm(request.POST)
# and the recommendations
editors_pick_formset = forms.EditorsPickFormSet(request.POST, instance=query)
searchpicks_formset = forms.SearchPromotionsFormSet(request.POST, instance=query)
if query_form.is_valid():
new_query = models.Query.get(query_form['query_string'].value())
new_query = Query.get(query_form['query_string'].value())
# Save editors picks
if save_editorspicks(query, new_query, editors_pick_formset):
# Save search picks
if save_searchpicks(query, new_query, searchpicks_formset):
messages.success(request, _("Editor's picks for '{0}' updated.").format(new_query), buttons=[
messages.button(reverse('wagtailsearch_editorspicks_edit', args=(query.id,)), _('Edit'))
messages.button(reverse('wagtailsearchpromotions:edit', args=(query.id,)), _('Edit'))
])
return redirect('wagtailsearch_editorspicks_index')
return redirect('wagtailsearchpromotions:index')
else:
if len(editors_pick_formset.non_form_errors()):
messages.error(request, " ".join(error for error in editors_pick_formset.non_form_errors())) # formset level error (e.g. no forms submitted)
if len(searchpicks_formset.non_form_errors()):
messages.error(request, " ".join(error for error in searchpicks_formset.non_form_errors())) # formset level error (e.g. no forms submitted)
else:
messages.error(request, _("Recommendations have not been saved due to errors")) # specific errors will be displayed within form fields
else:
query_form = forms.QueryForm(initial=dict(query_string=query.query_string))
editors_pick_formset = forms.EditorsPickFormSet(instance=query)
query_form = search_forms.QueryForm(initial=dict(query_string=query.query_string))
searchpicks_formset = forms.SearchPromotionsFormSet(instance=query)
return render(request, 'wagtailsearch/editorspicks/edit.html', {
return render(request, 'wagtailsearchpromotions/edit.html', {
'query_form': query_form,
'editors_pick_formset': editors_pick_formset,
'searchpicks_formset': searchpicks_formset,
'query': query,
})
def delete(request, query_id):
query = get_object_or_404(models.Query, id=query_id)
query = get_object_or_404(Query, id=query_id)
if request.POST:
query.editors_picks.all().delete()
messages.success(request, _("Editor's picks deleted."))
return redirect('wagtailsearch_editorspicks_index')
return redirect('wagtailsearchpromotions:index')
return render(request, 'wagtailsearch/editorspicks/confirm_delete.html', {
return render(request, 'wagtailsearchpromotions/confirm_delete.html', {
'query': query,
})

View File

@ -0,0 +1,26 @@
from django.core import urlresolvers
from django.conf.urls import include, url
from django.utils.translation import ugettext_lazy as _
from wagtail.wagtailcore import hooks
from wagtail.contrib.wagtailsearchpromotions import admin_urls
from wagtail.wagtailadmin.menu import MenuItem
@hooks.register('register_admin_urls')
def register_admin_urls():
return [
url(r'^searchpicks/', include(admin_urls, namespace='wagtailsearchpromotions')),
]
class SearchPicksMenuItem(MenuItem):
def is_shown(self, request):
# TEMPORARY: Only show if the user is a superuser
return request.user.is_superuser
@hooks.register('register_settings_menu_item')
def register_search_picks_menu_item():
return SearchPicksMenuItem(_('Promoted search results'), urlresolvers.reverse('wagtailsearchpromotions:index'), classnames='icon icon-pick', order=900)

View File

@ -94,6 +94,7 @@ INSTALLED_APPS = (
'wagtail.contrib.wagtailroutablepage',
'wagtail.contrib.wagtailfrontendcache',
'wagtail.contrib.wagtailapi',
'wagtail.contrib.wagtailsearchpromotions',
'wagtail.wagtailforms',
'wagtail.wagtailsearch',
'wagtail.wagtailembeds',

View File

@ -1,10 +1,6 @@
from django import forms
from django.forms.models import inlineformset_factory
from django.utils.translation import ugettext_lazy as _
from wagtail.wagtailadmin.widgets import AdminPageChooser
from wagtail.wagtailsearch import models
class QueryForm(forms.Form):
query_string = forms.CharField(label=_("Search term(s)/phrase"),
@ -12,54 +8,3 @@ class QueryForm(forms.Form):
"exact match is required for your Editors Picks to be "
"displayed, wildcards are NOT allowed."),
required=True)
class EditorsPickForm(forms.ModelForm):
sort_order = forms.IntegerField(required=False)
def __init__(self, *args, **kwargs):
super(EditorsPickForm, self).__init__(*args, **kwargs)
self.fields['page'].widget = AdminPageChooser()
class Meta:
model = models.EditorsPick
fields = ('query', 'page', 'description')
widgets = {
'description': forms.Textarea(attrs=dict(rows=3)),
}
EditorsPickFormSetBase = inlineformset_factory(models.Query, models.EditorsPick, form=EditorsPickForm, can_order=True, can_delete=True, extra=0)
class EditorsPickFormSet(EditorsPickFormSetBase):
minimum_forms = 1
minimum_forms_message = _("Please specify at least one recommendation for this search term.")
def add_fields(self, form, *args, **kwargs):
super(EditorsPickFormSet, self).add_fields(form, *args, **kwargs)
# Hide delete and order fields
form.fields['DELETE'].widget = forms.HiddenInput()
form.fields['ORDER'].widget = forms.HiddenInput()
# Remove query field
del form.fields['query']
def clean(self):
# Editors pick must have at least one recommended page to be valid
# Check there is at least one non-deleted form.
non_deleted_forms = self.total_form_count()
non_empty_forms = 0
for i in range(0, self.total_form_count()):
form = self.forms[i]
if self.can_delete and self._should_delete_form(form):
non_deleted_forms -= 1
if not (form.instance.id is None and not form.has_changed()):
non_empty_forms += 1
if (
non_deleted_forms < self.minimum_forms
or non_empty_forms < self.minimum_forms
):
raise forms.ValidationError(self.minimum_forms_message)

View File

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('wagtailsearch', '0002_add_verbose_names'),
]
operations = [
# EditorsPicks have been moved to the "wagtailsearchpromotions" module.
# Remove EditorsPick from wagtailsearch but don't drop the underlying table
# so wagtailsearchpromotions can pick it up in its initial migration.
# If wagtailsearchpromotions isn't installed, this table will remain
# in the database unmanaged until it is. This could potentially happen
# at any point in the future so it's important to keep this behaviour
# even if we decide to squash these migrations.
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.RemoveField(
model_name='editorspick',
name='page',
),
migrations.RemoveField(
model_name='editorspick',
name='query',
),
migrations.DeleteModel(
name='EditorsPick',
),
],
database_operations=[],
)
]

View File

@ -71,17 +71,3 @@ class QueryDailyHits(models.Model):
('query', 'date'),
)
verbose_name = _('Query Daily Hits')
class EditorsPick(models.Model):
query = models.ForeignKey(Query, db_index=True, related_name='editors_picks')
page = models.ForeignKey('wagtailcore.Page', verbose_name=_('Page'))
sort_order = models.IntegerField(null=True, blank=True, editable=False)
description = models.TextField(verbose_name=_('Description'), blank=True)
def __repr__(self):
return 'EditorsPick(query="' + self.query.query_string + '", page="' + self.page.title + '")'
class Meta:
ordering = ('sort_order', )
verbose_name = _("Editor's Pick")

View File

@ -1,311 +0,0 @@
from django.test import TestCase
from django.core.urlresolvers import reverse
from wagtail.tests.utils import WagtailTestUtils
from wagtail.wagtailsearch import models
class TestEditorsPicks(TestCase):
def test_editors_pick_create(self):
# Create an editors pick to the root page
models.EditorsPick.objects.create(
query=models.Query.get("root page"),
page_id=1,
sort_order=0,
description="First editors pick",
)
# Check
self.assertEqual(models.Query.get("root page").editors_picks.count(), 1)
self.assertEqual(models.Query.get("root page").editors_picks.first().page_id, 1)
def test_editors_pick_ordering(self):
# Add 3 editors picks in a different order to their sort_order values
# They should be ordered by their sort order values and not their insertion order
models.EditorsPick.objects.create(
query=models.Query.get("root page"),
page_id=1,
sort_order=0,
description="First editors pick",
)
models.EditorsPick.objects.create(
query=models.Query.get("root page"),
page_id=1,
sort_order=2,
description="Last editors pick",
)
models.EditorsPick.objects.create(
query=models.Query.get("root page"),
page_id=1,
sort_order=1,
description="Middle editors pick",
)
# Check
self.assertEqual(models.Query.get("root page").editors_picks.count(), 3)
self.assertEqual(models.Query.get("root page").editors_picks.first().description, "First editors pick")
self.assertEqual(models.Query.get("root page").editors_picks.last().description, "Last editors pick")
class TestEditorsPicksIndexView(TestCase, WagtailTestUtils):
def setUp(self):
self.login()
def test_simple(self):
response = self.client.get(reverse('wagtailsearch_editorspicks_index'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearch/editorspicks/index.html')
def test_search(self):
response = self.client.get(reverse('wagtailsearch_editorspicks_index'), {'q': "Hello"})
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['query_string'], "Hello")
def make_editors_picks(self):
for i in range(50):
models.EditorsPick.objects.create(
query=models.Query.get("query " + str(i)),
page_id=1,
sort_order=0,
description="First editors pick",
)
def test_pagination(self):
self.make_editors_picks()
response = self.client.get(reverse('wagtailsearch_editorspicks_index'), {'p': 2})
# Check response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearch/editorspicks/index.html')
# Check that we got the correct page
self.assertEqual(response.context['queries'].number, 2)
def test_pagination_invalid(self):
self.make_editors_picks()
response = self.client.get(reverse('wagtailsearch_editorspicks_index'), {'p': 'Hello World!'})
# Check response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearch/editorspicks/index.html')
# Check that we got page one
self.assertEqual(response.context['queries'].number, 1)
def test_pagination_out_of_range(self):
self.make_editors_picks()
response = self.client.get(reverse('wagtailsearch_editorspicks_index'), {'p': 99999})
# Check response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearch/editorspicks/index.html')
# Check that we got the last page
self.assertEqual(response.context['queries'].number, response.context['queries'].paginator.num_pages)
class TestEditorsPicksAddView(TestCase, WagtailTestUtils):
def setUp(self):
self.login()
def test_simple(self):
response = self.client.get(reverse('wagtailsearch_editorspicks_add'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearch/editorspicks/add.html')
def test_post(self):
# Submit
post_data = {
'query_string': "test",
'editors_picks-TOTAL_FORMS': 1,
'editors_picks-INITIAL_FORMS': 0,
'editors_picks-MAX_NUM_FORMS': 1000,
'editors_picks-0-DELETE': '',
'editors_picks-0-ORDER': 0,
'editors_picks-0-page': 1,
'editors_picks-0-description': "Hello",
}
response = self.client.post(reverse('wagtailsearch_editorspicks_add'), post_data)
# User should be redirected back to the index
self.assertRedirects(response, reverse('wagtailsearch_editorspicks_index'))
# Check that the editors pick was created
self.assertTrue(models.Query.get('test').editors_picks.filter(page_id=1).exists())
def test_post_without_recommendations(self):
# Submit
post_data = {
'query_string': "test",
'editors_picks-TOTAL_FORMS': 0,
'editors_picks-INITIAL_FORMS': 0,
'editors_picks-MAX_NUM_FORMS': 1000,
}
response = self.client.post(reverse('wagtailsearch_editorspicks_add'), post_data)
# User should be given an error
self.assertEqual(response.status_code, 200)
self.assertFormsetError(response, 'editors_pick_formset', None, None, "Please specify at least one recommendation for this search term.")
class TestEditorsPicksEditView(TestCase, WagtailTestUtils):
def setUp(self):
self.login()
# Create an editors pick to edit
self.query = models.Query.get("Hello")
self.editors_pick = self.query.editors_picks.create(page_id=1, description="Root page")
self.editors_pick_2 = self.query.editors_picks.create(page_id=2, description="Homepage")
def test_simple(self):
response = self.client.get(reverse('wagtailsearch_editorspicks_edit', args=(self.query.id, )))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearch/editorspicks/edit.html')
def test_post(self):
# Submit
post_data = {
'query_string': "Hello",
'editors_picks-TOTAL_FORMS': 2,
'editors_picks-INITIAL_FORMS': 2,
'editors_picks-MAX_NUM_FORMS': 1000,
'editors_picks-0-id': self.editors_pick.id,
'editors_picks-0-DELETE': '',
'editors_picks-0-ORDER': 0,
'editors_picks-0-page': 1,
'editors_picks-0-description': "Description has changed", # Change
'editors_picks-1-id': self.editors_pick_2.id,
'editors_picks-1-DELETE': '',
'editors_picks-1-ORDER': 1,
'editors_picks-1-page': 2,
'editors_picks-1-description': "Homepage",
}
response = self.client.post(reverse('wagtailsearch_editorspicks_edit', args=(self.query.id, )), post_data)
# User should be redirected back to the index
self.assertRedirects(response, reverse('wagtailsearch_editorspicks_index'))
# Check that the editors pick description was edited
self.assertEqual(models.EditorsPick.objects.get(id=self.editors_pick.id).description, "Description has changed")
def test_post_reorder(self):
# Check order before reordering
self.assertEqual(models.Query.get("Hello").editors_picks.all()[0], self.editors_pick)
self.assertEqual(models.Query.get("Hello").editors_picks.all()[1], self.editors_pick_2)
# Submit
post_data = {
'query_string': "Hello",
'editors_picks-TOTAL_FORMS': 2,
'editors_picks-INITIAL_FORMS': 2,
'editors_picks-MAX_NUM_FORMS': 1000,
'editors_picks-0-id': self.editors_pick.id,
'editors_picks-0-DELETE': '',
'editors_picks-0-ORDER': 1, # Change
'editors_picks-0-page': 1,
'editors_picks-0-description': "Root page",
'editors_picks-1-id': self.editors_pick_2.id,
'editors_picks-1-DELETE': '',
'editors_picks-1-ORDER': 0, # Change
'editors_picks-1-page': 2,
'editors_picks-1-description': "Homepage",
}
response = self.client.post(reverse('wagtailsearch_editorspicks_edit', args=(self.query.id, )), post_data)
# User should be redirected back to the index
self.assertRedirects(response, reverse('wagtailsearch_editorspicks_index'))
# Check that the ordering has been saved correctly
self.assertEqual(models.EditorsPick.objects.get(id=self.editors_pick.id).sort_order, 1)
self.assertEqual(models.EditorsPick.objects.get(id=self.editors_pick_2.id).sort_order, 0)
# Check that the recommendations were reordered
self.assertEqual(models.Query.get("Hello").editors_picks.all()[0], self.editors_pick_2)
self.assertEqual(models.Query.get("Hello").editors_picks.all()[1], self.editors_pick)
def test_post_delete_recommendation(self):
# Submit
post_data = {
'query_string': "Hello",
'editors_picks-TOTAL_FORMS': 2,
'editors_picks-INITIAL_FORMS': 2,
'editors_picks-MAX_NUM_FORMS': 1000,
'editors_picks-0-id': self.editors_pick.id,
'editors_picks-0-DELETE': '',
'editors_picks-0-ORDER': 0,
'editors_picks-0-page': 1,
'editors_picks-0-description': "Root page",
'editors_picks-1-id': self.editors_pick_2.id,
'editors_picks-1-DELETE': 1,
'editors_picks-1-ORDER': 1,
'editors_picks-1-page': 2,
'editors_picks-1-description': "Homepage",
}
response = self.client.post(reverse('wagtailsearch_editorspicks_edit', args=(self.query.id, )), post_data)
# User should be redirected back to the index
self.assertRedirects(response, reverse('wagtailsearch_editorspicks_index'))
# Check that the recommendation was deleted
self.assertFalse(models.EditorsPick.objects.filter(id=self.editors_pick_2.id).exists())
# The other recommendation should still exist
self.assertTrue(models.EditorsPick.objects.filter(id=self.editors_pick.id).exists())
def test_post_without_recommendations(self):
# Submit
post_data = {
'query_string': "Hello",
'editors_picks-TOTAL_FORMS': 2,
'editors_picks-INITIAL_FORMS': 2,
'editors_picks-MAX_NUM_FORMS': 1000,
'editors_picks-0-id': self.editors_pick.id,
'editors_picks-0-DELETE': 1,
'editors_picks-0-ORDER': 0,
'editors_picks-0-page': 1,
'editors_picks-0-description': "Description has changed", # Change
'editors_picks-1-id': self.editors_pick_2.id,
'editors_picks-1-DELETE': 1,
'editors_picks-1-ORDER': 1,
'editors_picks-1-page': 2,
'editors_picks-1-description': "Homepage",
}
response = self.client.post(reverse('wagtailsearch_editorspicks_edit', args=(self.query.id, )), post_data)
# User should be given an error
self.assertEqual(response.status_code, 200)
self.assertFormsetError(response, 'editors_pick_formset', None, None, "Please specify at least one recommendation for this search term.")
class TestEditorsPicksDeleteView(TestCase, WagtailTestUtils):
def setUp(self):
self.login()
# Create an editors pick to delete
self.query = models.Query.get("Hello")
self.editors_pick = self.query.editors_picks.create(page_id=1, description="Root page")
self.editors_pick_2 = self.query.editors_picks.create(page_id=2, description="Homepage")
def test_simple(self):
response = self.client.get(reverse('wagtailsearch_editorspicks_delete', args=(self.query.id, )))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailsearch/editorspicks/confirm_delete.html')
def test_post(self):
# Submit
post_data = {
'foo': 'bar',
}
response = self.client.post(reverse('wagtailsearch_editorspicks_delete', args=(self.query.id, )), post_data)
# User should be redirected back to the index
self.assertRedirects(response, reverse('wagtailsearch_editorspicks_index'))
# Check that both recommendations were deleted
self.assertFalse(models.EditorsPick.objects.filter(id=self.editors_pick_2.id).exists())
# The other recommendation should still exist
self.assertFalse(models.EditorsPick.objects.filter(id=self.editors_pick.id).exists())

View File

@ -1,13 +1,8 @@
from django.conf.urls import url
from wagtail.wagtailsearch.views import editorspicks, queries
from wagtail.wagtailsearch.views import queries
urlpatterns = [
url(r"^editorspicks/$", editorspicks.index, name="wagtailsearch_editorspicks_index"),
url(r"^editorspicks/add/$", editorspicks.add, name="wagtailsearch_editorspicks_add"),
url(r"^editorspicks/(\d+)/$", editorspicks.edit, name="wagtailsearch_editorspicks_edit"),
url(r"^editorspicks/(\d+)/delete/$", editorspicks.delete, name="wagtailsearch_editorspicks_delete"),
url(r"^queries/chooser/$", queries.chooser, name="wagtailsearch_queries_chooser"),
url(r"^queries/chooser/results/$", queries.chooserresults, name="wagtailsearch_queries_chooserresults"),
]

View File

@ -1,25 +1,11 @@
from django.core import urlresolvers
from django.conf.urls import include, url
from django.utils.translation import ugettext_lazy as _
from wagtail.wagtailcore import hooks
from wagtail.wagtailsearch.urls import admin as admin_urls
from wagtail.wagtailadmin.menu import MenuItem
@hooks.register('register_admin_urls')
def register_admin_urls():
return [
url(r'^search/', include(admin_urls)),
]
class EditorsPicksMenuItem(MenuItem):
def is_shown(self, request):
# TEMPORARY: Only show if the user is a superuser
return request.user.is_superuser
@hooks.register('register_settings_menu_item')
def register_editors_picks_menu_item():
return EditorsPicksMenuItem(_('Promoted search results'), urlresolvers.reverse('wagtailsearch_editorspicks_index'), classnames='icon icon-pick', order=900)