Skip to content

Commit 5d93f0d

Browse files
committed
spike a customizable page
1 parent 410b416 commit 5d93f0d

File tree

6 files changed

+276
-1
lines changed

6 files changed

+276
-1
lines changed

pages/custom_blocks.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,41 @@
88
from api.serializers import ImageSerializer
99
from openstax.functions import build_image_url, build_document_url
1010

11+
class HeroBlock(blocks.StructBlock):
12+
heading = blocks.CharBlock(required=True)
13+
sub_heading = blocks.CharBlock(required=False)
14+
description = blocks.RichTextBlock(required=False)
15+
image = ImageChooserBlock(required=False)
16+
primary_cta_text = blocks.CharBlock(required=False)
17+
primary_cta_link = blocks.URLBlock(required=False)
18+
secondary_cta_text = blocks.CharBlock(required=False)
19+
secondary_cta_link = blocks.URLBlock(required=False)
20+
21+
class Meta:
22+
icon = 'image'
23+
24+
class CardBlock(blocks.StructBlock):
25+
STYLE_CHOICES = [
26+
('rounded', 'Rounded'),
27+
('square', 'Square'),
28+
]
29+
style = blocks.ChoiceBlock(choices=STYLE_CHOICES, default='rounded')
30+
heading = blocks.CharBlock(required=True)
31+
description = blocks.RichTextBlock(required=True)
32+
link = blocks.URLBlock(required=False)
33+
cta = blocks.CharBlock(required=False)
34+
image = ImageChooserBlock(required=False)
35+
36+
class Meta:
37+
icon = 'form'
38+
39+
class SectionBlock(blocks.StructBlock):
40+
heading = blocks.CharBlock(required=False)
41+
html = blocks.RawHTMLBlock(required=False)
42+
cards = blocks.ListBlock(CardBlock(required=False))
43+
44+
class Meta:
45+
icon = 'table'
1146

1247
class ImageFormatChoiceBlock(FieldBlock):
1348
field = forms.ChoiceField(choices=(
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Generated by Django 5.0.4 on 2024-06-27 23:30
2+
3+
import django.db.models.deletion
4+
import pages.custom_blocks
5+
import wagtail.blocks
6+
import wagtail.fields
7+
import wagtail.images.blocks
8+
from django.db import migrations, models
9+
10+
11+
class Migration(migrations.Migration):
12+
13+
dependencies = [
14+
("pages", "0083_alter_k12mainpage_testimonials"),
15+
("wagtailcore", "0093_uploadedfile"),
16+
]
17+
18+
operations = [
19+
migrations.CreateModel(
20+
name="CustomizablePage",
21+
fields=[
22+
(
23+
"page_ptr",
24+
models.OneToOneField(
25+
auto_created=True,
26+
on_delete=django.db.models.deletion.CASCADE,
27+
parent_link=True,
28+
primary_key=True,
29+
serialize=False,
30+
to="wagtailcore.page",
31+
),
32+
),
33+
(
34+
"body",
35+
wagtail.fields.StreamField(
36+
[
37+
(
38+
"hero",
39+
wagtail.blocks.StructBlock(
40+
[
41+
("heading", wagtail.blocks.CharBlock(required=True)),
42+
("sub_heading", wagtail.blocks.CharBlock(required=False)),
43+
("description", wagtail.blocks.RichTextBlock(required=False)),
44+
("image", wagtail.images.blocks.ImageChooserBlock(required=False)),
45+
("primary_cta_text", wagtail.blocks.CharBlock(required=False)),
46+
("primary_cta_link", wagtail.blocks.URLBlock(required=False)),
47+
("secondary_cta_text", wagtail.blocks.CharBlock(required=False)),
48+
("secondary_cta_link", wagtail.blocks.URLBlock(required=False)),
49+
]
50+
),
51+
),
52+
(
53+
"section",
54+
wagtail.blocks.StructBlock(
55+
[
56+
("heading", wagtail.blocks.CharBlock(required=False)),
57+
("html", wagtail.blocks.RawHTMLBlock(required=False)),
58+
(
59+
"cards",
60+
wagtail.blocks.ListBlock(
61+
wagtail.blocks.StructBlock(
62+
[
63+
(
64+
"style",
65+
wagtail.blocks.ChoiceBlock(
66+
choices=[("rounded", "Rounded"), ("square", "Square")]
67+
),
68+
),
69+
("heading", wagtail.blocks.CharBlock(required=True)),
70+
("description", wagtail.blocks.RichTextBlock(required=True)),
71+
("link", wagtail.blocks.URLBlock(required=False)),
72+
("cta", wagtail.blocks.CharBlock(required=False)),
73+
(
74+
"image",
75+
wagtail.images.blocks.ImageChooserBlock(required=False),
76+
),
77+
],
78+
required=False,
79+
)
80+
),
81+
),
82+
]
83+
),
84+
),
85+
("paragraph", wagtail.blocks.RichTextBlock()),
86+
("image", pages.custom_blocks.APIImageChooserBlock()),
87+
("html", wagtail.blocks.RawHTMLBlock()),
88+
]
89+
),
90+
),
91+
],
92+
options={
93+
"abstract": False,
94+
},
95+
bases=("wagtailcore.page",),
96+
),
97+
]
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by Django 5.0.4 on 2024-06-27 23:30
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
("pages", "0084_customizablepage"),
11+
("snippets", "0037_pagelayout"),
12+
]
13+
14+
operations = [
15+
migrations.AddField(
16+
model_name="customizablepage",
17+
name="page_layout",
18+
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to="snippets.pagelayout"),
19+
),
20+
]

