diff --git a/client/src/views/StyelGuideView.vue b/client/src/views/StyelGuideView.vue
index 8f617f84..3257d3a7 100644
--- a/client/src/views/StyelGuideView.vue
+++ b/client/src/views/StyelGuideView.vue
@@ -3,6 +3,9 @@
import {reactive} from 'vue'
import {Listbox, ListboxButton, ListboxOption, ListboxOptions} from '@headlessui/vue'
import ItCheckbox from '@/components/ui/ItCheckbox.vue';
+import ItDropdown from "@/components/ui/ItDropdown.vue";
+import IconLogout from "@/components/icons/IconLogout.vue"
+import IconSettings from "@/components/icons/IconSettings.vue"
const state = reactive({
@@ -22,7 +25,35 @@ const state = reactive({
dropdownSelected: {id: 8},
})
+const dropdownData = [
+ [
+ {
+ title: 'Option 1',
+ icon: IconLogout,
+ data: {}
+ },
+ {
+ title: 'Option 2',
+ icon: null,
+ data: {
+ test: 12
+ }
+ }
+ ],
+ [
+ {
+ title: 'Option 3',
+ icon: IconSettings,
+ data: {
+ amount: 34
+ }
+ },
+ ]
+ ]
+// TODO: die CSS-Klasse für die Farben wird hier in der StyleGuideView.vue generiert.
+// deshalb muss man diese CSS-Klassen in tailwind.config.js "safelist"en, wenn diese sonst
+// noch nirgendwo verwendet werden.
const colors = ['blue', 'sky', 'orange', 'green', 'red', 'gray',];
const colorValues = [100, 200, 300, 400, 500, 600, 700, 800, 900,];
@@ -30,6 +61,10 @@ function colorBgClass(color: string, value: number) {
return `bg-${color}-${value}`;
}
+function log(data: any) {
+ console.log(data);
+}
+
@@ -272,11 +307,11 @@ function colorBgClass(color: string, value: number) {
+ class="absolute z-10 mt-1 w-full bg-white shadow-lg max-h-60 py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm">
+ :class="[active ? 'text-white bg-blue-900' : 'text-black', 'cursor-default select-none relative py-2 pl-3 pr-9']">
{{ person.name }}
@@ -298,6 +333,17 @@ function colorBgClass(color: string, value: number) {
Disabled
+ Dropdown
+
+
+ Click Me
+
+
diff --git a/client/tailwind.config.js b/client/tailwind.config.js
index bd222674..610daf61 100644
--- a/client/tailwind.config.js
+++ b/client/tailwind.config.js
@@ -4,7 +4,8 @@ module.exports = {
content: [
'./index.html',
'./src/**/*.{vue,js,ts,jsx,tsx}',
- // '../server/vbv_lernwelt/**/*.{html,js}',
+ // TODO: wenn man den server-pfad auch angibt wird Tailwind langsamer?! (Startzeit erhöht sich stark...)
+ // '../server/vbv_lernwelt/**/*.{html,js,py}',
],
theme: {
fontFamily: {
@@ -21,8 +22,9 @@ module.exports = {
transparent: 'transparent',
current: 'currentColor',
'white': '#ffffff',
+ 'black': '#0A0A0A',
'blue': {
- 700: '#1A5197',
+ 700: '#2957A6',
900: '#00224D',
},
'sky': {
@@ -31,21 +33,21 @@ module.exports = {
},
'orange': {
500: '#FE955A',
- 600: '#F37F3E',
+ 600: '#E68B4E',
},
'green': {
- 500: '#3EDF9C',
- 600: '#17D29A',
+ 500: '#54CE8B',
+ 600: '#5BB782',
},
'red': {
- 500: '#DE3618',
+ 500: '#EF7D68',
},
'gray': {
- 100: '#EDF2F6',
+ 200: '#EDF2F6',
300: '#E0E5EC',
500: '#B1C1CA',
700: '#6F787E',
- 900: '#0A0A0A',
+ 900: '#585F63',
},
}
},
diff --git a/client/tailwind.css b/client/tailwind.css
index 8f5c57af..a62e7281 100644
--- a/client/tailwind.css
+++ b/client/tailwind.css
@@ -3,7 +3,7 @@
@tailwind utilities;
html {
- @apply text-gray-900
+ @apply text-black
}
svg {
@@ -24,24 +24,37 @@ svg {
}
h2 {
- @apply text-3xl xl:text-4xl font-bold
+ @apply text-2xl md:text-3xl xl:text-4xl font-bold
}
.heading-2 {
- @apply text-3xl xl:text-4xl font-bold
+ @apply text-2xl md:text-3xl xl:text-4xl font-bold
}
h3 {
- @apply text-2xl font-bold
+ @apply text-xl xl:text-2xl font-bold
}
.heading-3 {
- @apply text-3xl xl:text-4xl font-bold
+ @apply text-xl xl:text-2xl font-bold
}
.link {
@apply underline underline-offset-2
}
+
+ .link-large {
+ @apply text-lg underline xl:text-xl
+ }
+
+ .text-large {
+ @apply text-lg xl:text-xl
+ }
+
+ .text-bold {
+ @apply text-base font-bold
+ }
+
}
@layer components {
@@ -59,7 +72,7 @@ svg {
.btn-secondary {
@apply font-bold py-2 px-4 align-middle inline-block
bg-white text-blue-900 border-2 border-blue-900
- hover:bg-gray-100
+ hover:bg-gray-200
disabled:opacity-50 disabled:cursor-not-allowed
}
diff --git a/env_secrets/local_chrigu.env b/env_secrets/local_chrigu.env
new file mode 100644
index 00000000..2e103689
Binary files /dev/null and b/env_secrets/local_chrigu.env differ
diff --git a/scripts/count_queries.py b/scripts/count_queries.py
new file mode 100644
index 00000000..48167c3b
--- /dev/null
+++ b/scripts/count_queries.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+import json
+import os
+import sys
+
+import django
+
+sys.path.append("../server")
+
+os.environ.setdefault("IT_APP_ENVIRONMENT", "development")
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.base")
+django.setup()
+
+from wagtail.models import Page
+
+
+def main():
+ from django.conf import settings
+ settings.DEBUG = True
+ from django.db import connection
+ from django.db import reset_queries
+ reset_queries()
+
+ page = Page.objects.get(slug='versicherungsvermittlerin', locale__language_code='de-CH')
+ serializer = page.specific.get_serializer_class()(page.specific)
+
+ print(serializer.data)
+ print(len(json.dumps(serializer.data)))
+ print(len(connection.queries))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/server/config/settings/base.py b/server/config/settings/base.py
index 176f7ba0..86cc8254 100644
--- a/server/config/settings/base.py
+++ b/server/config/settings/base.py
@@ -486,32 +486,37 @@ ALLOWED_HOSTS = env.list(
# CACHES
CACHES = {
"default": {
- "BACKEND": env(
- "IT_DJANGO_CACHE_BACKEND",
- default="django.core.cache.backends.db.DatabaseCache",
- ),
+ "BACKEND": env("IT_DJANGO_CACHE_BACKEND", default="django.core.cache.backends.db.DatabaseCache"),
"LOCATION": env("IT_DJANGO_CACHE_LOCATION", default="django_cache_table"),
- }
+ },
}
if "django_redis.cache.RedisCache" in env("IT_DJANGO_CACHE_BACKEND", default=""):
CACHES = {
"default": {
- "BACKEND": env(
- "IT_DJANGO_CACHE_BACKEND",
- default="django.core.cache.backends.db.DatabaseCache",
- ),
- "LOCATION": env("IT_DJANGO_CACHE_LOCATION", default="django_cache_table"),
+ "BACKEND": "django_redis.cache.RedisCache",
+ "LOCATION": env("IT_DJANGO_CACHE_LOCATION"),
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
- # Mimicing memcache behavior.
- # https://github.com/jazzband/django-redis#memcached-exceptions-behavior
"IGNORE_EXCEPTIONS": True,
},
- }
+ },
}
+CACHES["learning_path_cache"] = {
+ "BACKEND": "django.core.cache.backends.db.DatabaseCache",
+ "LOCATION": "django_cache_learning_path",
+}
+
# OAuth/OpenId Connect
+IT_OAUTH_TENANT_ID = env.str("IT_OAUTH_TENANT_ID", default=None)
+
+if IT_OAUTH_TENANT_ID:
+ IT_OAUTH_AUTHORIZE_PARAMS = {
+ 'tenant_id': IT_OAUTH_TENANT_ID
+ }
+else:
+ IT_OAUTH_AUTHORIZE_PARAMS = {}
OAUTH = {
"client_name": env("IT_OAUTH_CLIENT_NAME", default="lernetz"),
@@ -519,10 +524,11 @@ OAUTH = {
"client_secret": env("IT_OAUTH_CLIENT_SECRET", default=""),
"access_token_url": env("IT_OAUTH_ACCESS_TOKEN_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/token"),
"authorize_url": env("IT_OAUTH_AUTHORIZE_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/auth"),
+ "authorize_params": IT_OAUTH_AUTHORIZE_PARAMS,
"api_base_url": env("IT_OAUTH_API_BASE_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/"),
- "local_redirect_uri": env("IT_OAUTH_LOCAL_DIRECT_URI", default="http://localhost:3000/sso/callback/"),
+ "local_redirect_uri": env("IT_OAUTH_LOCAL_DIRECT_URI", default="http://localhost:8000/sso/callback/"),
"client_kwargs": {
- 'scope': '',
+ 'scope': env("IT_OAUTH_SCOPE", default=''),
'token_endpoint_auth_method': 'client_secret_post',
'token_placement': 'header',
}
diff --git a/server/vbv_lernwelt/core/create_default_users.py b/server/vbv_lernwelt/core/create_default_users.py
index cab5e08b..82c7c3ce 100644
--- a/server/vbv_lernwelt/core/create_default_users.py
+++ b/server/vbv_lernwelt/core/create_default_users.py
@@ -9,12 +9,12 @@ def create_default_users(user_model=User, group_model=Group, default_password=No
default_password = 'test'
admin_group, created = group_model.objects.get_or_create(name='admin_group')
- content_creator_grop, created = group_model.objects.get_or_create(name='content_creator_grop')
+ _content_creator_grop, _created = group_model.objects.get_or_create(name='content_creator_grop')
student_group, created = group_model.objects.get_or_create(name='student_group')
- def _create_student_user(email, first_name, last_name, avatar_url=''):
+ def _create_student_user(email, first_name, last_name, avatar_url='', password=default_password):
student_user, created = _get_or_create_user(
- user_model=user_model, username=email, password=default_password,
+ user_model=user_model, username=email, password=password,
)
student_user.first_name = first_name
student_user.last_name = last_name
@@ -73,7 +73,24 @@ def create_default_users(user_model=User, group_model=Group, default_password=No
email='christoph.bosshard@vbv-afa.ch',
first_name='Christoph',
last_name='Bosshard',
- avatar_url='/static/avatars/avatar_christoph.jpg'
+ avatar_url='/static/avatars/avatar_christoph.jpg',
+ password='myvbv1234'
+ )
+
+ _create_student_user(
+ email='alexandra.vangelista@lernetz.ch',
+ first_name='Alexandra',
+ last_name='Vangelista',
+ avatar_url='/static/avatars/avatar_alexandra.png',
+ password='myvbv1234'
+ )
+
+ _create_student_user(
+ email='chantal.rosenberg@vbv-afa.ch',
+ first_name='Chantal',
+ last_name='Rosenberg',
+ avatar_url='/static/avatars/avatar_chantal.png',
+ password='myvbv1234'
)
diff --git a/server/vbv_lernwelt/core/models.py b/server/vbv_lernwelt/core/models.py
index 55054f86..432e0608 100644
--- a/server/vbv_lernwelt/core/models.py
+++ b/server/vbv_lernwelt/core/models.py
@@ -2,6 +2,8 @@ from django.contrib.auth.models import AbstractUser
from django.db import models
from django.db.models import JSONField
+from vbv_lernwelt.core.managers import UserManager
+
class User(AbstractUser):
"""
@@ -13,6 +15,8 @@ class User(AbstractUser):
avatar_url = models.CharField(max_length=254, blank=True, default='')
email = models.EmailField('email address', unique=True)
+ objects = UserManager()
+
class SecurityRequestResponseLog(models.Model):
label = models.CharField(max_length=255, blank=True, default="")
diff --git a/server/vbv_lernwelt/learnpath/models.py b/server/vbv_lernwelt/learnpath/models.py
index 67534a3c..14fb1f64 100644
--- a/server/vbv_lernwelt/learnpath/models.py
+++ b/server/vbv_lernwelt/learnpath/models.py
@@ -29,6 +29,12 @@ class LearningPath(Page):
def __str__(self):
return f"{self.title}"
+ @classmethod
+ def get_serializer_class(cls):
+ return get_it_serializer_class(
+ cls, ['id', 'title', 'slug', 'type', 'translation_key', 'children']
+ )
+
class Topic(Page):
# title = models.TextField(default='')
@@ -55,8 +61,9 @@ class Topic(Page):
@classmethod
def get_serializer_class(cls):
- return get_it_serializer_class(cls,
- field_names=['id', 'title', 'slug', 'type', 'translation_key', 'is_visible', ])
+ return get_it_serializer_class(
+ cls, field_names=['id', 'title', 'slug', 'type', 'translation_key', 'is_visible', ]
+ )
class Meta:
verbose_name = "Topic"
@@ -143,6 +150,13 @@ class LearningSequence(Page):
def get_admin_display_title(self):
return f'{self.icon} {self.draft_title}'
+ def get_admin_display_title_html(self):
+ return f'''
+
+ <{self.icon} style="height: 32px; width: 32px;">{self.icon}>
+ {self.draft_title}
+ '''
+
def full_clean(self, *args, **kwargs):
super(LearningSequence, self).full_clean(*args, **kwargs)
@@ -161,6 +175,9 @@ class LearningUnit(Page):
def get_serializer_class(cls):
return get_it_serializer_class(cls, field_names=['id', 'title', 'slug', 'type', 'translation_key', 'children'])
+ def get_admin_display_title_html(self):
+ return f'
{self.draft_title}'
+
class LearningUnitQuestion(Page):
parent_page_types = ['learnpath.LearningUnit']
@@ -213,6 +230,13 @@ class LearningContent(Page):
return display_title
+ def get_admin_display_title_html(self):
+ return f'''
+
+
+ {self.get_admin_display_title()}
+ '''
+
class Meta:
verbose_name = "Learning Content"
diff --git a/server/vbv_lernwelt/learnpath/serializer_helpers.py b/server/vbv_lernwelt/learnpath/serializer_helpers.py
index bfaeb48b..73da6ade 100644
--- a/server/vbv_lernwelt/learnpath/serializer_helpers.py
+++ b/server/vbv_lernwelt/learnpath/serializer_helpers.py
@@ -20,5 +20,20 @@ class ItBaseSerializer(wagtail_serializers.BaseSerializer):
meta_fields = []
+ def __init__(self, *args, **kwargs):
+ self.descendants = kwargs.pop('descendants', None)
+ super().__init__(*args, **kwargs)
+
def get_children(self, obj):
- return [c.specific.get_serializer_class()(c.specific).data for c in obj.get_children()]
+ if not self.descendants:
+ self.descendants = [p for p in obj.get_descendants().specific()]
+ children = _get_children(self.descendants, obj)
+ return [c.specific.get_serializer_class()(c.specific, descendants=self.descendants).data for c in children]
+
+
+def _get_descendants(pages, obj):
+ return [c for c in pages if c.path.startswith(obj.path) and c.depth >= obj.depth]
+
+
+def _get_children(pages, obj):
+ return [c for c in pages if c.path.startswith(obj.path) and obj.depth + 1 == c.depth]
diff --git a/server/vbv_lernwelt/learnpath/serializers.py b/server/vbv_lernwelt/learnpath/serializers.py
index 3aa8d494..e69de29b 100644
--- a/server/vbv_lernwelt/learnpath/serializers.py
+++ b/server/vbv_lernwelt/learnpath/serializers.py
@@ -1,20 +0,0 @@
-from rest_framework import serializers
-
-from vbv_lernwelt.learnpath.models import Circle, LearningPath
-from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class
-
-
-class LearningPathSerializer(get_it_serializer_class(LearningPath, [])):
- children = serializers.SerializerMethodField()
-
- meta_fields = []
-
- def get_children(self, obj):
- return [c.specific.get_serializer_class()(c.specific).data for c in obj.get_children()]
-
- def get_meta_label(self, obj):
- return obj._meta.label
-
- class Meta:
- model = Circle
- fields = ['id', 'title', 'slug', 'type', 'translation_key', 'children']
diff --git a/server/vbv_lernwelt/learnpath/signals.py b/server/vbv_lernwelt/learnpath/signals.py
new file mode 100644
index 00000000..48369730
--- /dev/null
+++ b/server/vbv_lernwelt/learnpath/signals.py
@@ -0,0 +1,16 @@
+import structlog
+from django.core.cache import caches
+from django.db.models.signals import post_delete, post_save
+from wagtail.models import Page
+
+logger = structlog.get_logger(__name__)
+
+
+def invalidate_learning_path_cache(sender, **kwargs):
+ logger.debug('invalidate learning_path_cache', label='learning_path_cache')
+ caches['learning_path_cache'].clear()
+
+
+for subclass in Page.__subclasses__():
+ post_save.connect(invalidate_learning_path_cache, subclass)
+ post_delete.connect(invalidate_learning_path_cache, subclass)
diff --git a/server/vbv_lernwelt/learnpath/urls.py b/server/vbv_lernwelt/learnpath/urls.py
index 5b470619..2d12205e 100644
--- a/server/vbv_lernwelt/learnpath/urls.py
+++ b/server/vbv_lernwelt/learnpath/urls.py
@@ -1,10 +1,8 @@
from django.urls import path, re_path
-from .views import circle_view, generate_web_component_icons
-from .views import learningpath_view
+from .views import generate_web_component_icons, page_api_view
urlpatterns = [
- path(r"api/circle/
/", circle_view, name="circle_view"),
- path(r"api/learningpath//", learningpath_view, name="learningpath_view"),
+ path(r"api/page//", page_api_view, name="page_api_view"),
re_path(r"icons/$", generate_web_component_icons, name="generate_web_component_icons"),
]
diff --git a/server/vbv_lernwelt/learnpath/views.py b/server/vbv_lernwelt/learnpath/views.py
index 962bc23c..c1ea8f26 100644
--- a/server/vbv_lernwelt/learnpath/views.py
+++ b/server/vbv_lernwelt/learnpath/views.py
@@ -4,18 +4,19 @@ from pathlib import Path
from django.conf import settings
from django.shortcuts import render
+from django.views.decorators.cache import cache_page
from rest_framework.decorators import api_view
from rest_framework.response import Response
+from wagtail.models import Page
from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt
-from vbv_lernwelt.learnpath.models import Circle, LearningPath
-from vbv_lernwelt.learnpath.serializers import LearningPathSerializer
@api_view(['GET'])
-def circle_view(request, slug):
- circle = Circle.objects.get(slug=slug)
- serializer = Circle.get_serializer_class()(circle)
+@cache_page(60 * 60 * 8, cache="learning_path_cache")
+def page_api_view(request, slug):
+ page = Page.objects.get(slug=slug, locale__language_code='de-CH')
+ serializer = page.specific.get_serializer_class()(page.specific)
return Response(serializer.data)
@@ -38,9 +39,3 @@ def generate_web_component_icons(request):
context={'svg_files': svg_files},
content_type="application/javascript"
)
-
-@api_view(['GET'])
-def learningpath_view(request, slug):
- learning_path = LearningPath.objects.get(slug=slug, locale__language_code='de-CH')
- serializer = LearningPathSerializer(learning_path)
- return Response(serializer.data)
diff --git a/server/vbv_lernwelt/sso/client.py b/server/vbv_lernwelt/sso/client.py
index cfabe4c2..0170f2e3 100644
--- a/server/vbv_lernwelt/sso/client.py
+++ b/server/vbv_lernwelt/sso/client.py
@@ -23,7 +23,7 @@ oauth.register(
access_token_url=settings.OAUTH["access_token_url"],
access_token_params=None,
authorize_url=settings.OAUTH["authorize_url"],
- authorize_params=None,
+ authorize_params=settings.OAUTH["authorize_params"],
api_base_url=settings.OAUTH["api_base_url"],
client_kwargs=settings.OAUTH["client_kwargs"]
)
diff --git a/server/vbv_lernwelt/static/avatars/avatar_alexandra.png b/server/vbv_lernwelt/static/avatars/avatar_alexandra.png
new file mode 100644
index 00000000..e0a637e3
Binary files /dev/null and b/server/vbv_lernwelt/static/avatars/avatar_alexandra.png differ
diff --git a/server/vbv_lernwelt/static/avatars/avatar_chantal.png b/server/vbv_lernwelt/static/avatars/avatar_chantal.png
new file mode 100644
index 00000000..983b45f5
Binary files /dev/null and b/server/vbv_lernwelt/static/avatars/avatar_chantal.png differ
diff --git a/server/vbv_lernwelt/static/icons/icon-smiley-happy.svg b/server/vbv_lernwelt/static/icons/icon-smiley-happy.svg
index 0b60809c..df272150 100644
--- a/server/vbv_lernwelt/static/icons/icon-smiley-happy.svg
+++ b/server/vbv_lernwelt/static/icons/icon-smiley-happy.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/server/vbv_lernwelt/static/icons/icon-smiley-thinking.svg b/server/vbv_lernwelt/static/icons/icon-smiley-thinking.svg
index a9a165e7..cd2977f7 100644
--- a/server/vbv_lernwelt/static/icons/icon-smiley-thinking.svg
+++ b/server/vbv_lernwelt/static/icons/icon-smiley-thinking.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/server/vbv_lernwelt/templates/wagtailadmin/pages/index.html b/server/vbv_lernwelt/templates/wagtailadmin/pages/index.html
index 337de9c8..02a8b599 100644
--- a/server/vbv_lernwelt/templates/wagtailadmin/pages/index.html
+++ b/server/vbv_lernwelt/templates/wagtailadmin/pages/index.html
@@ -2,13 +2,28 @@
{% load wagtailadmin_tags i18n %}
{% block extra_js %}
- {{ block.super }}
+ {{ block.super }}
-
+
+
{% endblock %}
diff --git a/server/vbv_lernwelt/templates/wagtailadmin/pages/listing/_list.html b/server/vbv_lernwelt/templates/wagtailadmin/pages/listing/_list.html
new file mode 100644
index 00000000..54b82326
--- /dev/null
+++ b/server/vbv_lernwelt/templates/wagtailadmin/pages/listing/_list.html
@@ -0,0 +1,96 @@
+{% load i18n %}
+{% load l10n %}
+{% load wagtailadmin_tags %}
+
+ {% if show_ordering_column or show_bulk_actions %}
+
+ {% endif %}
+
+ {% if show_parent %}
+
+ {% endif %}
+
+
+
+
+
+ {% block pre_parent_page_headers %}
+ {% endblock %}
+
+ {% if parent_page %}
+ {% page_permissions parent_page as parent_page_perms %}
+
+ |
+ {% block parent_page_title %}
+ {% endblock %}
+ |
+ {% if parent_page.latest_revision_created_at %}
+
+ {% blocktrans trimmed with time_period=parent_page.latest_revision_created_at|timesince %}{{ time_period }}
+ ago{% endblocktrans %} {% endif %} |
+
+ {% if not parent_page.is_root %}
+ {{ parent_page.content_type.model_class.get_verbose_name }}
+ {% endif %}
+ |
+
+ {% if not parent_page.is_root %}
+ {% include "wagtailadmin/shared/page_status_tag.html" with page=parent_page %}
+ {% endif %}
+ |
+ |
+
+ {% endif %}
+
+ {% block post_parent_page_headers %}
+ {% endblock %}
+
+
+ {% if pages %}
+ {% trans "Select page" as checkbox_aria_label %}
+ {% for page in pages %}
+ {% page_permissions page as page_perms %}
+
+ {% if show_ordering_column %}
+ | {% if orderable and ordering == "ord" %}
+ {% trans 'Drag' %} {% endif %} |
+ {% elif show_bulk_actions %}
+ {% include "wagtailadmin/bulk_actions/listing_checkbox_cell.html" with obj_type="page" obj=page aria_labelledby_prefix="page_" aria_labelledby=page.pk|unlocalize aria_labelledby_suffix="_title" %}
+ {% endif %}
+
+ {{ page.type }}
+ {% block page_title %}
+ {% endblock %}
+ |
+ {% if show_parent %}
+
+ {% block page_parent_page_title %}
+ {% with page.get_parent as parent %}
+ {% if parent %}
+ {{ parent.specific_deferred.get_admin_display_title }}
+ {% endif %}
+ {% endwith %}
+ {% endblock %}
+ |
+ {% endif %}
+ {% if page.latest_revision_created_at %}
+
+ {% blocktrans trimmed with time_period=page.latest_revision_created_at|timesince %}{{ time_period }}
+ ago{% endblocktrans %} {% endif %} |
+ {{ page.content_type.model_class.get_verbose_name }} |
+
+ {% include "wagtailadmin/shared/page_status_tag.html" with page=page %}
+ |
+ {% block page_navigation %}
+ {% endblock %}
+
+ {% endfor %}
+ {% else %}
+ {% block no_results %}{% endblock %}
+ {% endif %}
+
+
diff --git a/server/vbv_lernwelt/templates/wagtailadmin/pages/listing/_list_explore.html b/server/vbv_lernwelt/templates/wagtailadmin/pages/listing/_list_explore.html
new file mode 100644
index 00000000..fd4de9f5
--- /dev/null
+++ b/server/vbv_lernwelt/templates/wagtailadmin/pages/listing/_list_explore.html
@@ -0,0 +1,8 @@
+{% extends "wagtailadmin/pages/listing/_list_explore.html" %}
+
+{% load i18n wagtailadmin_tags %}
+
+
+{% block page_title %}
+ {% include "wagtailadmin/pages/listing/_page_title_explore.html" %}
+{% endblock %}
diff --git a/server/vbv_lernwelt/templates/wagtailadmin/pages/listing/_page_title_explore.html b/server/vbv_lernwelt/templates/wagtailadmin/pages/listing/_page_title_explore.html
new file mode 100644
index 00000000..0b266118
--- /dev/null
+++ b/server/vbv_lernwelt/templates/wagtailadmin/pages/listing/_page_title_explore.html
@@ -0,0 +1,41 @@
+{% load i18n wagtailadmin_tags %}
+
+{# The title field for a page in the page listing, when in 'explore' mode #}
+
+
+
+
+ {% page_listing_buttons page page_perms %}
+