Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions coderedcms/migrations/0023_auto_20210908_1702.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 3.2.7 on 2021-09-08 21:02

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('coderedcms', '0022_auto_20210731_1853'),
]

operations = [
migrations.AddField(
model_name='coderedpage',
name='index_order_by_classifier',
field=models.ForeignKey(blank=True, help_text='Child pages will first be sorted following the order of this classifier’s terms (from Snippets > Classifiers).', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='coderedcms.classifier', verbose_name='Order child pages by classifier'),
),
migrations.AlterField(
model_name='coderedpage',
name='index_order_by',
field=models.CharField(blank=True, choices=[('', 'Default Ordering'), ('-first_published_at', 'Date first published, newest to oldest'), ('first_published_at', 'Date first published, oldest to newest'), ('-last_published_at', 'Date updated, newest to oldest'), ('last_published_at', 'Date updated, oldest to newest'), ('title', 'Title, alphabetical'), ('-title', 'Title, reverse alphabetical')], default='', help_text='Child pages will then be sorted by this attribute.', max_length=255, verbose_name='Order child pages by'),
),
]
39 changes: 38 additions & 1 deletion coderedcms/models/page_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,25 @@ class Meta:
default=index_show_subpages_default,
verbose_name=_('Show list of child pages')
)
index_order_by_classifier = models.ForeignKey(
'coderedcms.Classifier',
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name="+",
verbose_name=_('Order child pages by classifier'),
help_text=_(
'Child pages will first be sorted following the order of this '
'classifier’s terms (from Snippets > Classifiers).'
)
)
index_order_by = models.CharField(
max_length=255,
choices=index_order_by_choices,
default=index_order_by_default,
blank=True,
verbose_name=_('Order child pages by'),
help_text=_('Child pages will then be sorted by this attribute.')
)
index_num_per_page = models.PositiveIntegerField(
default=10,
Expand Down Expand Up @@ -306,6 +319,7 @@ class Meta:
[
FieldPanel('index_show_subpages'),
FieldPanel('index_num_per_page'),
FieldPanel('index_order_by_classifier'),
FieldPanel('index_order_by'),
FieldPanel('index_classifiers', widget=forms.CheckboxSelectMultiple()),
],
Expand Down Expand Up @@ -416,8 +430,31 @@ def get_index_children(self):
query = querymodel.objects.child_of(self).live()
else:
query = self.get_children().live()

# Determine query sorting order.
order = []

# To sort by term order of a specific classifier, annotate the child
# pages with the `sort_order` of its ClassifierTerms.
if self.index_order_by_classifier:
terms = ClassifierTerm.objects.filter(
classifier=self.index_order_by_classifier,
# Reverse ManyToMany of `coderedpage.classifier_terms`.
coderedpage=models.OuterRef("pk"),
)
query = query.annotate(
term_sort_order=models.Subquery(terms.values("sort_order"))
)
order.append("term_sort_order")

# Second, order by the specified model attribute.
if self.index_order_by:
return query.order_by(self.index_order_by)
order.append(self.index_order_by)

# Order the query.
if order:
query = query.order_by(*order)

return query

def get_content_walls(self, check_child_setting=True):
Expand Down
81 changes: 80 additions & 1 deletion coderedcms/models/tests/test_page_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
CoderedWebPage,
get_page_models
)
from coderedcms.models.snippet_models import Classifier, ClassifierTerm
from coderedcms.tests.testapp.models import (
ArticleIndexPage,
ArticlePage,
EventIndexPage,
EventPage,
FormPage,
IndexTestPage,
LocationIndexPage,
LocationPage,
StreamFormPage,
Expand All @@ -42,11 +44,13 @@ def setUp(self):
self.homepage = WebPage.objects.get(url_path='/home/')
self.homepage.add_child(instance=self.basic_page)

def tearDown(self):
self.basic_page.delete()

def test_get(self):
"""
Tests to make sure a basic version of the page serves a 200 from a GET request.
"""

response = self.client.get(self.basic_page.url, follow=True)
self.assertEqual(response.status_code, 200)

Expand Down Expand Up @@ -158,6 +162,9 @@ class CoderedStreamFormPageTestCase(AbstractPageTestCase, WagtailPageTests):
model = CoderedStreamFormPage


# -- CONCRETE PAGES ------------------------------------------------------------


class ArticlePageTestCase(ConcreteBasicPageTestCase, WagtailPageTests):
model = ArticlePage

Expand Down Expand Up @@ -192,3 +199,75 @@ class LocationPageTestCase(ConcreteBasicPageTestCase, WagtailPageTests):

class StreamFormPageTestCase(ConcreteFormPageTestCase, WagtailPageTests):
model = StreamFormPage


# -- PAGES FOR TESTING SPECIFIC FUNCTIONALITY ----------------------------------


class IndexTestCase(ConcreteBasicPageTestCase, WagtailPageTests):
"""
Tests indexing features (show/sort/filter child pages).
"""
model = IndexTestPage

def setUp(self):
super().setUp()

# Create some child pages under this page.
self.child_1 = WebPage(title=f"{self.basic_page.title} - Child 1")
self.basic_page.add_child(instance=self.child_1)
self.child_2 = WebPage(title=f"{self.basic_page.title} - Child 2")
self.basic_page.add_child(instance=self.child_2)
self.child_3 = WebPage(title=f"{self.basic_page.title} - Child 3")
self.basic_page.add_child(instance=self.child_3)

# Create some classifier terms for general purpose use.
self.classifier = Classifier.objects.create(name="Classifier")
self.term_a = ClassifierTerm.objects.create(
classifier=self.classifier,
name="Term A",
sort_order=0,
)
self.term_b = ClassifierTerm.objects.create(
classifier=self.classifier,
name="Term B",
sort_order=1,
)

def tearDown(self):
super().tearDown()
self.classifier.delete()

def test_get_index_children(self):
"""
Tests to make sure `get_index_children()` returns the correct queryset
based on selected page settings.
"""
# Test it without setting any options, ensure it is not broken.
children = self.basic_page.get_index_children()
self.assertIn(self.child_1, children)
self.assertIn(self.child_2, children)
self.assertIn(self.child_3, children)

# Test index_order_by returns in the correct order.
self.basic_page.index_order_by = "title"
self.basic_page.save()
children = self.basic_page.get_index_children()
self.assertEqual(self.child_1, children[0])
self.assertEqual(self.child_2, children[1])
self.assertEqual(self.child_3, children[2])

# Test index_order_by classifier returns in the correct order.
self.basic_page.index_order_by_classifier = self.classifier
self.basic_page.index_order_by = "title"
self.basic_page.save()
self.child_3.classifier_terms.add(self.term_a)
self.child_3.save()
self.child_1.classifier_terms.add(self.term_b)
self.child_1.save()
self.child_2.classifier_terms.add(self.term_b)
self.child_2.save()
children = self.basic_page.get_index_children()
self.assertEqual(self.child_3, children[0])
self.assertEqual(self.child_1, children[1])
self.assertEqual(self.child_2, children[2])
78 changes: 78 additions & 0 deletions coderedcms/tests/testapp/migrations/0005_auto_20210908_1741.py

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions coderedcms/tests/testapp/migrations/0006_indextestpage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 3.2.7 on 2021-09-08 21:41

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('coderedcms', '0023_auto_20210908_1702'),
('testapp', '0005_auto_20210908_1741'),
]

