diff --git a/client/src/views/LoginView.vue b/client/src/views/LoginView.vue
new file mode 100644
index 00000000..137ee027
--- /dev/null
+++ b/client/src/views/LoginView.vue
@@ -0,0 +1,62 @@
+
+
+
+
+ Login
+
+
+
+
+
+
+
diff --git a/client/src/views/MediaView.vue b/client/src/views/MediaView.vue
new file mode 100644
index 00000000..6439a274
--- /dev/null
+++ b/client/src/views/MediaView.vue
@@ -0,0 +1,15 @@
+
+
+
+
+ Mediathek
+
+
+
+
diff --git a/client/src/views/MessagesView.vue b/client/src/views/MessagesView.vue
new file mode 100644
index 00000000..93eb97d5
--- /dev/null
+++ b/client/src/views/MessagesView.vue
@@ -0,0 +1,15 @@
+
+
+
+
+ Messages
+
+
+
+
diff --git a/client/src/views/ProfileView.vue b/client/src/views/ProfileView.vue
index c5f45614..56909600 100644
--- a/client/src/views/ProfileView.vue
+++ b/client/src/views/ProfileView.vue
@@ -1,34 +1,15 @@
-
-
-
This is a profile page
-
-
+
-
diff --git a/client/src/views/ShopView.vue b/client/src/views/ShopView.vue
new file mode 100644
index 00000000..b1f7b356
--- /dev/null
+++ b/client/src/views/ShopView.vue
@@ -0,0 +1,15 @@
+
+
+
+
+ Shop
+
+
+
+
diff --git a/client/src/views/StyelGuideView.vue b/client/src/views/StyelGuideView.vue
index 8fb83e88..87b39b33 100644
--- a/client/src/views/StyelGuideView.vue
+++ b/client/src/views/StyelGuideView.vue
@@ -2,7 +2,6 @@
import {reactive} from 'vue'
import {Listbox, ListboxButton, ListboxOption, ListboxOptions} from '@headlessui/vue'
-import MainNavigationBar from '@/components/MainNavigationBar.vue';
import ItCheckbox from '@/components/ui/ItCheckbox.vue';
@@ -34,8 +33,6 @@ function colorBgClass(color: string, value: number) {
-
-
Style Guide
@@ -168,6 +165,16 @@ function colorBgClass(color: string, value: number) {
+
+ vbv
+
+
+
+
+ vbv-pos
+
+
+
diff --git a/tailwind.config.js b/client/tailwind.config.js
similarity index 90%
rename from tailwind.config.js
rename to client/tailwind.config.js
index 0c8db2df..e2862e2d 100644
--- a/tailwind.config.js
+++ b/client/tailwind.config.js
@@ -2,9 +2,9 @@ const colors = require('tailwindcss/colors')
module.exports = {
content: [
- './client/index.html',
- './client/src/**/*.{vue,js,ts,jsx,tsx}',
- './server/vbv_lernwelt/**/*.{html,js}',
+ './index.html',
+ './src/**/*.{vue,js,ts,jsx,tsx}',
+ '../server/vbv_lernwelt/**/*.{html,js}',
],
theme: {
fontFamily: {
diff --git a/tailwind/input.css b/client/tailwind.css
similarity index 100%
rename from tailwind/input.css
rename to client/tailwind.css
diff --git a/compose/django/docker_start.sh b/compose/django/docker_start.sh
index 05e535bd..cc6bffe0 100644
--- a/compose/django/docker_start.sh
+++ b/compose/django/docker_start.sh
@@ -4,15 +4,9 @@ set -o errexit
set -o pipefail
set -o nounset
+python /app/manage.py collectstatic --noinput
+
# TODO remove after stabilisation
python /app/manage.py reset_schema
-python /app/manage.py collectstatic --noinput
-python /app/manage.py createcachetable
-python /app/manage.py migrate
-
-# TODO remove after stabilisation
-python /app/manage.py create_default_users
-python /app/manage.py create_default_learning_path
-
/usr/local/bin/gunicorn config.asgi --bind 0.0.0.0:80 --chdir=/app -k uvicorn.workers.UvicornWorker
diff --git a/package.json b/package.json
index 6f79d5ff..62ff2d66 100644
--- a/package.json
+++ b/package.json
@@ -2,19 +2,16 @@
"name": "vbv_lernwelt_cypress",
"version": "1.0.0",
"scripts": {
- "build": "npm install --prefix client && npm run build --prefix client && npm run build:tailwind",
- "build:tailwind": "tailwindcss -i ./tailwind/input.css -o ./server/vbv_lernwelt/static/css/tailwind.css --minify",
+ "build": "npm install --prefix client && npm run build --prefix client && npm run build:tailwind --prefix client",
"test": "echo \"Error: no test specified\" && exit 1",
"cypress:open": "cypress open",
"cypress:run": "cypress run",
"cypress:ci": "cypress run --config baseUrl=http://localhost:8001",
- "cypress:ci:open": "cypress open --config baseUrl=http://localhost:8001",
- "tailwind": "tailwindcss -i ./tailwind/input.css -o ./server/vbv_lernwelt/static/css/tailwind.css --watch"
+ "cypress:ci:open": "cypress open --config baseUrl=http://localhost:8001"
},
"devDependencies": {
"@tailwindcss/forms": "^0.5.2",
"@tailwindcss/typography": "^0.5.2",
- "cypress": "^9.4.1",
- "tailwindcss": "^3.0.24"
+ "cypress": "^9.4.1"
}
}
diff --git a/server/config/settings/base.py b/server/config/settings/base.py
index f0168d54..176f7ba0 100644
--- a/server/config/settings/base.py
+++ b/server/config/settings/base.py
@@ -92,7 +92,9 @@ THIRD_PARTY_APPS = [
'wagtail.search',
'wagtail.admin',
'wagtail',
- 'wagtail.locales',
+ # 'wagtail.locales',
+ "wagtail_localize",
+ "wagtail_localize.locales",
'wagtail.api.v2',
'modelcluster',
@@ -127,7 +129,7 @@ AUTH_USER_MODEL = "core.User"
# FIXME make configurable!?
# LOGIN_URL = "/sso/login/"
-LOGIN_URL = "/login/"
+LOGIN_URL = "/login"
LOGIN_REDIRECT_URL = "/"
ALLOW_LOCAL_LOGIN = env.bool("IT_ALLOW_LOCAL_LOGIN", default=False)
@@ -168,7 +170,7 @@ MIDDLEWARE = [
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.common.BrokenLinkEmailsMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
- "django_htmx.middleware.HtmxMiddleware",
+ "csp.middleware.CSPMiddleware",
"vbv_lernwelt.core.middleware.auth.AuthenticationRequiredMiddleware",
"vbv_lernwelt.core.middleware.security.SecurityRequestResponseLoggingMiddleware",
"wagtail.contrib.redirects.middleware.RedirectMiddleware",
@@ -448,6 +450,10 @@ REST_FRAMEWORK = {
# django-cors-headers - https://github.com/adamchainz/django-cors-headers#setup
CORS_URLS_REGEX = r"^/api/.*$"
+# django-csp
+CSP_DEFAULT_SRC = ("'self'", "'unsafe-inline'", 'ws://localhost:3000', 'localhost:8000', 'blob:', 'data:', 'http://*')
+CSP_FRAME_ANCESTORS = ("'self'",)
+
# By Default swagger ui is available only to admin user. You can change permission classs to change that
# See more configuration options at https://drf-spectacular.readthedocs.io/en/latest/settings.html#settings
SPECTACULAR_SETTINGS = {
diff --git a/server/config/urls.py b/server/config/urls.py
index 49992051..f99b3bdb 100644
--- a/server/config/urls.py
+++ b/server/config/urls.py
@@ -1,7 +1,6 @@
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
-from django.contrib.auth import views as auth_views
from django.contrib.auth.decorators import user_passes_test
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.urls import include, path, re_path
@@ -17,8 +16,7 @@ from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt
from vbv_lernwelt.core.views import (
rate_limit_exceeded_view,
permission_denied_view,
- check_rate_limit, cypress_reset_view, vue_home,
-)
+ check_rate_limit, cypress_reset_view, vue_home, vue_login, me_user_view, )
from .wagtail_api import wagtail_api_router
@@ -43,14 +41,16 @@ urlpatterns = [
path('pages/', include(wagtail_urls)),
path('learnpath/', include("vbv_lernwelt.learnpath.urls")),
path('api/completion/', include("vbv_lernwelt.completion.urls")),
+ re_path(r'api/core/me/$', me_user_view, name='me_user_view'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
if settings.DEBUG:
# Static file serving when using Gunicorn + Uvicorn for local web socket development
urlpatterns += staticfiles_urlpatterns()
if settings.ALLOW_LOCAL_LOGIN:
- urlpatterns += [path("login/", django_view_authentication_exempt(
- auth_views.LoginView.as_view(template_name="core/login.html"))),]
+ urlpatterns += [
+ re_path(r'core/login/$', django_view_authentication_exempt(vue_login), name='vue_login'),
+ ]
# API URLS
@@ -67,7 +67,7 @@ urlpatterns += [
if settings.APP_ENVIRONMENT != 'production':
urlpatterns += [
- re_path(r'cypressreset/$', cypress_reset_view, name='cypress_reset_view'),
+ re_path(r'core/cypressreset/$', cypress_reset_view, name='cypress_reset_view'),
]
# fmt: on
diff --git a/server/requirements/requirements-dev.txt b/server/requirements/requirements-dev.txt
index b7892288..4f5f0a8f 100644
--- a/server/requirements/requirements-dev.txt
+++ b/server/requirements/requirements-dev.txt
@@ -82,6 +82,7 @@ django==3.2.13
# via
# -r requirements.in
# django-cors-headers
+ # django-csp
# django-debug-toolbar
# django-extensions
# django-filter
@@ -97,12 +98,15 @@ django==3.2.13
# djangorestframework
# drf-spectacular
# wagtail
+ # wagtail-localize
django-click==2.3.0
# via -r requirements.in
django-cors-headers==3.11.0
# via -r requirements.in
django-coverage-plugin==2.0.2
# via -r requirements-dev.in
+django-csp==3.7
+ # via -r requirements.in
django-debug-toolbar==3.2.4
# via -r requirements-dev.in
django-extensions==3.1.5
@@ -262,6 +266,8 @@ platformdirs==2.5.1
# virtualenv
pluggy==1.0.0
# via pytest
+polib==1.1.1
+ # via wagtail-localize
portalocker==2.4.0
# via concurrent-log-handler
pre-commit==2.17.0
@@ -400,6 +406,7 @@ typing-extensions==4.1.1
# django-stubs-ext
# djangorestframework-stubs
# mypy
+ # wagtail-localize
uritemplate==4.1.1
# via
# coreapi
@@ -414,12 +421,15 @@ uvloop==0.16.0
# via uvicorn
virtualenv==20.14.0
# via pre-commit
-wagtail==3.0.0
+wagtail==3.0.1
# via
# -r requirements.in
# wagtail-factories
+ # wagtail-localize
wagtail-factories==2.0.1
# via -r requirements.in
+wagtail-localize==1.2.1
+ # via -r requirements.in
watchdog==2.1.7
# via werkzeug
watchgod==0.8.1
diff --git a/server/requirements/requirements.in b/server/requirements/requirements.in
index ca86bc90..700df65a 100644
--- a/server/requirements/requirements.in
+++ b/server/requirements/requirements.in
@@ -24,6 +24,7 @@ dj-database-url
django-click
django-ratelimit
django-ipware
+django-csp
psycopg2-binary
gunicorn
@@ -35,3 +36,4 @@ concurrent-log-handler
wagtail>=3,<4
wagtail-factories
+wagtail-localize
diff --git a/server/requirements/requirements.txt b/server/requirements/requirements.txt
index e0c64c8b..4f09972f 100644
--- a/server/requirements/requirements.txt
+++ b/server/requirements/requirements.txt
@@ -50,6 +50,7 @@ django==3.2.13
# via
# -r requirements.in
# django-cors-headers
+ # django-csp
# django-filter
# django-htmx
# django-model-utils
@@ -61,10 +62,13 @@ django==3.2.13
# djangorestframework
# drf-spectacular
# wagtail
+ # wagtail-localize
django-click==2.3.0
# via -r requirements.in
django-cors-headers==3.11.0
# via -r requirements.in
+django-csp==3.7
+ # via -r requirements.in
django-filter==21.1
# via wagtail
django-htmx==1.9.0
@@ -134,6 +138,8 @@ pillow==9.0.1
# via
# -r requirements.in
# wagtail
+polib==1.1.1
+ # via wagtail-localize
portalocker==2.4.0
# via concurrent-log-handler
psycopg2-binary==2.9.3
@@ -192,6 +198,8 @@ telepath==0.2
# via wagtail
text-unidecode==1.3
# via python-slugify
+typing-extensions==4.2.0
+ # via wagtail-localize
uritemplate==4.1.1
# via drf-spectacular
urllib3==1.26.9
@@ -202,12 +210,15 @@ uvicorn[standard]==0.17.6
# via -r requirements.in
uvloop==0.16.0
# via uvicorn
-wagtail==3.0.0
+wagtail==3.0.1
# via
# -r requirements.in
# wagtail-factories
+ # wagtail-localize
wagtail-factories==2.0.1
# via -r requirements.in
+wagtail-localize==1.2.1
+ # via -r requirements.in
watchgod==0.8.1
# via uvicorn
webencodings==0.5.1
diff --git a/server/vbv_lernwelt/completion/tests/test_api.py b/server/vbv_lernwelt/completion/tests/test_api.py
index 6d21296e..22bcca73 100644
--- a/server/vbv_lernwelt/completion/tests/test_api.py
+++ b/server/vbv_lernwelt/completion/tests/test_api.py
@@ -19,7 +19,7 @@ class CompletionApiTestCase(APITestCase):
def setUp(self) -> None:
self.user = User.objects.get(username='student')
- self.client.login(username='student', password='student')
+ self.client.login(username='student', password='test')
def test_completeLearningContent_works(self):
learning_content = LearningContent.objects.get(title='Einleitung Circle "Anlayse"')
diff --git a/server/vbv_lernwelt/completion/views.py b/server/vbv_lernwelt/completion/views.py
index da7479dc..c195ebc1 100644
--- a/server/vbv_lernwelt/completion/views.py
+++ b/server/vbv_lernwelt/completion/views.py
@@ -26,7 +26,7 @@ def mark_circle_completion(request):
page_key = request.data.get('page_key')
completed = request.data.get('completed', True)
- page = Page.objects.get(translation_key=page_key)
+ page = Page.objects.get(translation_key=page_key, locale__language_code='de-CH')
page_type = get_wagtail_type(page.specific)
circle = Circle.objects.ancestor_of(page).first()
diff --git a/server/vbv_lernwelt/core/create_default_users.py b/server/vbv_lernwelt/core/create_default_users.py
index 886e736f..cab5e08b 100644
--- a/server/vbv_lernwelt/core/create_default_users.py
+++ b/server/vbv_lernwelt/core/create_default_users.py
@@ -5,25 +5,76 @@ from vbv_lernwelt.core.models import User
def create_default_users(user_model=User, group_model=Group, default_password=None):
+ if default_password is None:
+ 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')
student_group, created = group_model.objects.get_or_create(name='student_group')
- admin_password = default_password
- if not admin_password:
- admin_password = 'admin'
- admin_user, created = _get_or_create_user(user_model=user_model, username='admin', password=admin_password)
- admin_user.is_superuser = True
- admin_user.is_staff = True
- admin_user.groups.add(admin_group)
- admin_user.save()
+ def _create_student_user(email, first_name, last_name, avatar_url=''):
+ student_user, created = _get_or_create_user(
+ user_model=user_model, username=email, password=default_password,
+ )
+ student_user.first_name = first_name
+ student_user.last_name = last_name
+ student_user.avatar_url = avatar_url
+ student_user.groups.add(student_group)
+ student_user.save()
- student_user_password = default_password
- if not student_user_password:
- student_user_password = 'student'
- student_user, created = _get_or_create_user(user_model=user_model, username='student', password=student_user_password)
- student_user.groups.add(student_group)
- student_user.save()
+ def _create_admin_user(email, first_name, last_name, avatar_url=''):
+ admin_user, created = _get_or_create_user(
+ user_model=user_model, username=email, password=default_password
+ )
+ admin_user.is_superuser = True
+ admin_user.is_staff = True
+ admin_user.first_name = first_name
+ admin_user.last_name = last_name
+ admin_user.avatar_url = avatar_url
+ admin_user.groups.add(admin_group)
+ admin_user.save()
+
+ _create_admin_user(
+ email='info@iterativ.ch',
+ first_name='Info',
+ last_name='Iterativ',
+ avatar_url='/static/avatars/avatar_iterativ.png'
+ )
+
+ _create_admin_user(
+ email='admin',
+ first_name='Peter',
+ last_name='Adminson',
+ avatar_url='/static/avatars/avatar_iterativ.png'
+ )
+
+ _create_student_user(
+ email='student',
+ first_name='Student',
+ last_name='Meier',
+ avatar_url='/static/avatars/avatar_iterativ.png'
+ )
+
+ _create_student_user(
+ email='daniel.egger@iterativ.ch',
+ first_name='Daniel',
+ last_name='Egger',
+ avatar_url='/static/avatars/avatar_iterativ.png'
+ )
+
+ _create_student_user(
+ email='axel.manderbach@lernetz.ch',
+ first_name='Axel',
+ last_name='Manderbach',
+ avatar_url='/static/avatars/avatar_axel.jpg'
+ )
+
+ _create_student_user(
+ email='christoph.bosshard@vbv-afa.ch',
+ first_name='Christoph',
+ last_name='Bosshard',
+ avatar_url='/static/avatars/avatar_christoph.jpg'
+ )
def _get_or_create_user(user_model, *args, **kwargs):
@@ -34,6 +85,10 @@ def _get_or_create_user(user_model, *args, **kwargs):
user = user_model.objects.filter(username=username).first()
if not user:
- user = user_model.objects.create(username=username, password=make_password(password))
+ user = user_model.objects.create(
+ username=username,
+ password=make_password(password),
+ email=username,
+ )
created = True
return user, created
diff --git a/server/vbv_lernwelt/core/management/commands/cypress_reset.py b/server/vbv_lernwelt/core/management/commands/cypress_reset.py
index 934dc1f2..757ef365 100644
--- a/server/vbv_lernwelt/core/management/commands/cypress_reset.py
+++ b/server/vbv_lernwelt/core/management/commands/cypress_reset.py
@@ -10,4 +10,4 @@ def command(customer_language):
print("cypress reset data")
delete_default_learning_path()
- create_default_learning_path()
+ create_default_learning_path(skip_locales=False)
diff --git a/server/vbv_lernwelt/core/management/commands/reset_schema.py b/server/vbv_lernwelt/core/management/commands/reset_schema.py
index d4faa57d..bad46f70 100644
--- a/server/vbv_lernwelt/core/management/commands/reset_schema.py
+++ b/server/vbv_lernwelt/core/management/commands/reset_schema.py
@@ -1,5 +1,6 @@
import djclick as click
from django.conf import settings
+from django.core.management import call_command
from django.db import transaction, connection
@@ -24,3 +25,7 @@ def command():
print(user)
reset_schema(db_config_user=user)
+ call_command('createcachetable')
+ call_command('migrate')
+ call_command('create_default_users')
+ call_command('create_default_learning_path')
diff --git a/server/vbv_lernwelt/core/migrations/0002_user_model.py b/server/vbv_lernwelt/core/migrations/0002_user_model.py
new file mode 100644
index 00000000..2733511e
--- /dev/null
+++ b/server/vbv_lernwelt/core/migrations/0002_user_model.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.2.13 on 2022-06-28 12:06
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('core', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='user',
+ name='avatar_url',
+ field=models.CharField(blank=True, default='', max_length=254),
+ ),
+ migrations.AlterField(
+ model_name='user',
+ name='email',
+ field=models.EmailField(max_length=254, unique=True, verbose_name='email address'),
+ ),
+ ]
diff --git a/server/vbv_lernwelt/core/migrations/0002_create_users.py b/server/vbv_lernwelt/core/migrations/0003_create_users.py
similarity index 94%
rename from server/vbv_lernwelt/core/migrations/0002_create_users.py
rename to server/vbv_lernwelt/core/migrations/0003_create_users.py
index e5d92cfa..faf79e86 100644
--- a/server/vbv_lernwelt/core/migrations/0002_create_users.py
+++ b/server/vbv_lernwelt/core/migrations/0003_create_users.py
@@ -15,7 +15,7 @@ def create_users(apps, schema_editor):
class Migration(migrations.Migration):
dependencies = [
- ("core", "0001_initial"),
+ ("core", "0002_user_model"),
]
operations = [
diff --git a/server/vbv_lernwelt/core/models.py b/server/vbv_lernwelt/core/models.py
index b679d72d..55054f86 100644
--- a/server/vbv_lernwelt/core/models.py
+++ b/server/vbv_lernwelt/core/models.py
@@ -10,6 +10,8 @@ class User(AbstractUser):
"""
# FIXME: look into it...
# objects = UserManager()
+ avatar_url = models.CharField(max_length=254, blank=True, default='')
+ email = models.EmailField('email address', unique=True)
class SecurityRequestResponseLog(models.Model):
diff --git a/server/vbv_lernwelt/core/serializers.py b/server/vbv_lernwelt/core/serializers.py
new file mode 100644
index 00000000..ddc5ef24
--- /dev/null
+++ b/server/vbv_lernwelt/core/serializers.py
@@ -0,0 +1,11 @@
+from rest_framework import serializers
+
+from vbv_lernwelt.core.models import User
+
+
+class UserSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = User
+ fields = [
+ 'id', 'first_name', 'last_name', 'email', 'username', 'avatar_url',
+ ]
diff --git a/server/vbv_lernwelt/core/templates/core/login.html b/server/vbv_lernwelt/core/templates/core/login.html
deleted file mode 100644
index 4a8794dd..00000000
--- a/server/vbv_lernwelt/core/templates/core/login.html
+++ /dev/null
@@ -1,35 +0,0 @@
-{% extends "base.html" %}
-
-{% block content %}
-
-
-
-{% endblock %}
diff --git a/server/vbv_lernwelt/core/utils.py b/server/vbv_lernwelt/core/utils.py
index d48f902c..8ad3171f 100644
--- a/server/vbv_lernwelt/core/utils.py
+++ b/server/vbv_lernwelt/core/utils.py
@@ -6,8 +6,6 @@ from rest_framework.throttling import UserRateThrottle
from structlog.types import EventDict
-#from .models import User
-
def structlog_add_app_info(
logger: logging.Logger, method_name: str, event_dict: EventDict
) -> EventDict:
diff --git a/server/vbv_lernwelt/core/views.py b/server/vbv_lernwelt/core/views.py
index 29c9daa0..36c3a3b9 100644
--- a/server/vbv_lernwelt/core/views.py
+++ b/server/vbv_lernwelt/core/views.py
@@ -1,7 +1,9 @@
# Create your views here.
import requests
+import structlog
from django.conf import settings
+from django.contrib.auth import authenticate, login
from django.core.management import call_command
from django.http import JsonResponse, HttpResponse, HttpResponseRedirect
from django.shortcuts import render
@@ -11,8 +13,12 @@ from ratelimit.decorators import ratelimit
from rest_framework import authentication
from rest_framework.decorators import api_view, authentication_classes, permission_classes
from rest_framework.permissions import IsAdminUser
+from rest_framework.response import Response
from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt
+from vbv_lernwelt.core.serializers import UserSerializer
+
+logger = structlog.get_logger(__name__)
@django_view_authentication_exempt
@@ -37,6 +43,32 @@ def vue_home(request):
return HttpResponse(content)
+@api_view(['POST'])
+@ensure_csrf_cookie
+def vue_login(request):
+ try:
+ username = request.data.get('username')
+ password = request.data.get('password')
+ if username and password:
+ user = authenticate(request, username=username, password=password)
+ if user:
+ login(request, user)
+ logger.debug('login successful', username=username, email=user.email, label='login')
+ return Response(UserSerializer(user).data)
+ except Exception as e:
+ logger.exception(e)
+
+ logger.debug('login failed', username=username, label='login')
+ return Response({'success': False}, status=401)
+
+
+@api_view(['GET'])
+def me_user_view(request):
+ if request.user.is_authenticated:
+ return Response(UserSerializer(request.user).data)
+ return Response(status=403)
+
+
def permission_denied_view(request, exception):
return render(request, "403.html", status=403)
diff --git a/server/vbv_lernwelt/learnpath/management/commands/create_default_learning_path.py b/server/vbv_lernwelt/learnpath/management/commands/create_default_learning_path.py
index 7df9caaf..2218afd3 100644
--- a/server/vbv_lernwelt/learnpath/management/commands/create_default_learning_path.py
+++ b/server/vbv_lernwelt/learnpath/management/commands/create_default_learning_path.py
@@ -5,4 +5,4 @@ from vbv_lernwelt.learnpath.tests.create_default_learning_path import create_def
@click.command()
def command():
- create_default_learning_path()
+ create_default_learning_path(skip_locales=False)
diff --git a/server/vbv_lernwelt/learnpath/tests/create_default_learning_path.py b/server/vbv_lernwelt/learnpath/tests/create_default_learning_path.py
index 28b3737e..560b7434 100644
--- a/server/vbv_lernwelt/learnpath/tests/create_default_learning_path.py
+++ b/server/vbv_lernwelt/learnpath/tests/create_default_learning_path.py
@@ -1,17 +1,20 @@
import wagtail_factories
from django.conf import settings
-from wagtail.models import Site, Page
+from django.core.management import call_command
+from wagtail.models import Site, Page, Locale
+from wagtail_localize.models import LocaleSynchronization
from vbv_lernwelt.core.admin import User
-from vbv_lernwelt.learnpath.models import LearningPath, Topic, Circle, LearningSequence, LearningContent
+from vbv_lernwelt.learnpath.models import LearningPath, Topic, Circle, LearningSequence, LearningContent, LearningUnit, \
+ LearningUnitQuestion
from vbv_lernwelt.learnpath.tests.learning_path_factories import LearningPathFactory, TopicFactory, CircleFactory, \
LearningSequenceFactory, LearningContentFactory, VideoBlockFactory, PodcastBlockFactory, CompetenceBlockFactory, \
ExerciseBlockFactory, DocumentBlockFactory, LearningUnitFactory, LearningUnitQuestionFactory
-def create_default_learning_path(user=None):
+def create_default_learning_path(user=None, skip_locales=True):
if user is None:
- user = User.objects.get(username='admin')
+ user = User.objects.get(username='info@iterativ.ch')
site = Site.objects.filter(is_default_site=True).first()
@@ -22,6 +25,7 @@ def create_default_learning_path(user=None):
site.port = 8000
site.save()
+
# create_default_competences()
lp = LearningPathFactory(title="Versicherungsvermittler/in", parent=site.root_page)
@@ -404,12 +408,29 @@ Fachspezialisten bei.
# tp = TopicFactory.create(title="Prüfung", is_visible=False, learning_path=lp)
# circle_7 = CircleFactory.create(title="Prüfungsvorbereitung", parent=lp, topic=tp)
+ # locales
+ if not skip_locales:
+ locale_de = Locale.objects.get(language_code='de-CH')
+ locale_fr, _ = Locale.objects.get_or_create(language_code='fr-CH')
+ LocaleSynchronization.objects.get_or_create(
+ locale_id=locale_fr.id,
+ sync_from_id=locale_de.id
+ )
+ locale_it, _ = Locale.objects.get_or_create(language_code='it-CH')
+ LocaleSynchronization.objects.get_or_create(
+ locale_id=locale_it.id,
+ sync_from_id=locale_de.id
+ )
+ call_command('sync_locale_trees')
+
# all pages belong to 'admin' by default
Page.objects.update(owner=user)
def delete_default_learning_path():
LearningContent.objects.all().delete()
+ LearningUnitQuestion.objects.all().delete()
+ LearningUnit.objects.all().delete()
LearningSequence.objects.all().delete()
Circle.objects.all().delete()
Topic.objects.all().delete()
diff --git a/server/vbv_lernwelt/learnpath/tests/learning_path_factories.py b/server/vbv_lernwelt/learnpath/tests/learning_path_factories.py
index 260153e3..16848614 100644
--- a/server/vbv_lernwelt/learnpath/tests/learning_path_factories.py
+++ b/server/vbv_lernwelt/learnpath/tests/learning_path_factories.py
@@ -57,7 +57,7 @@ class LearningContentFactory(wagtail_factories.PageFactory):
class VideoBlockFactory(wagtail_factories.StructBlockFactory):
- url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
+ url = "https://www.youtube.com/embed/qhPIfxS2hvI"
description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam"
class Meta:
@@ -65,7 +65,7 @@ class VideoBlockFactory(wagtail_factories.StructBlockFactory):
class WebBasedTrainingBlockFactory(wagtail_factories.StructBlockFactory):
- url = "https://www.example.com"
+ url = "/media/web_based_trainings/rise_cmi5_test_export/scormcontent/index.html"
description = "Beispiel Rise Modul"
class Meta:
@@ -74,7 +74,7 @@ class WebBasedTrainingBlockFactory(wagtail_factories.StructBlockFactory):
class PodcastBlockFactory(wagtail_factories.StructBlockFactory):
description = "Beispiel Podcast"
- url = "https://docs.wagtail.org/en/stable/topics/streamfield.html"
+ url = "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/325190984&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true"
class Meta:
model = PodcastBlock
diff --git a/server/vbv_lernwelt/static/avatars/avatar_axel.jpg b/server/vbv_lernwelt/static/avatars/avatar_axel.jpg
new file mode 100644
index 00000000..d8884568
Binary files /dev/null and b/server/vbv_lernwelt/static/avatars/avatar_axel.jpg differ
diff --git a/server/vbv_lernwelt/static/avatars/avatar_christoph.png b/server/vbv_lernwelt/static/avatars/avatar_christoph.png
new file mode 100644
index 00000000..5e0bc0b7
Binary files /dev/null and b/server/vbv_lernwelt/static/avatars/avatar_christoph.png differ
diff --git a/server/vbv_lernwelt/static/avatars/avatar_iterativ.png b/server/vbv_lernwelt/static/avatars/avatar_iterativ.png
new file mode 100644
index 00000000..9167b36e
Binary files /dev/null and b/server/vbv_lernwelt/static/avatars/avatar_iterativ.png differ
diff --git a/server/vbv_lernwelt/static/icons/icon-vbv-pos.svg b/server/vbv_lernwelt/static/icons/icon-vbv-pos.svg
new file mode 100644
index 00000000..12aa3e79
--- /dev/null
+++ b/server/vbv_lernwelt/static/icons/icon-vbv-pos.svg
@@ -0,0 +1,37 @@
+
+
+
diff --git a/server/vbv_lernwelt/static/icons/icon-vbv.svg b/server/vbv_lernwelt/static/icons/icon-vbv.svg
new file mode 100644
index 00000000..e4b14657
--- /dev/null
+++ b/server/vbv_lernwelt/static/icons/icon-vbv.svg
@@ -0,0 +1,37 @@
+
+
+
diff --git a/server/vbv_lernwelt/templates/admin/index.html b/server/vbv_lernwelt/templates/admin/index.html
index 61e80fee..27e592a4 100644
--- a/server/vbv_lernwelt/templates/admin/index.html
+++ b/server/vbv_lernwelt/templates/admin/index.html
@@ -5,9 +5,13 @@
{% include "admin/app_list.html" with app_list=app_list show_changelinks=True %}
-
+