5 Commits

3 changed files with 118 additions and 4 deletions

View File

@@ -0,0 +1,23 @@
# Generated by Django 6.0.3 on 2026-03-30 07:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('home', '0020_coursepage_repository_url_and_more'),
]
operations = [
migrations.AddField(
model_name='modulelessonpage',
name='create_gitea_repo',
field=models.BooleanField(default=False, help_text='If enabled, a Gitea repository will be automatically created for this module when the module is published.'),
),
migrations.AddField(
model_name='modulelessonpage',
name='gitea_repo_url',
field=models.URLField(blank=True, help_text="URL of the Gitea repository for this lesson (auto-generated if 'create_gitea_repo' is enabled)", null=True),
),
]

View File

@@ -0,0 +1,40 @@
# Generated by Django 6.0.3 on 2026-03-30 07:53
import django.db.models.deletion
import wagtail.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('home', '0021_modulelessonpage_create_gitea_repo_and_more'),
('wagtailcore', '0096_referenceindex_referenceindex_source_object_and_more'),
('wagtailimages', '0027_image_description'),
]
operations = [
migrations.CreateModel(
name='BlogIndexPage',
fields=[
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
],
options={
'abstract': False,
},
bases=('wagtailcore.page',),
),
migrations.CreateModel(
name='BlogPage',
fields=[
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
('author', models.CharField(max_length=255)),
('body', wagtail.fields.StreamField([('heading', 0), ('paragraph', 1), ('image', 2)], block_lookup={0: ('wagtail.blocks.CharBlock', (), {'form_classname': 'title'}), 1: ('wagtail.blocks.RichTextBlock', (), {}), 2: ('wagtail.images.blocks.ImageBlock', [], {})})),
('image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image')),
],
options={
'abstract': False,
},
bases=('wagtailcore.page',),
),
]

View File

@@ -1,15 +1,17 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from django.contrib.auth.models import Group, User
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import Group, User
from django.db import models from django.db import models
from django.forms import CheckboxSelectMultiple from django.forms import CheckboxSelectMultiple
from django.utils import timezone from django.utils import timezone
from modelcluster.contrib.taggit import ClusterTaggableManager from modelcluster.contrib.taggit import ClusterTaggableManager
from modelcluster.fields import ParentalKey from modelcluster.fields import ParentalKey
from taggit.models import TaggedItemBase from taggit.models import TaggedItemBase
from wagtail import blocks
from wagtail.admin.panels import FieldPanel from wagtail.admin.panels import FieldPanel
from wagtail.fields import RichTextField from wagtail.fields import RichTextField, StreamField
from wagtail.images.blocks import ImageBlock
from wagtail.models import Page from wagtail.models import Page
from wagtail.models.copying import ParentalManyToManyField from wagtail.models.copying import ParentalManyToManyField
from wagtail_color_panel.edit_handlers import NativeColorPanel from wagtail_color_panel.edit_handlers import NativeColorPanel
@@ -28,6 +30,10 @@ class HomePage(Page):
content_panels = Page.content_panels + ["body"] content_panels = Page.content_panels + ["body"]
class BlogIndexPage(Page):
subpage_types = ["home.BlogPage"]
class CourseIndexPage(Page): class CourseIndexPage(Page):
subpage_types = ["home.CoursePage"] subpage_types = ["home.CoursePage"]
@@ -50,6 +56,31 @@ class CourseIndexPage(Page):
return context return context
class BlogPage(Page):
author = models.CharField(max_length=255)
image = models.ForeignKey(
"wagtailimages.Image",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="+",
)
body = StreamField(
[
("heading", blocks.CharBlock(classname="title")),
("paragraph", blocks.RichTextBlock()),
("image", ImageBlock()),
]
)
content_panels = Page.content_panels + [
FieldPanel("author"),
FieldPanel("image"),
FieldPanel("body"),
]
parent_page_types = ["home.BlogIndexPage"]
class CoursePage(Page): class CoursePage(Page):
course_image = models.ForeignKey( course_image = models.ForeignKey(
"wagtailimages.Image", "wagtailimages.Image",
@@ -164,6 +195,15 @@ class CourseModulePage(Page):
class ModuleLessonPage(Page): class ModuleLessonPage(Page):
body = RichTextField(blank=True) body = RichTextField(blank=True)
create_gitea_repo = models.BooleanField(
default=False,
help_text="If enabled, a Gitea repository will be automatically created for this module when the module is published.",
)
gitea_repo_url = models.URLField(
null=True,
blank=True,
help_text="URL of the Gitea repository for this lesson (auto-generated if 'create_gitea_repo' is enabled)",
)
@property @property
def module(self): def module(self):
@@ -180,7 +220,15 @@ class ModuleLessonPage(Page):
return f"{module.full_title} - {self.title}" return f"{module.full_title} - {self.title}"
return self.title return self.title
content_panels = Page.content_panels + ["body"] content_panels = Page.content_panels + [
FieldPanel("body"),
FieldPanel("create_gitea_repo"),
FieldPanel(
"gitea_repo_url",
read_only=True,
heading="Gitea Repository URL (auto-generated if 'create_gitea_repo' is enabled)",
),
]
parent_page_types = ["home.CourseModulePage"] parent_page_types = ["home.CourseModulePage"]
@@ -271,6 +319,8 @@ class EventPage(Page):
Generate EventOccurrence objects for this event based on recurrence settings. Generate EventOccurrence objects for this event based on recurrence settings.
For endless recurrence, generate up to days_ahead into the future. For endless recurrence, generate up to days_ahead into the future.
""" """
from .event_occurrence import EventOccurrence
now = timezone.now() now = timezone.now()
if not self.recurrence_enabled: if not self.recurrence_enabled:
# if recurrence is not enabled, ensure there's at least one occurrence for the specified start/end # if recurrence is not enabled, ensure there's at least one occurrence for the specified start/end
@@ -320,6 +370,7 @@ class EventPage(Page):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
super().save(*args, **kwargs) super().save(*args, **kwargs)
if self.live:
self.generate_occurrences() self.generate_occurrences()
content_panels = Page.content_panels + [ content_panels = Page.content_panels + [