224 lines
6.8 KiB
Python
224 lines
6.8 KiB
Python
from django.contrib.auth.models import Group
|
|
from django.db import models
|
|
from django.forms import CheckboxSelectMultiple
|
|
from wagtail import blocks
|
|
from wagtail.admin.panels import FieldPanel
|
|
from wagtail.fields import RichTextField, StreamField
|
|
from wagtail.images.blocks import ImageBlock
|
|
from wagtail.models import Page
|
|
from wagtail.models.copying import ParentalManyToManyField
|
|
|
|
from purchase.models import CoursePurchase
|
|
|
|
|
|
class EmptyPage(Page):
|
|
pass
|
|
|
|
|
|
class HomePage(Page):
|
|
body = RichTextField(blank=True)
|
|
|
|
content_panels = Page.content_panels + ["body"]
|
|
|
|
|
|
class BlogIndexPage(Page):
|
|
subpage_types = ["home.BlogPage"]
|
|
|
|
|
|
class CourseIndexPage(Page):
|
|
subpage_types = ["home.CoursePage"]
|
|
|
|
def get_context(self, request):
|
|
context = super().get_context(request)
|
|
all_courses = self.get_children().live()
|
|
purchased_courses = set()
|
|
other_courses = set()
|
|
|
|
for course in all_courses:
|
|
if course.specific._user_has_access(request.user):
|
|
purchased_courses.add(course)
|
|
else:
|
|
other_courses.add(course)
|
|
|
|
context["purchased_courses"] = sorted(
|
|
purchased_courses, key=lambda c: c.title.lower()
|
|
)
|
|
context["other_courses"] = sorted(other_courses, key=lambda c: c.title.lower())
|
|
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):
|
|
course_image = models.ForeignKey(
|
|
"wagtailimages.Image",
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name="+",
|
|
)
|
|
description = models.CharField(max_length=255, blank=True)
|
|
body = RichTextField(blank=True)
|
|
allowed_groups = ParentalManyToManyField(
|
|
Group,
|
|
related_name="course_pages",
|
|
help_text="Additional groups that should have access to this course, e.g. Editors. NOTE: Users who purchase the course will be automatically added to a dedicated access group for this course, so you don't need to add that group here.",
|
|
)
|
|
|
|
repository_url = models.URLField(
|
|
null=True,
|
|
blank=True,
|
|
)
|
|
|
|
def _user_has_access(self, user):
|
|
if not user.is_authenticated:
|
|
return False
|
|
|
|
user_group_ids = user.groups.values_list("id", flat=True)
|
|
if self.allowed_groups.filter(id__in=user_group_ids).exists():
|
|
return True
|
|
|
|
return CoursePurchase.objects.filter(
|
|
user=user, course=self, refunded=False
|
|
).exists()
|
|
|
|
def _user_purchase_id(self, user):
|
|
if not user.is_authenticated:
|
|
return None
|
|
|
|
purchase = CoursePurchase.objects.filter(
|
|
user=user, course=self, refunded=False
|
|
).first()
|
|
print(f"User {user} purchase for course {self}: {purchase}")
|
|
return purchase.id if purchase else None
|
|
|
|
def mock_purchase(self, user):
|
|
"""Mock method to simulate purchasing this course for a user."""
|
|
if not user.is_authenticated:
|
|
return False
|
|
obj, created = CoursePurchase.objects.get_or_create(
|
|
user=user, course=self, refunded=False
|
|
)
|
|
# Add user to dedicated access group for this course
|
|
group_name = f"course_{self.id}_access"
|
|
group, _ = Group.objects.get_or_create(name=group_name)
|
|
user.groups.add(group)
|
|
# Ensure allowed_groups only includes this access group
|
|
if not self.allowed_groups.filter(id=group.id).exists():
|
|
self.allowed_groups.add(group)
|
|
return created
|
|
|
|
def save(self, *args, **kwargs):
|
|
if self.id is not None:
|
|
group_name = f"course_{self.id}_access"
|
|
group, created = Group.objects.get_or_create(name=group_name)
|
|
if not self.allowed_groups.filter(id=group.id).exists():
|
|
self.allowed_groups.add(group)
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
def get_context(self, request):
|
|
context = super().get_context(request)
|
|
context["user_has_access"] = self._user_has_access(request.user)
|
|
context["user_purchase_id"] = self._user_purchase_id(request.user)
|
|
return context
|
|
|
|
content_panels = Page.content_panels + [
|
|
FieldPanel("course_image"),
|
|
FieldPanel("description"),
|
|
FieldPanel("body"),
|
|
FieldPanel("allowed_groups", widget=CheckboxSelectMultiple),
|
|
FieldPanel(
|
|
"repository_url",
|
|
read_only=True,
|
|
heading="Repository URL (auto-generated)",
|
|
),
|
|
]
|
|
parent_page_types = ["home.CourseIndexPage"]
|
|
subpage_types = ["home.CourseModulePage"]
|
|
|
|
|
|
class CourseModulePage(Page):
|
|
body = RichTextField(blank=True)
|
|
|
|
@property
|
|
def course(self):
|
|
if hasattr(self, "get_parent"):
|
|
parent = self.get_parent()
|
|
if parent and hasattr(parent, "specific"):
|
|
return parent.specific
|
|
return None
|
|
|
|
@property
|
|
def full_title(self):
|
|
course = self.course
|
|
if course:
|
|
return f"{course.title} - {self.title}"
|
|
return self.title
|
|
|
|
content_panels = Page.content_panels + ["body"]
|
|
subpage_types = ["home.ModuleLessonPage"]
|
|
parent_page_types = ["home.CoursePage"]
|
|
|
|
|
|
class ModuleLessonPage(Page):
|
|
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
|
|
def module(self):
|
|
if hasattr(self, "get_parent"):
|
|
parent = self.get_parent()
|
|
if parent and hasattr(parent, "specific"):
|
|
return parent.specific
|
|
return None
|
|
|
|
@property
|
|
def full_title(self):
|
|
module = self.module
|
|
if module:
|
|
return f"{module.full_title} - {self.title}"
|
|
return self.title
|
|
|
|
content_panels = Page.content_panels + [
|
|
FieldPanel("body"),
|
|
FieldPanel("create_gitea_repo"),
|
|
FieldPanel(
|
|
"gitea_repo_url",
|
|
read_only=True,
|
|
heading="Gitea Repository URL",
|
|
),
|
|
]
|
|
parent_page_types = ["home.CourseModulePage"]
|