Skip to content

Commit 58e7c0c

Browse files
committed
Merge branch 'dev' into release/2
2 parents 7216055 + 3482099 commit 58e7c0c

39 files changed

Lines changed: 49129 additions & 213 deletions

coderedcms/blocks/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
CardBlock,
4242
CarouselBlock,
4343
ContentWallBlock,
44+
FilmStripBlock,
4445
ImageGalleryBlock,
4546
ModalBlock,
4647
NavDocumentLinkWithSubLinkBlock,
@@ -89,6 +90,7 @@
8990
("accordion", AccordionBlock()),
9091
("card", CardBlock()),
9192
("carousel", CarouselBlock()),
93+
("film_strip", FilmStripBlock()),
9294
("image_gallery", ImageGalleryBlock()),
9395
("modal", ModalBlock(HTML_STREAMBLOCKS)),
9496
("pricelist", PriceListBlock()),

coderedcms/blocks/content_blocks.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,19 @@ class Meta:
8080
template = "coderedcms/blocks/carousel_block.html"
8181

8282

83+
class FilmStripBlock(BaseBlock):
84+
"""
85+
Enables choosing a Film Strip Snippet.
86+
"""
87+
88+
film_strip = SnippetChooserBlock("coderedcms.FilmStrip")
89+
90+
class Meta:
91+
icon = "image"
92+
label = _("Film Strip")
93+
template = "coderedcms/blocks/film_strip_block.html"
94+
95+
8396
class ImageGalleryBlock(BaseBlock):
8497
"""
8598
Show a collection of images with interactive previews that expand to
@@ -204,7 +217,7 @@ class NavSubLinkBlock(BaseBlock):
204217

205218
class NavExternalLinkWithSubLinkBlock(NavSubLinkBlock, NavExternalLinkBlock):
206219
"""
207-
Extermal link with option for sub-links.
220+
External link with option for sub-links.
208221
"""
209222

210223
class Meta:

coderedcms/blocks/html_blocks.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ class PageListBlock(BaseBlock):
228228
label=_("Classified as"),
229229
help_text=_("Only show pages that are classified with this term."),
230230
)
231+
# DEPRECATED: Remove in 3.0
231232
show_preview = blocks.BooleanBlock(
232233
required=False,
233234
default=False,
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# Generated by Django 4.1.7 on 2023-03-30 19:14
2+
3+
import coderedcms.fields
4+
from django.db import migrations, models
5+
import django.db.models.deletion
6+
import modelcluster.fields
7+
8+
9+
class Migration(migrations.Migration):
10+
dependencies = [
11+
("wagtailimages", "0025_alter_image_file_alter_rendition_file"),
12+
("coderedcms", "0035_remove_googleapisettings_site_and_more"),
13+
]
14+
15+
operations = [
16+
migrations.CreateModel(
17+
name="FilmStrip",
18+
fields=[
19+
(
20+
"id",
21+
models.AutoField(
22+
auto_created=True,
23+
primary_key=True,
24+
serialize=False,
25+
verbose_name="ID",
26+
),
27+
),
28+
("name", models.CharField(max_length=255, verbose_name="Name")),
29+
],
30+
options={
31+
"verbose_name": "Film Strip",
32+
},
33+
),
34+
migrations.CreateModel(
35+
name="FilmPanel",
36+
fields=[
37+
(
38+
"id",
39+
models.AutoField(
40+
auto_created=True,
41+
primary_key=True,
42+
serialize=False,
43+
verbose_name="ID",
44+
),
45+
),
46+
(
47+
"sort_order",
48+
models.IntegerField(blank=True, editable=False, null=True),
49+
),
50+
(
51+
"background_color",
52+
models.CharField(
53+
blank=True,
54+
help_text="Hexadecimal, rgba, or CSS color notation (e.g. #ff0011)",
55+
max_length=255,
56+
verbose_name="Background color",
57+
),
58+
),
59+
(
60+
"foreground_color",
61+
models.CharField(
62+
blank=True,
63+
help_text="Hexadecimal, rgba, or CSS color notation (e.g. #ff0011)",
64+
max_length=255,
65+
verbose_name="Text color",
66+
),
67+
),
68+
(
69+
"custom_css_class",
70+
models.CharField(
71+
blank=True,
72+
max_length=255,
73+
verbose_name="Custom CSS class",
74+
),
75+
),
76+
(
77+
"custom_id",
78+
models.CharField(
79+
blank=True, max_length=255, verbose_name="Custom ID"
80+
),
81+
),
82+
(
83+
"content",
84+
coderedcms.fields.CoderedStreamField(
85+
blank=True, use_json_field=True
86+
),
87+
),
88+
(
89+
"background_image",
90+
models.ForeignKey(
91+
blank=True,
92+
null=True,
93+
on_delete=django.db.models.deletion.SET_NULL,
94+
related_name="+",
95+
to="wagtailimages.image",
96+
verbose_name="Background image",
97+
),
98+
),
99+
(
100+
"film_strip",
101+
modelcluster.fields.ParentalKey(
102+
on_delete=django.db.models.deletion.CASCADE,
103+
related_name="film_panels",
104+
to="coderedcms.filmstrip",
105+
verbose_name="Film Panel",
106+
),
107+
),
108+
],
109+
options={
110+
"verbose_name": "Film Panel",
111+
},
112+
),
113+
]
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Generated by Django 4.1.8 on 2023-04-13 21:36
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
dependencies = [
9+
("coderedcms", "0036_filmstrip_filmpanel"),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name="coderedpage",
15+
name="related_classifier_term",
16+
field=models.ForeignKey(
17+
blank=True,
18+
help_text="When getting related pages, pages with this term will be weighted over other classifier terms. By default, pages with the greatest number of classifiers in common are ranked highest.",
19+
null=True,
20+
on_delete=django.db.models.deletion.SET_NULL,
21+
related_name="+",
22+
to="coderedcms.classifierterm",
23+
verbose_name="Preferred related classifier term",
24+
),
25+
),
26+
migrations.AddField(
27+
model_name="coderedpage",
28+
name="related_num",
29+
field=models.PositiveIntegerField(
30+
default=3, verbose_name="Number of related pages to show"
31+
),
32+
),
33+
migrations.AddField(
34+
model_name="coderedpage",
35+
name="related_show",
36+
field=models.BooleanField(
37+
default=False, verbose_name="Show list of related pages"
38+
),
39+
),
40+
]

coderedcms/models/page_models.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,12 @@ class Meta:
146146
# ajax_template = ''
147147
# search_template = ''
148148

149+
# Template used in site search results.
150+
search_template = "coderedcms/pages/search_result.html"
151+
152+
# Template used for related pages, Latest Pages block, and Page Preview block.
153+
miniview_template = "coderedcms/pages/page.mini.html"
154+
149155
###############
150156
# Content fields
151157
###############
@@ -217,6 +223,41 @@ class Meta:
217223
help_text=_("Enable filtering child pages by these classifiers."),
218224
)
219225

226+
#####################
227+
# Related Page Fields
228+
#####################
229+
230+
# Subclasses can override this to query on a specific page model, in the
231+
# format "appname.Model". By default sibling pages are used.
232+
related_query_pagemodel = None
233+
234+
# Subclasses can override this to enabled related pages by default.
235+
related_show_default = False
236+
237+
related_show = models.BooleanField(
238+
default=related_show_default,
239+
verbose_name=_("Show list of related pages"),
240+
)
241+
242+
related_num = models.PositiveIntegerField(
243+
default=3,
244+
verbose_name=_("Number of related pages to show"),
245+
)
246+
247+
related_classifier_term = models.ForeignKey(
248+
"coderedcms.ClassifierTerm",
249+
blank=True,
250+
null=True,
251+
on_delete=models.SET_NULL,
252+
related_name="+",
253+
verbose_name=_("Preferred related classifier term"),
254+
help_text=_(
255+
"When getting related pages, pages with this term will be "
256+
"weighted over other classifier terms. By default, pages with "
257+
"the greatest number of classifiers in common are ranked highest."
258+
),
259+
)
260+
220261
###############
221262
# Layout fields
222263
###############
@@ -311,6 +352,14 @@ class Meta:
311352
],
312353
heading=_("Show Child Pages"),
313354
),
355+
MultiFieldPanel(
356+
[
357+
FieldPanel("related_show"),
358+
FieldPanel("related_num"),
359+
FieldPanel("related_classifier_term"),
360+
],
361+
heading=_("Related Pages"),
362+
),
314363
]
315364

316365
promote_panels = SeoMixin.seo_meta_panels + SeoMixin.seo_struct_panels
@@ -339,6 +388,7 @@ def __init__(self, *args, **kwargs):
339388
if not self.id:
340389
self.index_order_by = self.index_order_by_default
341390
self.index_show_subpages = self.index_show_subpages_default
391+
self.related_show = self.related_show_default
342392

343393
@cached_classmethod
344394
def get_edit_handler(cls):
@@ -455,6 +505,60 @@ def get_index_children(self):
455505

456506
return query
457507

508+
def get_related_pages(
509+
self, pagetype: str = None, num: int = None
510+
) -> models.QuerySet:
511+
"""
512+
Returns a queryset of sibling pages, or the model type
513+
defined by ``pagetype`` or ``self.related_query_pagemodel``,
514+
ordered by number of shared classifier terms.
515+
516+
:param str pagetype: The model type to query on. This should
517+
be a string in the format "appname.Model".
518+
Overrides ``self.related_query_pagemodel``
519+
520+
:param int num: The number of results to return.
521+
Overrides ``self.related_num``.
522+
"""
523+
524+
if pagetype is None:
525+
pagetype = self.related_query_pagemodel
526+
527+
if num is None:
528+
num = self.related_num
529+
530+
# Get our related query model, and queryset.
531+
if pagetype:
532+
querymodel = resolve_model_string(pagetype, self._meta.app_label)
533+
r_qs = querymodel.objects.all().live()
534+
else:
535+
r_qs = self.get_parent().specific.get_index_children()
536+
537+
# Exclude self to avoid infinite recursion.
538+
r_qs = r_qs.exclude(pk=self.pk)
539+
540+
order_by = []
541+
542+
# If we have a preferred classifier term, order by that.
543+
if self.related_classifier_term:
544+
p_ct_q = models.Q(classifier_terms=self.related_classifier_term)
545+
r_qs = r_qs.annotate(p_ct=p_ct_q)
546+
order_by.append("-p_ct")
547+
548+
# If this page has a classifier, then order by number of
549+
# shared classifier terms.
550+
if self.classifier_terms.exists():
551+
r_ct_q = models.Q(classifier_terms__in=self.classifier_terms.all())
552+
r_qs = r_qs.annotate(r_ct=models.Count("classifier_terms", r_ct_q))
553+
order_by.append("-r_ct")
554+
555+
# Order the related pages, then add distinct to deal with
556+
# annotating based on a many to many.
557+
if order_by:
558+
r_qs = r_qs.order_by(*order_by).distinct()
559+
560+
return r_qs[:num]
561+
458562
def get_content_walls(self, check_child_setting=True):
459563
current_content_walls = []
460564
if check_child_setting:
@@ -478,6 +582,7 @@ def get_context(self, request, *args, **kwargs):
478582
"""
479583
context = super().get_context(request)
480584