operations = [
migrations.CreateModel(
name='IndexTestPage',
fields=[
('coderedpage_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='coderedcms.coderedpage')),
],
options={
'verbose_name': 'Index Test Page',
},
bases=('coderedcms.coderedpage',),
),
]
25 changes: 24 additions & 1 deletion coderedcms/tests/testapp/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from coderedcms.models.page_models import CoderedPage
from modelcluster.fields import ParentalKey
from coderedcms.forms import CoderedFormField
from coderedcms.models import (
Expand Down Expand Up @@ -129,7 +130,8 @@ class Meta:
class LocationIndexPage(CoderedLocationIndexPage):
"""
A page that holds a list of locations and displays them with a Google Map.
This does require a Google Maps API Key that can be defined in Settings > Google API Settings
This does require a Google Maps API Key that can be defined in Settings >
Google API Settings
"""
class Meta:
verbose_name = 'Location Landing Page'
Expand All @@ -152,3 +154,24 @@ class Meta:

class StreamFormConfirmEmail(CoderedEmail):
page = ParentalKey('StreamFormPage', related_name='confirmation_emails')


"""
--------------------------------------------------------------------------------
CUSTOM PAGE TYPES for testing specific features. These should be based on
CoderedPage when testing CoderedPage-specific functionality (which is where most
of our logic lives).
--------------------------------------------------------------------------------
"""


class IndexTestPage(CoderedPage):
"""
Tests indexing features (show/sort/filter child pages).
"""
class Meta:
verbose_name = "Index Test Page"

index_query_pagemodel = "testapp.WebPage"

template = 'coderedcms/pages/base.html'
23 changes: 17 additions & 6 deletions docs/features/page_types/web_pages.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,27 @@ layout. You have the following options:

* **Template**: The template you want the page to use to render.

* **Show List of Child Pages**: Toggles whether this parent page should show a
* **Show list of child pages**: Toggles whether this parent page should show a
list of its children pages.

* **Number Per Page**: Controls how many children pages you want to show at
* **Number per page**: Controls how many children pages you want to show at
once.

* **Order Children Pages by**: Controls how the children pages are sorted on
this parent page.

* **Filter Child Pages by**: Using Classifier terms, control which children
* **Order child pages by classifier**: Child pages will first be sorted
following the order of this classifier's terms. For example, if this were set
to a classifier called "Status" (which contained terms "In Stock" and "Out of
Stock" in that respective order), then child pages that are "In Stock" would
be shown first before pages that are "Out of Stock".

* **Order child pages by**: Controls how the children pages are sorted on
this parent page. For example, if "Title, alphabetically" were selected here,
the pages would be listed A-Z. If **Order child pages by classifier** is set
(above) then this ordering will be applied after the classifier ordering.
Following the previous example, if "Status" classifier were set above, "In
Stock" items would be sorted A-Z, then "Out of Stock" items would be sorted
A-Z.

* **Filter child pages by**: Using Classifier terms, control which children
pages are shown on the parent page.

SEO Tab
Expand Down
4 changes: 4 additions & 0 deletions docs/releases/v0.22.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ New features

* Upgraded to Wagtail 2.14

* Can now order child pages by Classifiers, using the Classifier Term's
sort order, in addition to ordering by model attributes. Read details in
:doc:`/features/page_types/web_pages`.


Upgrade considerations
----------------------
Expand Down