Compare commits
5 Commits
main
...
feat/add-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
af55935651
|
|||
|
55e6a80c08
|
|||
|
f7b0bba3a4
|
|||
|
f0a58c46cb
|
|||
|
3a980a7a20
|
@@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
40
home/migrations/0022_blogindexpage_blogpage.py
Normal file
40
home/migrations/0022_blogindexpage_blogpage.py
Normal 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',),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -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 + [
|
||||||
|
|||||||
Reference in New Issue
Block a user