pages/models.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,49 @@
3030
InfoBoxBlock, \
3131
TestimonialBlock, \
3232
AllyLogoBlock, \
33-
AssignableBookBlock
33+
AssignableBookBlock, \
34+
CardBlock, \
35+
SectionBlock, \
36+
HeroBlock
3437

3538
from .custom_fields import \
3639
Group
3740
import snippets.models as snippets
3841

3942

43+
class CustomizablePage(Page):
44+
page_layout = models.ForeignKey(snippets.PageLayout, on_delete=models.PROTECT)
45+
body = StreamField([
46+
('hero', HeroBlock()),
47+
('section', SectionBlock()),
48+
('paragraph', blocks.RichTextBlock()),
49+
('image', APIImageChooserBlock()),
50+
('html', blocks.RawHTMLBlock()),
51+
('paragraph', blocks.RichTextBlock()),
52+
('image', APIImageChooserBlock()),
53+
], use_json_field=True)
54+
55+
api_fields = [
56+
APIField('page_layout'),
57+
APIField('body'),
58+
APIField('slug'),
59+
APIField('seo_title'),
60+
APIField('search_description'),
61+
]
62+
63+
content_panels = [
64+
TitleFieldPanel('title'),
65+
FieldPanel('page_layout'),
66+
FieldPanel('body'),
67+
]
68+
69+
promote_panels = [
70+
FieldPanel('slug', widget=SlugInput),
71+
FieldPanel('seo_title'),
72+
FieldPanel('search_description'),
73+
]
74+
75+
4076
class AboutUsPage(Page):
4177
who_heading = models.CharField(max_length=255)
4278
who_paragraph = models.TextField()
@@ -480,6 +516,7 @@ class Meta:
480516
'books.BookIndex',
481517
'news.NewsIndex',
482518
'news.PressIndex',
519+
'pages.CustomizablePage',
483520
]
484521

485522
max_count = 1
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Generated by Django 5.0.4 on 2024-06-27 23:30
2+
3+
import django.db.models.deletion
4+
import uuid
5+
from django.db import migrations, models
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
("snippets", "0036_contentwarning"),
12+
("wagtailcore", "0093_uploadedfile"),
13+
("wagtailimages", "0026_delete_uploadedimage"),
14+
]
15+
16+
operations = [
17+
migrations.CreateModel(
18+
name="PageLayout",
19+
fields=[
20+
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
21+
("translation_key", models.UUIDField(default=uuid.uuid4, editable=False)),
22+
(
23+
"layout",
24+
models.CharField(
25+
choices=[("default", "Default"), ("splash_image", "Splash Image")],
26+
default="default",
27+
max_length=255,
28+
),
29+
),
30+
(
31+
"background_image",
32+
models.ForeignKey(
33+
blank=True,
34+
null=True,
35+
on_delete=django.db.models.deletion.SET_NULL,
36+
related_name="+",
37+
to="wagtailimages.image",
38+
),
39+
),
40+
(
41+
"locale",
42+
models.ForeignKey(
43+
editable=False,
44+
on_delete=django.db.models.deletion.PROTECT,
45+
related_name="+",
46+
to="wagtailcore.locale",
47+
),
48+
),
49+
],
50+
options={
51+
"abstract": False,
52+
"unique_together": {("translation_key", "locale")},
53+
},
54+
),
55+
]

snippets/models.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,3 +501,34 @@ def __str__(self):
501501

502502

503503
register_snippet(ContentWarning)
504+
505+
class PageLayout(TranslatableMixin, models.Model):
506+
LAYOUT_CHOICES = (
507+
('default', 'Default'),
508+
('splash_image', 'Splash Image'),
509+
)
510+
layout = models.CharField(max_length=255, choices=LAYOUT_CHOICES, default='default')
511+
background_image = models.ForeignKey(
512+
'wagtailimages.Image',
513+
null=True,
514+
blank=True,
515+
on_delete=models.SET_NULL,
516+
related_name='+'
517+
)
518+
519+
@property
520+
def background_image_api_response(self):
521+
return build_image_url(self.background_image)
522+
523+
api_fields = ('layout', 'background_image_api_response')
524+
525+
panels = [
526+
FieldPanel('layout'),
527+
FieldPanel('background_image')
528+
]
529+
530+
def __str__(self):
531+
return self.layout
532+
533+
534+
register_snippet(PageLayout)

0 commit comments

Comments
 (0)