585+
# Show list of child pages.
481586
if self.index_show_subpages:
482587
# Get child pages
483588
all_children = self.get_index_children()
@@ -528,9 +633,16 @@ def get_context(self, request, *args, **kwargs):
528633

529634
context["index_paginated"] = paged_children
530635
context["index_children"] = all_children
636+
637+
# Show a list of related pages.
638+
if self.related_show:
639+
context["related_pages"] = self.get_related_pages()
640+
641+
# Content walls.
531642
context["content_walls"] = self.get_content_walls(
532643
check_child_setting=False
533644
)
645+
534646
return context
535647

536648

@@ -593,6 +705,9 @@ class Meta:
593705
abstract = True
594706

595707
template = "coderedcms/pages/article_page.html"
708+
search_template = "coderedcms/pages/article_page.search.html"
709+
710+
related_show_default = True
596711

597712
# Override body to provide simpler content
598713
body = StreamField(
@@ -1593,6 +1708,7 @@ class Meta:
15931708
abstract = True
15941709

15951710
template = "coderedcms/pages/form_page.html"
1711+
miniview_template = "coderedcms/pages/form_page.mini.html"
15961712
landing_page_template = "coderedcms/pages/form_page_landing.html"
15971713

15981714
base_form_class = WagtailAdminFormPageForm

0 commit comments

Comments
 (0)