Compare commits
22 Commits
feat/creat
...
feat/add-m
| Author | SHA1 | Date | |
|---|---|---|---|
|
e503d69235
|
|||
|
57ec3162d0
|
|||
|
c4e9ec5484
|
|||
|
c8732a05cb
|
|||
|
6810e540e5
|
|||
|
21500e0f10
|
|||
|
be42d71bb8
|
|||
|
b5e9e1ec66
|
|||
|
d575c836e9
|
|||
|
84a6c4cf5e
|
|||
|
e46f034d9e
|
|||
|
dc7e34f5b6
|
|||
|
f002651e2a
|
|||
|
c789eeb4ff
|
|||
|
acb6ea58ce
|
|||
|
72fca4228c
|
|||
|
9f779407af
|
|||
|
f2f594afb6
|
|||
|
95ab896e5f
|
|||
|
4f58cb0320
|
|||
|
294ea9a28b
|
|||
|
e56aff1a5c
|
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-03-17 11:46+0000\n"
|
||||
"POT-Creation-Date: 2026-03-20 12:18+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -18,6 +18,79 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: home/templates/chat/admin/admin_chat.html:5
|
||||
msgid "Chat with"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/chat/admin/admin_chat.html:10
|
||||
msgid "Admin Chat View"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/chat/admin/admin_chat.html:11
|
||||
msgid ""
|
||||
"This is the admin view of the chat. Here you can manage conversations and "
|
||||
"monitor user interactions."
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/chat/admin/admin_chat.html:19
|
||||
#: home/templates/chat/user_chat.html:18
|
||||
msgid "No messages found."
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/chat/admin/admin_chat.html:24
|
||||
#: home/templates/chat/user_chat.html:23
|
||||
msgid "Type your message here..."
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/chat/admin/admin_chat.html:25
|
||||
#: home/templates/chat/user_chat.html:24
|
||||
msgid "Send"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/chat/admin/admin_chat_dashboard.html:5
|
||||
#: home/templates/chat/user_chat.html:5
|
||||
msgid "Chat"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/chat/admin/admin_chat_dashboard.html:10
|
||||
msgid "Admin Chat Dashboard"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/chat/admin/admin_chat_dashboard.html:19
|
||||
msgid "No active chats found."
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/chat/user_chat.html:9
|
||||
msgid "Chat with Support"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/chat/user_chat.html:10
|
||||
msgid ""
|
||||
"This is the user chat interface. Here you can communicate with our support "
|
||||
"team for assistance."
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/course_index_page.html:4
|
||||
#: home/templates/home/course_index_page.html:10
|
||||
msgid "Courses"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/course_index_page.html:12
|
||||
msgid "Purchased Courses"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/course_index_page.html:22
|
||||
msgid "Purchased"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/course_index_page.html:32
|
||||
msgid "Available Courses"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/course_index_page.html:42
|
||||
msgid "Not Purchased"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/course_module_page.html:21
|
||||
msgid "Lessons"
|
||||
msgstr ""
|
||||
@@ -26,36 +99,40 @@ msgstr ""
|
||||
msgid "No lessons yet."
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/course_page.html:26
|
||||
msgid "Modules"
|
||||
#: home/templates/home/course_page.html:31
|
||||
msgid "Refund Purchase"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/course_page.html:33
|
||||
msgid "Modules"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/course_page.html:40
|
||||
msgid "No modules yet."
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/course_page.html:39
|
||||
#: home/templates/home/course_page.html:46
|
||||
msgid ""
|
||||
"You need to be logged in to access this course. Please log in or sign up to "
|
||||
"view the modules."
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/course_page.html:40
|
||||
#: home/templates/home/course_page.html:47
|
||||
#: home/templates/home/event_page.html:40
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/course_page.html:41
|
||||
#: home/templates/home/course_page.html:48
|
||||
#: home/templates/home/event_page.html:43
|
||||
msgid "Sign Up"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/course_page.html:46
|
||||
#: home/templates/home/course_page.html:53
|
||||
msgid ""
|
||||
"You don't have access to this course. Please purchase it to view the modules."
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/course_page.html:47
|
||||
#: home/templates/home/course_page.html:54
|
||||
msgid "Purchase Course"
|
||||
msgstr ""
|
||||
|
||||
@@ -91,46 +168,3 @@ msgstr ""
|
||||
#: home/templates/home/event_page.html:54
|
||||
msgid "Sign Up for Event"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:6
|
||||
msgid "Visit the Wagtail website"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:15
|
||||
msgid "View the release notes"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:27
|
||||
msgid "Welcome to your new Wagtail site!"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:28
|
||||
msgid ""
|
||||
"Please feel free to <a href=\"https://github.com/wagtail/wagtail/wiki/"
|
||||
"Slack\">join our community on Slack</a>, or get started with one of the "
|
||||
"links below."
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:35
|
||||
msgid "Wagtail Documentation"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:36
|
||||
msgid "Topics, references, & how-tos"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:42
|
||||
msgid "Tutorial"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:43
|
||||
msgid "Build your first Wagtail site"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:49
|
||||
msgid "Admin Interface"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:50
|
||||
msgid "Create your superuser first!"
|
||||
msgstr ""
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-03-17 11:46+0000\n"
|
||||
"POT-Creation-Date: 2026-03-20 12:18+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -20,25 +20,102 @@ msgstr ""
|
||||
"(n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && "
|
||||
"n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
|
||||
|
||||
#: home/templates/chat/admin/admin_chat.html:5
|
||||
msgid "Chat with"
|
||||
msgstr "Czat z"
|
||||
|
||||
#: home/templates/chat/admin/admin_chat.html:10
|
||||
msgid "Admin Chat View"
|
||||
msgstr "Widok administratora czatu"
|
||||
|
||||
#: home/templates/chat/admin/admin_chat.html:11
|
||||
msgid ""
|
||||
"This is the admin view of the chat. Here you can manage conversations and "
|
||||
"monitor user interactions."
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/chat/admin/admin_chat.html:19
|
||||
#: home/templates/chat/user_chat.html:18
|
||||
msgid "No messages found."
|
||||
msgstr "Brak wiadomości."
|
||||
|
||||
#: home/templates/chat/admin/admin_chat.html:24
|
||||
#: home/templates/chat/user_chat.html:23
|
||||
msgid "Type your message here..."
|
||||
msgstr "Wiadomość..."
|
||||
|
||||
#: home/templates/chat/admin/admin_chat.html:25
|
||||
#: home/templates/chat/user_chat.html:24
|
||||
msgid "Send"
|
||||
msgstr "Wyślij"
|
||||
|
||||
#: home/templates/chat/admin/admin_chat_dashboard.html:5
|
||||
#: home/templates/chat/user_chat.html:5
|
||||
msgid "Chat"
|
||||
msgstr "Czat"
|
||||
|
||||
#: home/templates/chat/admin/admin_chat_dashboard.html:10
|
||||
msgid "Admin Chat Dashboard"
|
||||
msgstr "Panel administratora czatu"
|
||||
|
||||
#: home/templates/chat/admin/admin_chat_dashboard.html:19
|
||||
msgid "No active chats found."
|
||||
msgstr "Brak aktywnych czatów."
|
||||
|
||||
#: home/templates/chat/user_chat.html:9
|
||||
msgid "Chat with Support"
|
||||
msgstr "Czat z administracją"
|
||||
|
||||
#: home/templates/chat/user_chat.html:10
|
||||
msgid ""
|
||||
"This is the user chat interface. Here you can communicate with our support "
|
||||
"team for assistance."
|
||||
msgstr ""
|
||||
"To jest interfejs czatu dla użytkowników. Tutaj możesz komunikować się z "
|
||||
"naszym zespołem wsparcia w celu uzyskania pomocy."
|
||||
|
||||
#: home/templates/home/course_index_page.html:4
|
||||
#: home/templates/home/course_index_page.html:10
|
||||
msgid "Courses"
|
||||
msgstr "Kursy"
|
||||
|
||||
#: home/templates/home/course_index_page.html:12
|
||||
msgid "Purchased Courses"
|
||||
msgstr "Zakupione kursy"
|
||||
|
||||
#: home/templates/home/course_index_page.html:22
|
||||
msgid "Purchased"
|
||||
msgstr "Zakupiony"
|
||||
|
||||
#: home/templates/home/course_index_page.html:32
|
||||
msgid "Available Courses"
|
||||
msgstr "Dostępne kursy"
|
||||
|
||||
#: home/templates/home/course_index_page.html:42
|
||||
msgid "Not Purchased"
|
||||
msgstr "Niezakupiony"
|
||||
|
||||
#: home/templates/home/course_module_page.html:21
|
||||
msgid "Lessons"
|
||||
msgstr "Lekcje"
|
||||
|
||||
#: home/templates/home/course_module_page.html:28
|
||||
#, fuzzy
|
||||
#| msgid "No modules yet."
|
||||
msgid "No lessons yet."
|
||||
msgstr "Brak lekcji."
|
||||
|
||||
#: home/templates/home/course_page.html:26
|
||||
#: home/templates/home/course_page.html:31
|
||||
msgid "Refund Purchase"
|
||||
msgstr "Zwróć zakup"
|
||||
|
||||
#: home/templates/home/course_page.html:33
|
||||
msgid "Modules"
|
||||
msgstr "Moduły"
|
||||
|
||||
#: home/templates/home/course_page.html:33
|
||||
#: home/templates/home/course_page.html:40
|
||||
msgid "No modules yet."
|
||||
msgstr "Brak modułów."
|
||||
|
||||
#: home/templates/home/course_page.html:39
|
||||
#: home/templates/home/course_page.html:46
|
||||
msgid ""
|
||||
"You need to be logged in to access this course. Please log in or sign up to "
|
||||
"view the modules."
|
||||
@@ -46,22 +123,22 @@ msgstr ""
|
||||
"Musisz być zalogowany, aby uzyskać dostęp do tego kursu. Zaloguj się lub "
|
||||
"zarejestruj, aby zobaczyć moduły."
|
||||
|
||||
#: home/templates/home/course_page.html:40
|
||||
#: home/templates/home/course_page.html:47
|
||||
#: home/templates/home/event_page.html:40
|
||||
msgid "Login"
|
||||
msgstr "Zaloguj się"
|
||||
|
||||
#: home/templates/home/course_page.html:41
|
||||
#: home/templates/home/course_page.html:48
|
||||
#: home/templates/home/event_page.html:43
|
||||
msgid "Sign Up"
|
||||
msgstr "Zarejestruj się"
|
||||
|
||||
#: home/templates/home/course_page.html:46
|
||||
#: home/templates/home/course_page.html:53
|
||||
msgid ""
|
||||
"You don't have access to this course. Please purchase it to view the modules."
|
||||
msgstr "Nie masz dostępu do tego kursu. Zakup go, aby zobaczyć moduły."
|
||||
|
||||
#: home/templates/home/course_page.html:47
|
||||
#: home/templates/home/course_page.html:54
|
||||
msgid "Purchase Course"
|
||||
msgstr "Kup kurs"
|
||||
|
||||
@@ -105,46 +182,3 @@ msgstr ""
|
||||
#: home/templates/home/event_page.html:54
|
||||
msgid "Sign Up for Event"
|
||||
msgstr "Zapisz się"
|
||||
|
||||
#: home/templates/home/welcome_page.html:6
|
||||
msgid "Visit the Wagtail website"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:15
|
||||
msgid "View the release notes"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:27
|
||||
msgid "Welcome to your new Wagtail site!"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:28
|
||||
msgid ""
|
||||
"Please feel free to <a href=\"https://github.com/wagtail/wagtail/wiki/"
|
||||
"Slack\">join our community on Slack</a>, or get started with one of the "
|
||||
"links below."
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:35
|
||||
msgid "Wagtail Documentation"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:36
|
||||
msgid "Topics, references, & how-tos"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:42
|
||||
msgid "Tutorial"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:43
|
||||
msgid "Build your first Wagtail site"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:49
|
||||
msgid "Admin Interface"
|
||||
msgstr ""
|
||||
|
||||
#: home/templates/home/welcome_page.html:50
|
||||
msgid "Create your superuser first!"
|
||||
msgstr ""
|
||||
|
||||
25
home/migrations/0018_courseindexpage.py
Normal file
25
home/migrations/0018_courseindexpage.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# Generated by Django 6.0.3 on 2026-03-19 14:41
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('home', '0017_chatmessage'),
|
||||
('wagtailcore', '0096_referenceindex_referenceindex_source_object_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CourseIndexPage',
|
||||
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',),
|
||||
),
|
||||
]
|
||||
18
home/migrations/0019_coursepage_description.py
Normal file
18
home/migrations/0019_coursepage_description.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 6.0.3 on 2026-03-19 15:01
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('home', '0018_courseindexpage'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='coursepage',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=255),
|
||||
),
|
||||
]
|
||||
@@ -1,6 +1,7 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.forms import CheckboxSelectMultiple
|
||||
from django.utils import timezone
|
||||
@@ -14,6 +15,8 @@ from wagtail.models.copying import ParentalManyToManyField
|
||||
from wagtail_color_panel.edit_handlers import NativeColorPanel
|
||||
from wagtail_color_panel.fields import ColorField
|
||||
|
||||
from purchase.models import CoursePurchase
|
||||
|
||||
|
||||
class EmptyPage(Page):
|
||||
pass
|
||||
@@ -25,8 +28,29 @@ class HomePage(Page):
|
||||
content_panels = Page.content_panels + ["body"]
|
||||
|
||||
|
||||
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 CoursePage(Page):
|
||||
body = RichTextField(blank=True)
|
||||
course_image = models.ForeignKey(
|
||||
"wagtailimages.Image",
|
||||
null=True,
|
||||
@@ -34,28 +58,74 @@ class CoursePage(Page):
|
||||
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="Select a group to restrict access to this course. Non-members will be prompted to purchase the course to view modules.",
|
||||
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.",
|
||||
)
|
||||
|
||||
def _user_has_access(self, user):
|
||||
if not user.is_authenticated:
|
||||
return False
|
||||
|
||||
user_group_ids = user.groups.values_list("id", flat=True)
|
||||
return self.allowed_groups.filter(id__in=user_group_ids).exists() # pyright: ignore[reportAttributeAccessIssue]
|
||||
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):
|
||||
group_name = f"course_{self.id}_access"
|
||||
group, created = Group.objects.get_or_create(name=group_name)
|
||||
if state := not self.allowed_groups.filter(id=group.id).exists():
|
||||
print(state)
|
||||
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),
|
||||
]
|
||||
parent_page_types = ["home.CourseIndexPage"]
|
||||
subpage_types = ["home.CourseModulePage"]
|
||||
|
||||
|
||||
|
||||
52
home/templates/home/course_index_page.html
Normal file
52
home/templates/home/course_index_page.html
Normal file
@@ -0,0 +1,52 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static i18n wagtailcore_tags wagtailimages_tags %}
|
||||
|
||||
{% block title %}{% trans "Courses" %}{% endblock title %}
|
||||
|
||||
{% block body_class %}template-courseindex{% endblock body_class %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<h1 class="text-3xl font-bold mb-6 text-center">{% trans "Courses" %}</h1>
|
||||
|
||||
<h2 class="text-2xl font-semibold mb-4">{% trans "Purchased Courses" %}</h2>
|
||||
<div class="flex flex-wrap -mx-4">
|
||||
{% for course in purchased_courses %}
|
||||
<div class="w-full md:w-1/2 lg:w-1/3 px-4 mb-8">
|
||||
<a href="{{ course.url }}" class="block bg-green-50 rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300">
|
||||
{% image course.specific.course_image original %}
|
||||
<div class="p-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h2 class="text-xl font-semibold">{{ course.specific.title }}</h2>
|
||||
<div class="relative w-8 h-8 rounded-full bg-green-500">
|
||||
<i class="fi fi-br-lock-open-alt leading-0 absolute left-0 top-1/2 translate-x-1/2 -translate-y-1/2 text-white" title="{% trans "Purchased" %}"></i>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-gray-600">{{ course.specific.description|truncatewords:20 }}</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<h2 class="text-2xl font-semibold mb-4">{% trans "Available Courses" %}</h2>
|
||||
<div class="flex flex-wrap -mx-4">
|
||||
{% for course in other_courses %}
|
||||
<div class="w-full md:w-1/2 lg:w-1/3 px-4 mb-8">
|
||||
<a href="{{ course.url }}" class="block bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300">
|
||||
{% image course.specific.course_image original %}
|
||||
<div class="p-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h2 class="text-xl font-semibold">{{ course.specific.title }}</h2>
|
||||
<div class="relative w-8 h-8 rounded-full bg-gray-200">
|
||||
<i class="fi fi-br-shopping-basket leading-0 absolute left-0 top-1/2 translate-x-1/2 -translate-y-1/2 text-gray-700" title="{% trans "Not Purchased" %}"></i>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-gray-600">{{ course.specific.description|truncatewords:20 }}</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
@@ -17,12 +17,19 @@
|
||||
</h1>
|
||||
|
||||
{% if page.course_image %}
|
||||
{% image page.course_image original alt=page.title class="w-full h-auto rounded-lg mb-6" %}
|
||||
{% image page.course_image original alt=page.title class="w-full h-auto rounded-lg mb-4" %}
|
||||
{% endif %}
|
||||
|
||||
<p class="not-prose text-gray-600 mb-6 text-lg">
|
||||
{{ page.description }}
|
||||
</p>
|
||||
|
||||
{{ page.body|richtext }}
|
||||
|
||||
{% if user_has_access %}
|
||||
{% if user_purchase_id %}
|
||||
<a href="{% url 'mock_refund_purchase' purchase_id=user_purchase_id %}" class="mt-4 inline-block bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition">{% trans "Refund Purchase" %}</a>
|
||||
{% endif %}
|
||||
<h2 class="not-prose text-2xl mt-8 mb-4 text-gray-700 font-semibold">{% trans "Modules" %}</h2>
|
||||
<ul class="list-disc list-inside">
|
||||
{% for module in page.get_children.specific.live %}
|
||||
@@ -44,7 +51,7 @@
|
||||
{% else %}
|
||||
<div class="not-prose mt-8 p-4 bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700">
|
||||
<p>{% trans "You don't have access to this course. Please purchase it to view the modules." %}</p>
|
||||
<a href="" class="mt-4 inline-block bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition">{% trans "Purchase Course" %}</a>
|
||||
<a href="{% url 'mock_purchase_course' course_id=page.id %}" class="mt-4 inline-block bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition">{% trans "Purchase Course" %}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock content %}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
{% load i18n wagtailcore_tags %}
|
||||
|
||||
<header class="header">
|
||||
<div class="logo">
|
||||
<a href="https://wagtail.org/">
|
||||
<svg class="figure-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 342.5 126.2"><title>{% trans "Visit the Wagtail website" %}</title><path fill="#FFF" d="M84 1.9v5.7s-10.2-3.8-16.8 3.1c-4.8 5-5.2 10.6-3 18.1 21.6 0 25 12.1 25 12.1L87 27l6.8-8.3c0-9.8-8.1-16.3-9.8-16.8z"/><circle cx="85.9" cy="15.9" r="2.6"/><path d="M89.2 40.9s-3.3-16.6-24.9-12.1c-2.2-7.5-1.8-13 3-18.1C73.8 3.8 84 7.6 84 7.6V1.9C80.4.3 77 0 73.2 0 59.3 0 51.6 10.4 48.3 17.4L9.2 89.3l11-2.1-20.2 39 14.1-2.5L24.9 93c30.6 0 69.8-11 64.3-52.1z"/><path d="M102.4 27l-8.6-8.3L87 27z"/><path fill="#FFF" d="M30 84.1s1-.2 2.8-.6c1.8-.4 4.3-1 7.3-1.8 1.5-.4 3.1-.9 4.8-1.5 1.7-.6 3.5-1.2 5.2-2 1.8-.7 3.6-1.6 5.4-2.6 1.8-1 3.5-2.1 5.1-3.4.4-.3.8-.6 1.2-1l1.2-1c.7-.7 1.5-1.4 2.2-2.2.7-.7 1.3-1.5 1.9-2.3l.9-1.2.4-.6.4-.6c.2-.4.5-.8.7-1.2.2-.4.4-.8.7-1.2l.3-.6.3-.6c.2-.4.4-.8.5-1.2l.9-2.4c.2-.8.5-1.6.7-2.3.2-.7.3-1.5.5-2.1.1-.7.2-1.3.3-2 .1-.6.2-1.2.2-1.7.1-.5.1-1 .2-1.5.1-1.8.1-2.8.1-2.8l1.6.1s-.1 1.1-.2 2.9c-.1.5-.1 1-.2 1.5-.1.6-.1 1.2-.3 1.8-.1.6-.3 1.3-.4 2-.2.7-.4 1.4-.6 2.2-.2.8-.5 1.5-.8 2.4-.3.8-.6 1.6-1 2.5l-.6 1.2-.3.6-.3.6c-.2.4-.5.8-.7 1.3-.3.4-.5.8-.8 1.2-.1.2-.3.4-.4.6l-.4.6-.9 1.2c-.7.8-1.3 1.6-2.1 2.3-.7.8-1.5 1.4-2.3 2.2l-1.2 1c-.4.3-.8.6-1.3.9-1.7 1.2-3.5 2.3-5.3 3.3-1.8.9-3.7 1.8-5.5 2.5-1.8.7-3.6 1.3-5.3 1.8-1.7.5-3.3 1-4.9 1.3-3 .7-5.6 1.3-7.4 1.6-1.6.6-2.6.8-2.6.8z"/><g fill="#231F20"><path d="M127 83.9h-8.8l-12.6-36.4h7.9l9 27.5 9-27.5h7.9l9 27.5 9-27.5h7.9L153 83.9h-8.8L135.6 59 127 83.9zM200.1 83.9h-7V79c-3 3.6-7 5.4-12.1 5.4-3.8 0-6.9-1.1-9.4-3.2s-3.7-5-3.7-8.6c0-3.6 1.3-6.3 4-8 2.6-1.8 6.2-2.7 10.7-2.7h9.9v-1.4c0-4.8-2.7-7.3-8.1-7.3-3.4 0-6.9 1.2-10.5 3.7l-3.4-4.8c4.4-3.5 9.4-5.3 15.1-5.3 4.3 0 7.8 1.1 10.5 3.2 2.7 2.2 4.1 5.6 4.1 10.2v23.7zm-7.7-13.6v-3.1h-8.6c-5.5 0-8.3 1.7-8.3 5.2 0 1.8.7 3.1 2.1 4.1 1.4.9 3.3 1.4 5.7 1.4 2.4 0 4.6-.7 6.4-2.1 1.8-1.3 2.7-3.1 2.7-5.5zM241.7 47.5v31.7c0 6.4-1.7 11.3-5.2 14.5-3.5 3.2-8 4.8-13.4 4.8-5.5 0-10.4-1.7-14.8-5.1l3.6-5.8c3.6 2.7 7.1 4 10.8 4 3.6 0 6.5-.9 8.6-2.8 2.1-1.9 3.2-4.9 3.2-9v-4.7c-1.1 2.1-2.8 3.9-4.9 5.1-2.1 1.3-4.5 1.9-7.1 1.9-4.8 0-8.8-1.7-11.9-5.1-3.1-3.4-4.7-7.6-4.7-12.6s1.6-9.2 4.7-12.6c3.1-3.4 7.1-5.1 11.9-5.1 4.8 0 8.7 2 11.7 6v-5.4h7.5zm-28.4 16.8c0 3 .9 5.6 2.8 7.7 1.8 2.2 4.3 3.2 7.5 3.2 3.1 0 5.7-1 7.6-3.1 1.9-2.1 2.9-4.7 2.9-7.8 0-3.1-1-5.8-2.9-7.9-2-2.2-4.5-3.2-7.6-3.2-3.1 0-5.6 1.1-7.4 3.4-2 2.1-2.9 4.7-2.9 7.7zM260.9 53.6v18.5c0 1.7.5 3.1 1.4 4.1.9 1 2.2 1.5 3.8 1.5 1.6 0 3.2-.8 4.7-2.4l3.1 5.4c-2.7 2.4-5.7 3.6-8.9 3.6-3.3 0-6-1.1-8.3-3.4-2.3-2.3-3.5-5.3-3.5-9.1V53.6h-4.6v-6.2h4.6V36.1h7.7v11.4h9.6v6.2h-9.6zM309.5 83.9h-7V79c-3 3.6-7 5.4-12.1 5.4-3.8 0-6.9-1.1-9.4-3.2s-3.7-5-3.7-8.6c0-3.6 1.3-6.3 4-8 2.6-1.8 6.2-2.7 10.7-2.7h9.9v-1.4c0-4.8-2.7-7.3-8.1-7.3-3.4 0-6.9 1.2-10.5 3.7l-3.4-4.8c4.4-3.5 9.4-5.3 15.1-5.3 4.3 0 7.8 1.1 10.5 3.2 2.7 2.2 4.1 5.6 4.1 10.2v23.7zm-7.7-13.6v-3.1h-8.6c-5.5 0-8.3 1.7-8.3 5.2 0 1.8.7 3.1 2.1 4.1 1.4.9 3.3 1.4 5.7 1.4 2.4 0 4.6-.7 6.4-2.1 1.8-1.3 2.7-3.1 2.7-5.5zM319.3 40.2c-1-1-1.4-2.1-1.4-3.4 0-1.3.5-2.5 1.4-3.4 1-1 2.1-1.4 3.4-1.4 1.3 0 2.5.5 3.4 1.4 1 1 1.4 2.1 1.4 3.4 0 1.3-.5 2.5-1.4 3.4s-2.1 1.4-3.4 1.4c-1.3.1-2.4-.4-3.4-1.4zm7.2 43.7h-7.7V47.5h7.7v36.4zM342.5 83.9h-7.7V33.1h7.7v50.8z"/></g></svg>
|
||||
</a>
|
||||
</div>
|
||||
<div class="header-link">
|
||||
{% comment %}
|
||||
This works for all cases but prerelease versions:
|
||||
{% endcomment %}
|
||||
<a href="{% wagtail_documentation_path %}/releases/{% wagtail_release_notes_path %}">
|
||||
{% trans "View the release notes" %}
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
<main class="main">
|
||||
<div class="figure">
|
||||
<svg class="figure-space" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300" aria-hidden="true">
|
||||
<path class="egg" fill="currentColor" d="M150 250c-42.741 0-75-32.693-75-90s42.913-110 75-110c32.088 0 75 52.693 75 110s-32.258 90-75 90z"/>
|
||||
<ellipse fill="#ddd" cx="150" cy="270" rx="40" ry="7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="main-text">
|
||||
<h1>{% trans "Welcome to your new Wagtail site!" %}</h1>
|
||||
<p>{% trans 'Please feel free to <a href="https://github.com/wagtail/wagtail/wiki/Slack">join our community on Slack</a>, or get started with one of the links below.' %}</p>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="footer" role="contentinfo">
|
||||
<a class="option option-one" href="{% wagtail_documentation_path %}/">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true"><path d="M9 21c0 .5.4 1 1 1h4c.6 0 1-.5 1-1v-1H9v1zm3-19C8.1 2 5 5.1 5 9c0 2.4 1.2 4.5 3 5.7V17c0 .5.4 1 1 1h6c.6 0 1-.5 1-1v-2.3c1.8-1.3 3-3.4 3-5.7 0-3.9-3.1-7-7-7zm2.9 11.1l-.9.6V16h-4v-2.3l-.9-.6C7.8 12.2 7 10.6 7 9c0-2.8 2.2-5 5-5s5 2.2 5 5c0 1.6-.8 3.2-2.1 4.1z"/></svg>
|
||||
<div>
|
||||
<h2>{% trans "Wagtail Documentation" %}</h2>
|
||||
<p>{% trans "Topics, references, & how-tos" %}</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="option option-two" href="{% wagtail_documentation_path %}/getting_started/tutorial.html">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></svg>
|
||||
<div>
|
||||
<h2>{% trans "Tutorial" %}</h2>
|
||||
<p>{% trans "Build your first Wagtail site" %}</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="option option-three" href="{% url 'wagtailadmin_home' %}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true"><path d="M0 0h24v24H0z" fill="none"/><path d="M16.5 13c-1.2 0-3.07.34-4.5 1-1.43-.67-3.3-1-4.5-1C5.33 13 1 14.08 1 16.25V19h22v-2.75c0-2.17-4.33-3.25-6.5-3.25zm-4 4.5h-10v-1.25c0-.54 2.56-1.75 5-1.75s5 1.21 5 1.75v1.25zm9 0H14v-1.25c0-.46-.2-.86-.52-1.22.88-.3 1.96-.53 3.02-.53 2.44 0 5 1.21 5 1.75v1.25zM7.5 12c1.93 0 3.5-1.57 3.5-3.5S9.43 5 7.5 5 4 6.57 4 8.5 5.57 12 7.5 12zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 5.5c1.93 0 3.5-1.57 3.5-3.5S18.43 5 16.5 5 13 6.57 13 8.5s1.57 3.5 3.5 3.5zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2z"/></svg>
|
||||
<div>
|
||||
<h2>{% trans "Admin Interface" %}</h2>
|
||||
<p>{% trans "Create your superuser first!" %}</p>
|
||||
</div>
|
||||
</a>
|
||||
</footer>
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-03-17 11:46+0000\n"
|
||||
"POT-Creation-Date: 2026-03-20 12:18+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -26,7 +26,7 @@ msgstr ""
|
||||
msgid "Sorry, you don't have permission to access this page."
|
||||
msgstr ""
|
||||
|
||||
#: kursy/templates/calendar.html:4 kursy/templates/header.html:8
|
||||
#: kursy/templates/calendar.html:4
|
||||
msgid "Course Calendar"
|
||||
msgstr ""
|
||||
|
||||
@@ -34,19 +34,27 @@ msgstr ""
|
||||
msgid "Loading..."
|
||||
msgstr ""
|
||||
|
||||
#: kursy/templates/header.html:10
|
||||
#: kursy/templates/header.html:7
|
||||
msgid "Courses"
|
||||
msgstr ""
|
||||
|
||||
#: kursy/templates/header.html:8
|
||||
msgid "Calendar"
|
||||
msgstr ""
|
||||
|
||||
#: kursy/templates/header.html:13
|
||||
msgid "Logout"
|
||||
msgstr ""
|
||||
|
||||
#: kursy/templates/header.html:12 kursy/templates/occurrence_detail.html:39
|
||||
#: kursy/templates/header.html:15 kursy/templates/occurrence_detail.html:39
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
|
||||
#: kursy/templates/header.html:13 kursy/templates/occurrence_detail.html:42
|
||||
#: kursy/templates/header.html:16 kursy/templates/occurrence_detail.html:42
|
||||
msgid "Sign Up"
|
||||
msgstr ""
|
||||
|
||||
#: kursy/templates/header.html:32
|
||||
#: kursy/templates/header.html:35
|
||||
msgid "Search courses..."
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-03-17 11:46+0000\n"
|
||||
"POT-Creation-Date: 2026-03-20 12:18+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -28,7 +28,7 @@ msgstr "Odmowa dostępu"
|
||||
msgid "Sorry, you don't have permission to access this page."
|
||||
msgstr "Przepraszamy, ale nie masz uprawnień do dostępu do tej strony."
|
||||
|
||||
#: kursy/templates/calendar.html:4 kursy/templates/header.html:8
|
||||
#: kursy/templates/calendar.html:4
|
||||
msgid "Course Calendar"
|
||||
msgstr "Kalendarz kursów"
|
||||
|
||||
@@ -36,19 +36,27 @@ msgstr "Kalendarz kursów"
|
||||
msgid "Loading..."
|
||||
msgstr "Ładowanie..."
|
||||
|
||||
#: kursy/templates/header.html:10
|
||||
#: kursy/templates/header.html:7
|
||||
msgid "Courses"
|
||||
msgstr "Kursy"
|
||||
|
||||
#: kursy/templates/header.html:8
|
||||
msgid "Calendar"
|
||||
msgstr "Kalendarz"
|
||||
|
||||
#: kursy/templates/header.html:13
|
||||
msgid "Logout"
|
||||
msgstr "Wyloguj się"
|
||||
|
||||
#: kursy/templates/header.html:12 kursy/templates/occurrence_detail.html:39
|
||||
#: kursy/templates/header.html:15 kursy/templates/occurrence_detail.html:39
|
||||
msgid "Login"
|
||||
msgstr "Zaloguj się"
|
||||
|
||||
#: kursy/templates/header.html:13 kursy/templates/occurrence_detail.html:42
|
||||
#: kursy/templates/header.html:16 kursy/templates/occurrence_detail.html:42
|
||||
msgid "Sign Up"
|
||||
msgstr "Zarejestruj się"
|
||||
|
||||
#: kursy/templates/header.html:32
|
||||
#: kursy/templates/header.html:35
|
||||
msgid "Search courses..."
|
||||
msgstr "Szukaj kursów..."
|
||||
|
||||
@@ -62,8 +70,6 @@ msgid "You are signed up for this event. We look forward to seeing you there!"
|
||||
msgstr ""
|
||||
|
||||
#: kursy/templates/occurrence_detail.html:31
|
||||
#, fuzzy
|
||||
#| msgid "Sign Up"
|
||||
msgid "Cancel Sign Up"
|
||||
msgstr "Zrezygnuj"
|
||||
|
||||
@@ -71,17 +77,23 @@ msgstr "Zrezygnuj"
|
||||
msgid ""
|
||||
"You need to be logged in to sign up for this event. Please log in or sign up "
|
||||
"to reserve your spot."
|
||||
msgstr "Musisz być zalogowany, aby zapisać się na to wydarzenie. Zaloguj się lub zarejestruj, aby zarezerwować swoje miejsce."
|
||||
msgstr ""
|
||||
"Musisz być zalogowany, aby zapisać się na to wydarzenie. Zaloguj się lub "
|
||||
"zarejestruj, aby zarezerwować swoje miejsce."
|
||||
|
||||
#: kursy/templates/occurrence_detail.html:47
|
||||
msgid ""
|
||||
"This event is fully booked. Please check back later for any cancellations."
|
||||
msgstr "To wydarzenie jest w pełni zarezerwowane. Sprawdź ponownie później w przypadku zwolnienia miejsc."
|
||||
msgstr ""
|
||||
"To wydarzenie jest w pełni zarezerwowane. Sprawdź ponownie później w "
|
||||
"przypadku zwolnienia miejsc."
|
||||
|
||||
#: kursy/templates/occurrence_detail.html:51
|
||||
msgid ""
|
||||
"You are not signed up for this event. Please sign up to reserve your spot."
|
||||
msgstr "Nie jesteś zapisany na to wydarzenie. Zapisz się, aby zarezerwować swoje miejsce."
|
||||
msgstr ""
|
||||
"Nie jesteś zapisany na to wydarzenie. Zapisz się, aby zarezerwować swoje "
|
||||
"miejsce."
|
||||
|
||||
#: kursy/templates/occurrence_detail.html:53
|
||||
msgid "Sign Up for Event"
|
||||
|
||||
@@ -32,6 +32,7 @@ dotenv.load_dotenv(BASE_DIR / ".env")
|
||||
INSTALLED_APPS = [
|
||||
"home",
|
||||
"search",
|
||||
"purchase",
|
||||
"wagtail.contrib.forms",
|
||||
"wagtail.contrib.redirects",
|
||||
"wagtail.embeds",
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
{% load i18n wagtailcore_tags %}
|
||||
<header class="bg-blue-900 text-white shadow-md relative">
|
||||
<header class="bg-blue-900 text-white shadow-md lg:sticky top-0 z-40">
|
||||
<div class="container mx-auto flex items-center justify-between py-4 px-6">
|
||||
{% wagtail_site as current_site %}
|
||||
<a class="text-xl font-bold" href="/">{{ current_site.site_name }}</a>
|
||||
<nav class="flex items-center gap-4">
|
||||
<a class="text-xl font-bold" href="/">{{ current_site.site_name }}</a>
|
||||
<a href="{% slugurl 'courses' %}" class="hover:underline">{% trans "Courses" %}</a>
|
||||
<a href="{% url 'calendar' %}" class="hover:underline">{% trans "Calendar" %}</a>
|
||||
</nav>
|
||||
|
||||
<nav class="flex items-center gap-4">
|
||||
<a href="{% url 'calendar' %}" class="hover:underline">{% trans "Course Calendar" %}</a>
|
||||
{% if user.is_authenticated %}
|
||||
<a href="{% url 'account_logout' %}" class="hover:underline">{% trans "Logout" %}</a>
|
||||
{% else %}
|
||||
@@ -28,8 +31,8 @@
|
||||
</div>
|
||||
|
||||
<div class="container mx-auto px-6 mb-2 md:mb-0">
|
||||
<form action="{% url 'search' %}" method="get" class="flex items-center bg-blue-950 rounded-md md:w-auto md:absolute md:left-1/2 md:top-1/2 md:transform md:-translate-x-1/2 md:-translate-y-1/2 md:mt-0">
|
||||
<input type="text" name="query" placeholder="{% trans 'Search courses...' %}" class="rounded-md px-3 py-2 w-full md:w-auto focus:outline-none">
|
||||
<form action="{% url 'search' %}" method="get" class="flex items-center bg-blue-950 rounded-md mb-2 lg:w-auto lg:absolute lg:left-1/2 lg:top-1/2 lg:transform lg:-translate-x-1/2 lg:-translate-y-1/2 lg:mt-0">
|
||||
<input type="text" name="query" placeholder="{% trans 'Search courses...' %}" class="rounded-lg px-3 py-2 w-full lg:w-auto focus:outline-none">
|
||||
<button type="submit" class="bg-white text-blue-900 rounded-md px-3 py-2 hover:bg-gray-200 transition"><i class="fi fi-br-search"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -22,6 +22,7 @@ urlpatterns = [
|
||||
path("i18n/", include("django.conf.urls.i18n")),
|
||||
path("oauth2/", include("oauth2_provider.urls", namespace="oauth2_provider")),
|
||||
path("", include("home.urls")),
|
||||
path("", include("purchase.urls")),
|
||||
path("calendar/", views.calendar, name="calendar"),
|
||||
# TODO: move occurrence related urls to home app
|
||||
path(
|
||||
|
||||
0
purchase/__init__.py
Normal file
0
purchase/__init__.py
Normal file
3
purchase/admin.py
Normal file
3
purchase/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
5
purchase/apps.py
Normal file
5
purchase/apps.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PurchaseConfig(AppConfig):
|
||||
name = 'purchase'
|
||||
28
purchase/migrations/0001_initial.py
Normal file
28
purchase/migrations/0001_initial.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 6.0.3 on 2026-03-19 17:36
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('home', '0019_coursepage_description'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CoursePurchase',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('purchased_at', models.DateTimeField(auto_now_add=True)),
|
||||
('refunded', models.BooleanField(default=False)),
|
||||
('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='home.coursepage')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
0
purchase/migrations/__init__.py
Normal file
0
purchase/migrations/__init__.py
Normal file
23
purchase/models.py
Normal file
23
purchase/models.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import Group
|
||||
from django.db import models
|
||||
|
||||
|
||||
class CoursePurchase(models.Model):
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
||||
course = models.ForeignKey("home.CoursePage", on_delete=models.CASCADE)
|
||||
purchased_at = models.DateTimeField(auto_now_add=True)
|
||||
refunded = models.BooleanField(default=False)
|
||||
|
||||
def mock_refund(self):
|
||||
self.refunded = True
|
||||
self.save()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
group_name = f"course_{self.course.id}_access"
|
||||
|
||||
group, _ = Group.objects.get_or_create(name=group_name)
|
||||
if self.refunded:
|
||||
print(f"Removing user {self.user} from group {group_name} due to refund")
|
||||
self.user.groups.remove(group)
|
||||
3
purchase/tests.py
Normal file
3
purchase/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
16
purchase/urls.py
Normal file
16
purchase/urls.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path(
|
||||
"mock-purchase/<int:course_id>/",
|
||||
views.mock_purchase_course,
|
||||
name="mock_purchase_course",
|
||||
),
|
||||
path(
|
||||
"mock-refund/<int:purchase_id>/",
|
||||
views.mock_refund_purchase,
|
||||
name="mock_refund_purchase",
|
||||
),
|
||||
]
|
||||
21
purchase/views.py
Normal file
21
purchase/views.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from django.shortcuts import redirect, render
|
||||
from django.urls import reverse
|
||||
|
||||
from home.models import CoursePage
|
||||
from purchase.models import CoursePurchase
|
||||
|
||||
|
||||
def mock_purchase_course(request, course_id):
|
||||
course = CoursePage.objects.get(id=course_id)
|
||||
|
||||
course.mock_purchase(request.user)
|
||||
|
||||
return redirect(course.url)
|
||||
|
||||
|
||||
def mock_refund_purchase(request, purchase_id):
|
||||
purchase = CoursePurchase.objects.get(id=purchase_id)
|
||||
|
||||
purchase.mock_refund()
|
||||
|
||||
return redirect(purchase.course.url)
|
||||
Reference in New Issue
Block a user