diff --git a/.dockerignore b/.dockerignore index 0da5edc..00bc5b6 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,6 @@ *.pyc *.pyo /.env -/data -/static_collected +/docker /node_modules +.git diff --git a/.env-db b/.env-db.example similarity index 100% rename from .env-db rename to .env-db.example diff --git a/.env-local b/.env-local index a6d4e75..e570215 100644 --- a/.env-local +++ b/.env-local @@ -1,2 +1,2 @@ -DATABASE_URL=postgres://postgres@postgres:5432/db -DEFAULT_HAYSTACK_URL=es+https://tcjf1ngoog:qj70l67kk2@tagesschule-elementa-8329801232.eu-west-1.bonsaisearch.net/test-* \ No newline at end of file +DATABASE_URL=postgres://django@MuzQzD6yLyaksfw9f6NUDLsK6Tp7gD7f8uX:5432/db +DEFAULT_HAYSTACK_URL=es+https://tcjf1ngoog:qj70l67kk2@tagesschule-elementa-8329801232.eu-west-1.bonsaisearch.net/test-* diff --git a/.env b/.env.example similarity index 56% rename from .env rename to .env.example index 394df2f..14b70e7 100644 --- a/.env +++ b/.env.example @@ -1,5 +1,13 @@ SECRET_KEY=TEST---asdg4hr63453452542h4sdf25g42s3df54hj38rd4sg3f2d54h3sd5f4g53 -DEBUG=False +DEBUG=True SENTRY_DSN=https://460e310d034c49a794941e087c4fcc6e@sentry.io/1196285 DEFAULT_HAYSTACK_URL=es+https://tcjf1ngoog:qj70l67kk2@tagesschule-elementa-8329801232.eu-west-1.bonsaisearch.net/index-* -DATABASE_URL=postgres://django:MuzQzD6yLyaksfw9f6NUDLsK6Tp7gD7f8uX@postgres:5432/db \ No newline at end of file +DATABASE_URL=postgres://django:MuzQzD6yLyaksfw9f6NUDLsK6Tp7gD7f8uX@postgres:5432/db +HTTP_PORT=8009 +POSTGRES_PASSWORD=MuzQzD6yLyaksfw9f6NUDLsK6Tp7gD7f8uX +POSTGRES_USER=django +POSTGRES_DB=db +POSTGRES_DATA_DIR=./docker/pgdata +MEDIA_DIR=./docker/data/media +DATA_DIR=./docker/data +STATIC_DIR=./docker/static_collected diff --git a/.gitignore b/.gitignore index 7c4695e..98a5307 100644 --- a/.gitignore +++ b/.gitignore @@ -15,16 +15,23 @@ Thumbs.db # Aldryn .aldryn -/data /data.tar.gz -/static_collected +/docker/static_collected /node_modules # -/static/css/ -/static/js/ /static/fonts/ /static/img/ /static/animation/ /.idea /requirements.txt -/conf/certbot/ \ No newline at end of file +/docker/conf/certbot/ +/docker/pgdata/** +/docker/data +.env-nginx +.env-db +.env +docker/storage/** +.bash_history +.cache/ +.local/ +.ssh/ \ No newline at end of file diff --git a/.viminfo b/.viminfo new file mode 100644 index 0000000..82e8630 --- /dev/null +++ b/.viminfo @@ -0,0 +1,69 @@ +# This viminfo file was generated by Vim 8.0. +# You may edit it if you're careful! + +# Viminfo version +|1,4 + +# Value of 'encoding' when this file was written +*encoding=utf-8 + + +# hlsearch on (H) or off (h): +~h +# Command Line History (newest to oldest): +:q +|2,0,1599761890,,"q" + +# Search String History (newest to oldest): + +# Expression History (newest to oldest): + +# Input Line History (newest to oldest): + +# Debug Line History (newest to oldest): + +# Registers: + +# File marks: +'0 1 0 ~/restart.sh +|4,48,1,0,1599761890,"~/restart.sh" +'1 1 0 ~/restart.sh +|4,49,1,0,1599761817,"~/restart.sh" +'2 1 0 ~/restart.sh +|4,50,1,0,1599761501,"~/restart.sh" +'3 1 0 ~/restart.sh +|4,51,1,0,1599761501,"~/restart.sh" +'4 1 0 ~/restart.sh +|4,52,1,0,1599660361,"~/restart.sh" +'5 1 0 ~/restart.sh +|4,53,1,0,1599660361,"~/restart.sh" +'6 1 0 ~/restart.sh +|4,54,1,0,1599660361,"~/restart.sh" +'7 1 0 ~/restart.sh +|4,55,1,0,1599660361,"~/restart.sh" +'8 1 0 ~/restart.sh +|4,56,1,0,1599485736,"~/restart.sh" +'9 1 0 ~/restart.sh +|4,57,1,0,1599485736,"~/restart.sh" + +# Jumplist (newest first): +-' 1 0 ~/restart.sh +|4,39,1,0,1599761890,"~/restart.sh" +-' 1 0 ~/restart.sh +|4,39,1,0,1599761817,"~/restart.sh" +-' 1 0 ~/restart.sh +|4,39,1,0,1599761501,"~/restart.sh" +-' 1 0 ~/restart.sh +|4,39,1,0,1599660361,"~/restart.sh" +-' 1 0 ~/restart.sh +|4,39,1,0,1599485736,"~/restart.sh" +-' 1 0 ~/restart.sh +|4,39,1,0,1599480996,"~/restart.sh" +-' 1 0 ~/restart.sh +|4,39,1,0,1599474185,"~/restart.sh" + +# History of marks within files (newest to oldest): + +> ~/restart.sh + * 1599761887 0 + " 1 0 diff --git a/Dockerfile b/Dockerfile index 6a2024f..4585261 100644 --- a/Dockerfile +++ b/Dockerfile @@ -54,8 +54,8 @@ RUN pip-reqs compile && \ COPY . /app # -RUN mkdir /app/static_collected -RUN mkdir /app/data/media +RUN mkdir -p /app/static_collected +RUN mkdir -p /app/data/media # ENV GULP_MODE=production diff --git a/README.md b/README.md new file mode 100644 index 0000000..c26c159 --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# Tagesschule elementa + +ssh tagesschule@docker.mprofiag.de +sudo ./docker-update.sh + +## Docker + +1. Copy environment files `.env*.example` to `.env*` and make the configuration changes. +Configure database user and ports for docker. + - HTTP_PORT=8009 [.env] ... + + +2. Main app has several mountpoints / volumes. Point them into the appropriate location on +your filesystem + - ./docker/static_collected + - ./docker/data/media + - ./docker/data + +3. To start + + docker-compose up + +4. Restore DB + + docker exec -i tagesschule_db_1 pg_restore -U django --no-owner -d db < 41ebf901-4607-4653-9b00-54a42d877b38.dump + +3. Migrate + + docker-compose exec web manage.py migrate + +5. Add admin user + +In docker container `docker-compose exec web bash` run + + python manage.py shell + +In that shell create admin user (https://stackoverflow.com/questions/18503770/how-to-create-user-from-django-shell) + + user@host> manage.py shell + >>> from django.contrib.auth.models import User + >>> user=User.objects.create_user('foo', password='bar') + >>> user.is_superuser=True + >>> user.is_staff=True + >>> user.save() + diff --git a/bin/gulp b/bin/gulp index c080ee6..58e2d96 100755 --- a/bin/gulp +++ b/bin/gulp @@ -1,2 +1,2 @@ #/bin/bash -exec docker-compose run --rm web gulp "$@" --debug +exec docker-compose run --rm web gulp "$@" --inspect diff --git a/docker-build.sh b/docker-build.sh new file mode 100755 index 0000000..90ebb57 --- /dev/null +++ b/docker-build.sh @@ -0,0 +1,2 @@ +#!/bin/bash +cd /var/www/tagesschule && git pull && docker-compose build web \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index a14bc52..8a27071 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,55 +5,36 @@ services: image: nginx:1.16-alpine restart: unless-stopped volumes: - - ./conf/nginx:/etc/nginx/conf.d - - ./conf/certbot/conf:/etc/letsencrypt - - ./conf/certbot/www:/var/www/certbot - - ./static_collected:/app/static_collected - - ./data/media:/app/data/media + - ./docker/conf/nginx:/etc/nginx/conf.d + - ./docker/static_collected:/app/static_collected + - ${MEDIA_DIR}:/app/data/media ports: - - "80:80" - - "443:443" - command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'" + - ${HTTP_PORT}:80 + #command: "/bin/sh -c 'while :; do sleep 1m & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'" depends_on: - web - networks: - - nginx_network - - certbot: - image: certbot/certbot - restart: unless-stopped - volumes: - - ./conf/certbot/conf:/etc/letsencrypt - - ./conf/certbot/www:/var/www/certbot - entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'" - + network_mode: bridge + links: + - web web: + restart: unless-stopped build: "." links: - "db:postgres" volumes: - - ".:/app:rw" - - "./data:/data:rw" - - ./static_collected:/app/static_collected - - ./data/media:/app/data/media - networks: - - nginx_network - - db_network + - .:/app:rw + - ${DATA_DIR}:/data:rw + - ${STATIC_DIR}:/app/static_collected + - ${MEDIA_DIR}:/app/data/media + network_mode: bridge command: "/bin/sh -c '/app/wait-for-postgres.sh postgres /app/run.sh'" env_file: - ./.env db: + restart: unless-stopped image: postgres:9.6-alpine env_file: - ./.env-db volumes: - - ".:/app:rw" - - "./pgdata:/var/lib/postgresql/data:rw" - networks: - - db_network - -networks: - nginx_network: - driver: bridge - db_network: - driver: bridge \ No newline at end of file + - ${POSTGRES_DATA_DIR}:/var/lib/postgresql/data:rw + network_mode: bridge diff --git a/docker-logs.sh b/docker-logs.sh new file mode 100755 index 0000000..307c2ff --- /dev/null +++ b/docker-logs.sh @@ -0,0 +1,2 @@ +#!/bin/bash +cd /var/www/tagesschule && docker-compose logs --tail=100 -f diff --git a/docker-restart.sh b/docker-restart.sh new file mode 100755 index 0000000..82b7879 --- /dev/null +++ b/docker-restart.sh @@ -0,0 +1,2 @@ +#!/bin/bash +cd /var/www/tagesschule && git pull && docker-compose restart web \ No newline at end of file diff --git a/docker-up.sh b/docker-up.sh new file mode 100755 index 0000000..5f74f85 --- /dev/null +++ b/docker-up.sh @@ -0,0 +1,2 @@ +#!/bin/bash +cd /var/www/tagesschule && docker-compose up \ No newline at end of file diff --git a/docker-update.sh b/docker-update.sh new file mode 100755 index 0000000..9f9e2bd --- /dev/null +++ b/docker-update.sh @@ -0,0 +1,2 @@ +#!/bin/bash +cd /var/www/tagesschule && git pull && docker-compose build web && docker-compose up \ No newline at end of file diff --git a/docker-web-cli.sh b/docker-web-cli.sh new file mode 100755 index 0000000..0e08add --- /dev/null +++ b/docker-web-cli.sh @@ -0,0 +1,2 @@ +#!/bin/bash +cd /var/www/tagesschule && docker-compose exec web bash \ No newline at end of file diff --git a/conf/nginx/app.conf b/docker/conf/nginx/app.conf similarity index 66% rename from conf/nginx/app.conf rename to docker/conf/nginx/app.conf index c8ba7da..ed01236 100644 --- a/conf/nginx/app.conf +++ b/docker/conf/nginx/app.conf @@ -2,34 +2,16 @@ upstream gunicorn { server web:80; } - server { listen 80; server_name tagesschule.mprofiag.ch; server_tokens off; - - location /.well-known/acme-challenge/ { - root /var/www/certbot; - } - - location / { - return 301 https://$host$request_uri; - } -} - -server { - listen 443 ssl; - server_name tagesschule.mprofiag.ch; - server_tokens off; sendfile on; - ssl_certificate /etc/letsencrypt/live/tagesschule.mprofiag.ch/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/tagesschule.mprofiag.ch/privkey.pem; - include /etc/letsencrypt/options-ssl-nginx.conf; - ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; - add_header X-Frame-Options ""; + client_max_body_size 1000M; + gzip on; gzip_http_version 1.1; gzip_disable "MSIE [1-6]\."; diff --git a/docker/pgdata/.gitkeep b/docker/pgdata/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/private/js/modules/_reveal.js b/private/js/modules/_reveal.js index 9664999..5d26c94 100644 --- a/private/js/modules/_reveal.js +++ b/private/js/modules/_reveal.js @@ -54,6 +54,7 @@ $(function() { reveal_element($('.header__logo')); reveal_elements($('.timetable__clock__frame')); reveal_element($('.timetable__next')); + reveal_element($('.timetable__button')); reveal_element($('.timetable__start__background')); }); \ No newline at end of file diff --git a/private/js/modules/plugins/form.js b/private/js/modules/plugins/form.js index 78ba305..3cb3ad7 100755 --- a/private/js/modules/plugins/form.js +++ b/private/js/modules/plugins/form.js @@ -13,11 +13,14 @@ $(function() { url: $form.attr('action'), data: $form.serialize(), success: function(data) { + django_recaptcha_callbacks = []; var $new = $(data).find('#' + id); $new.find('.reveal').each(function() { $(this).removeClass('reveal reveal_animation'); }); + $new.removeClass('reveal reveal_animation'); $form.replaceWith($new); + djangoRecaptchaOnLoadCallback(); } }); }); diff --git a/private/js/modules/plugins/timetable.js b/private/js/modules/plugins/timetable.js index bbdc82e..9cc0bf0 100644 --- a/private/js/modules/plugins/timetable.js +++ b/private/js/modules/plugins/timetable.js @@ -1,4 +1,4 @@ -$(function() { +$(function () { var $body = $('body'); var $timetable = $('#timetable'); @@ -9,7 +9,7 @@ $(function() { $(window).scrollTop(0); window.timetable_can_scroll = false; - window.setTimeout(function() { + window.setTimeout(function () { window.timetable_can_scroll = true; }, 2500); @@ -39,17 +39,17 @@ $(function() { function init_timetable_items() { $timetable.attr('data-active', 1); - $timetable.find('.timetable__item').each(function() { + $timetable.find('.timetable__item').each(function () { $(this).addClass('reveal_container') }); - window.setTimeout(function() { + window.setTimeout(function () { $timetable.addClass('active'); - window.requestAnimationFrame(function() { - $timetable.find('.timetable__item').each(function() { + window.requestAnimationFrame(function () { + $timetable.find('.timetable__item').each(function () { var index = parseInt($(this).attr('data-id')); var elementWatcher = scrollMonitor.create(this); - elementWatcher.enterViewport(function() { + elementWatcher.enterViewport(function () { set_timetable_item(index); }); }); @@ -76,7 +76,7 @@ $(function() { var $timetable_progress = $('#timetable__clock__progress'); var timetable_clock_progress_max = parseFloat($timetable_progress.attr('stroke-dasharray')); - $body.on('click', '.timetable__next', function(event) { + $body.on('click', '.timetable__next', function (event) { event.preventDefault(); if (!activated) { $(window).scrollTop($(window).height()); @@ -146,4 +146,45 @@ $(function() { $timetable_progress.attr('stroke-dashoffset', timetable_clock_progress_max * Math.abs(total_progress - 1)); } } + + document.querySelector('.timetable__button a').addEventListener('click', function (event) { + event.preventDefault(); + var player; + var vimeo_id = document.querySelector('.timetable__button').getAttribute('data-video-vimeo-id'); + + var pswpElement = document.querySelectorAll('.pswp')[0], + gallery, + options, + items; + + options = { + showHideOpacity: true, + download: false, + fullscreenEl: false, + shareEl: false, + bgOpacity: 0.85, + mainClass: 'timetable__pswp', + }; + + items = [ + { + html: '', + }, + ]; + + var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options); + + gallery.listen('close', function () { + document.querySelector('#timetable').style.height = ''; + player.pause(); + }); + + gallery.init(); + + document.querySelector('#timetable').style.height = '100%'; + var iframe = document.querySelector('.pswp iframe'); + player = new Vimeo.Player(iframe); + player.play(); + }); + }); \ No newline at end of file diff --git a/private/js/modules/privacy.js b/private/js/modules/privacy.js index 6b9eeb2..9c278a3 100644 --- a/private/js/modules/privacy.js +++ b/private/js/modules/privacy.js @@ -11,14 +11,14 @@ $(function() { $body.on('click', '.privacy_action--accept', function(event) { event.preventDefault(); - setCookie(cookie_name, true, 360); + setCookie(cookie_name, 'true', 60); $privacy_message.remove(); $body.trigger('enable_tracking'); }); $body.on('click', '.privacy_action--decline', function(event) { event.preventDefault(); - setCookie(cookie_name, false, 360); + setCookie(cookie_name, 'false', 60); $privacy_message.remove(); }); diff --git a/private/scss/main.scss b/private/scss/main.scss index 051789a..3aebdc2 100644 --- a/private/scss/main.scss +++ b/private/scss/main.scss @@ -38,3 +38,4 @@ @import "modules/plugins/_timetable.scss"; @import "modules/plugins/_reference_list.scss"; @import "modules/plugins/_form.scss"; +@import "modules/plugins/_iframe.scss"; diff --git a/private/scss/modules/plugins/_form.scss b/private/scss/modules/plugins/_form.scss index e2a947c..1acd20f 100644 --- a/private/scss/modules/plugins/_form.scss +++ b/private/scss/modules/plugins/_form.scss @@ -153,6 +153,29 @@ min-width: em(200px); } +.button--icon { + white-space: nowrap; + transform: none; + transition: transform 0.4s $easeOutQuart; + .button__icon { + display: inline-block; + vertical-align: top; + position: relative; + transform: none !important; + width: auto; + padding-left: em(15px); + } + .button__text { + vertical-align: top; + transform: none !important; + padding-left: 0; + min-width: auto; + } + &:hover { + transform: scale(1.05); + } +} + .form__field { width: 100%; display: block; diff --git a/private/scss/modules/plugins/_gallery.scss b/private/scss/modules/plugins/_gallery.scss index 77a5a53..9793f1f 100644 --- a/private/scss/modules/plugins/_gallery.scss +++ b/private/scss/modules/plugins/_gallery.scss @@ -3,6 +3,25 @@ font-size: 0; } +.gallery__iframe { + width: 100%; + max-width: 140vh; + margin: auto; + position: relative; + top: 50%; + transform: translateY(-50%); +} + +.gallery__iframe__main { + width: 100%; + padding-bottom: 56.25%; + position: relative; +} + +.gallery__iframe__main iframe { + position: absolute; +} + .gallery__item { display: inline-block; vertical-align: top; diff --git a/private/scss/modules/plugins/_iframe.scss b/private/scss/modules/plugins/_iframe.scss new file mode 100644 index 0000000..01db66f --- /dev/null +++ b/private/scss/modules/plugins/_iframe.scss @@ -0,0 +1,3 @@ +.iframe { + margin: em(50px) 0; +} diff --git a/private/scss/modules/plugins/_section.scss b/private/scss/modules/plugins/_section.scss index 281ff57..bccf24f 100644 --- a/private/scss/modules/plugins/_section.scss +++ b/private/scss/modules/plugins/_section.scss @@ -6,4 +6,8 @@ width: 100%; display: block; height: 1px; -} \ No newline at end of file +} + +.spacer ~ .spacer { + height: em(50px); +} diff --git a/private/scss/modules/plugins/_timetable.scss b/private/scss/modules/plugins/_timetable.scss index c85072e..2d9bbc4 100644 --- a/private/scss/modules/plugins/_timetable.scss +++ b/private/scss/modules/plugins/_timetable.scss @@ -11,9 +11,44 @@ $timetable_count: 15; } } +.timetable__button { + opacity: 0; + position: fixed; + bottom: 150px; + left: 50%; + transform: translateX(-50%); + z-index: 1000; + transition: opacity 0.5s $easeOutQuad, transform 0.5s $easeOutQuad; + #timetable[data-active="0"] & { + opacity: 1; + } + img { + position: absolute; + top: 50%; + left: 50%; + width: 40px; + height: 20px; + margin-left: -20px; + } + &.reveal { + transform: translateX(-50%) scale(1.2); + } + @media screen and (max-width: $huge_breakpoint) { + bottom: 130px; + } + @media screen and (max-width: $large_breakpoint) { + bottom: 120px; + } +} + +.timetable__pswp .pswp__top-bar { + opacity: 1; + background: transparent; +} + .timetable__clock__frame { position: fixed; - top: 50%; + top: 40%; transform: translateY(-50%); width: 30%; left: 35%; @@ -25,10 +60,10 @@ $timetable_count: 15; left: 50%; max-width: 40vh; transform: translateX(-50%) translateY(14%); - #timetable[data-active="0"] & { - top: 50%; - transform: translateX(-50%) translateY(-50%); - } +// #timetable[data-active="0"] & { +// top: 50%; +// transform: translateX(-50%) translateY(-50%); +// } } } diff --git a/requirements.in b/requirements.in index 263c27c..484c0b4 100644 --- a/requirements.in +++ b/requirements.in @@ -15,20 +15,31 @@ https://control.divio.com/api/v1/apps/serve/django-filer/1.4.1/e7e860ea-0af6-4fc elasticsearch==6.4.0 django-spurl==0.6.5 aldryn-search==1.0.1 -django-fontawesome==0.3.1 +django-fontawesome==1.0 mailchimp3==2.1.0 django-image-cropping==1.2.0 -django-anymail[mailgun]==1.4 django-admin-view-permission==1.9 gunicorn==19.9.0 sentry-sdk==0.14.3 -aldryn-forms-recaptcha-plugin==1.0.0.2 django-recaptcha2==1.4.1 # compat versions -django-storages<1.9 +django-storages<1.9 # https://stackoverflow.com/questions/60297619/divio-importerror-cannot-import-name-s3boto psycopg2<2.8 django-parler<=2.1 django-select2<=6.3.1 +django-formtools==2.2 django-sekizai<=1.1.0 django-classy-tags<=1.0.0 -djangocms-attributes-field<=1.2.0 \ No newline at end of file +djangocms-attributes-field<=1.2.0 +django-haystack==2.8.1 +django-simple-captcha==0.5.12 +django-treebeard==4.3.1 +django-meta==1.7.0 +tablib==0.14.0 +django-simple-sso==0.14.1 +easy-thumbnails==2.7.1 +dj-database-url==0.5.0 +django-js-asset==1.2.3 +idna==3.4 +cssselect==1.1.0 +lxml-html-clean==0.1.1 diff --git a/settings.py b/settings.py index 119079a..d95c04c 100644 --- a/settings.py +++ b/settings.py @@ -38,8 +38,7 @@ aldryn_addons.settings.load(locals()) INSTALLED_APPS.insert(0, 'admin_view_permission') INSTALLED_APPS.extend([ - 'aldryn_forms_recaptcha_plugin', - 'snowpenguin.django.recaptcha3', + 'snowpenguin.django.recaptcha2', 'portal', 'project', 'fontawesome', @@ -205,12 +204,13 @@ CKEDITOR_SETTINGS_TIMETABLE_ITEM_TITLE['bodyClass'] = 'h2 timetable__item__title CKEDITOR_SETTINGS_TIMETABLE_ITEM_TEXT = copy.deepcopy(CKEDITOR_SETTINGS_INPUT) CKEDITOR_SETTINGS_TIMETABLE_ITEM_TEXT['bodyClass'] = 'section__text timetable__item__text' -ANYMAIL = { - 'MAILGUN_API_KEY': 'key-f6625f8850326f8774b2f587c0c41cd2', - 'MAILGUN_SENDER_DOMAIN': 'mg.tagesschule-elementa.ch', -} -EMAIL_BACKEND = 'anymail.backends.mailgun.EmailBackend' -DEFAULT_FROM_EMAIL = 'system@tagesschule-elementa.ch' +DEFAULT_FROM_EMAIL = 'web@tagesschule-elementa.ch' + +EMAIL_USE_TLS = True +EMAIL_HOST = 'smtp.office365.com' +EMAIL_HOST_USER = DEFAULT_FROM_EMAIL +EMAIL_HOST_PASSWORD = 'TagesschuleElementa1234' +EMAIL_PORT = 587 FILER_ENABLE_PERMISSIONS = True @@ -255,11 +255,18 @@ if not DEBUG: 'tagesschule-elementa.ch', 'www.tagesschule-elementa.ch', 'tagesschule.mprofiag.ch', + 'docker.mprofiag.de' ] DATA_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 1024 -RECAPTCHA_PUBLIC_KEY = '6Lec78gZAAAAANc-oxXJPMi7BXmINlP-QkcS937g' -RECAPTCHA_PRIVATE_KEY = '6Lec78gZAAAAADuIppqW7cSh6iPw3TZQ9r-ogHtz' +RECAPTCHA_PUBLIC_KEY = '6LeILd0ZAAAAAB9xO_y8kS292wv2ikl0M8s7zFn9' +RECAPTCHA_PRIVATE_KEY = '6LeILd0ZAAAAAOGF1AvxdiGcXWLjr2BzHaQ8Zush' RECAPTCHA_SCORE_THRESHOLD = 0.5 + +ALDRYN_FORMS_ACTION_BACKENDS = { + 'default': 'project.action_backends.DefaultAction', + 'email_only': 'aldryn_forms.action_backends.EmailAction', + 'none': 'aldryn_forms.action_backends.NoAction', +} diff --git a/src/portal/migrations/0005_auto_20200325_1611.py b/src/portal/migrations/0005_auto_20200325_1611.py new file mode 100644 index 0000000..fdb7e7c --- /dev/null +++ b/src/portal/migrations/0005_auto_20200325_1611.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.21 on 2020-03-25 16:11 +from __future__ import unicode_literals + +from django.db import migrations +import django.db.models.deletion +import parler.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('portal', '0004_auto_20180718_1754'), + ] + + operations = [ + migrations.AlterModelOptions( + name='announcement', + options={'ordering': ['-updated'], 'verbose_name': 'Neuigkeit', 'verbose_name_plural': 'Aktuell'}, + ), + migrations.AlterField( + model_name='announcementtranslation', + name='master', + field=parler.fields.TranslationsForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='portal.Announcement'), + ), + migrations.AlterField( + model_name='downloadfiletranslation', + name='master', + field=parler.fields.TranslationsForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='portal.DownloadFile'), + ), + migrations.AlterField( + model_name='downloadsectiontranslation', + name='master', + field=parler.fields.TranslationsForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='portal.DownloadSection'), + ), + migrations.AlterField( + model_name='informationsectiontranslation', + name='master', + field=parler.fields.TranslationsForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='portal.InformationSection'), + ), + migrations.AlterField( + model_name='informationtranslation', + name='master', + field=parler.fields.TranslationsForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='portal.Information'), + ), + ] diff --git a/src/project/action_backends.py b/src/project/action_backends.py new file mode 100644 index 0000000..77a1a1e --- /dev/null +++ b/src/project/action_backends.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +from aldryn_forms.action_backends_base import BaseAction + +class DefaultAction(BaseAction): + verbose_name = 'Default' + + def form_valid(self, cmsplugin, instance, request, form): + email = form.cleaned_data.get('emailfield_1', None) + if email and email in ['eric.jones.z.mail@gmail.com']: + return + recipients = cmsplugin.send_notifications(instance, form) + form.instance.set_recipients(recipients) + form.save() diff --git a/src/project/admin.py b/src/project/admin.py index f3c0021..6ea1195 100644 --- a/src/project/admin.py +++ b/src/project/admin.py @@ -2,8 +2,10 @@ from django.contrib import admin from cms.extensions import PageExtensionAdmin from parler.admin import TranslatableAdmin - +from aldryn_forms.models import FormSubmission +from aldryn_forms.admin.base import BaseFormSubmissionAdmin from project.models import ImageExtension, SliderItemQualification, Notification +from project.views import CustomFormExportWizardView @admin.register(ImageExtension) @@ -19,3 +21,15 @@ class SliderItemQualificationAdmin(TranslatableAdmin): @admin.register(Notification) class NotificationAdmin(TranslatableAdmin): pass + + +admin.site.unregister(FormSubmission) + + +@admin.register(FormSubmission) +class FormSubmissionAdmin(BaseFormSubmissionAdmin): + + def get_form_export_view(self): + return CustomFormExportWizardView.as_view(admin=self, file_type='xls') + + diff --git a/src/project/cms_plugins.py b/src/project/cms_plugins.py index 5972fd7..1bfcfaf 100644 --- a/src/project/cms_plugins.py +++ b/src/project/cms_plugins.py @@ -8,11 +8,14 @@ from djangocms_picture.cms_plugins import PicturePlugin as _PicturePlugin from djangocms_text_ckeditor.cms_plugins import TextPlugin as _TextPlugin from django.contrib import admin from mailchimp3 import MailChimp +from aldryn_forms.cms_plugins import Field +from snowpenguin.django.recaptcha2.fields import ReCaptchaField +from snowpenguin.django.recaptcha2.widgets import ReCaptchaWidget from project.forms import NewsletterSubscriptionForm from project.models import Section, Quote, SliderItem, SectionText, Video, DownloadSection, DownloadSectionFolder, \ TextSliderItem, HighlightListItem, ReferenceListItem, SocialMediaList, SocialMediaListItem, Timetable, \ - TimetableItem, Partner, HighlightList, Image, TitleListItem, TitleList, IntroImage, Gallery + TimetableItem, Partner, HighlightList, Image, TitleListItem, TitleList, IntroImage, Gallery, Iframe @plugin_pool.register_plugin @@ -26,7 +29,7 @@ class SectionPlugin(CMSPluginBase): 'SectionTextPlugin', 'VideoPlugin', 'DownloadSectionPlugin', 'TextSliderPlugin', 'HighlightListPlugin', 'ReferenceListPlugin', 'FormPlugin', 'PicturePlugin', 'SubPageListPlugin', 'PartnerPlugin', 'NewsletterSubscriptionPlugin', 'NewsletterArchivePlugin', - 'SocialMediaListPlugin', 'GalleryPlugin'] + 'SocialMediaListPlugin', 'GalleryPlugin', 'IframePlugin'] @plugin_pool.register_plugin @@ -206,6 +209,24 @@ class FormPlugin(_FormPlugin): 'SubmitButton', 'ReCaptchaFieldPlugin'] +@plugin_pool.register_plugin +class ReCaptchaFieldPlugin(Field): + name = 'ReCaptcha Field' + render_template = True + allow_children = False + form_field = ReCaptchaField + form_field_widget = ReCaptchaWidget + + form_field_enabled_options = [ + 'error_messages', + ] + fieldset_general_fields = [] + fieldset_advanced_fields = [] + + def get_form_field_widget_kwargs(self, instance): + return {'explicit': True} + + class SocialMediaListItemInlineAdmin(admin.TabularInline): model = SocialMediaListItem extra = 0 @@ -327,3 +348,11 @@ class GalleryPlugin(CMSPluginBase): module = 'Content' name = 'Gallery' render_template = 'project/plugins/content/gallery.html' + + +@plugin_pool.register_plugin +class IframePlugin(CMSPluginBase): + model = Iframe + module = 'Content' + name = 'Iframe' + render_template = 'project/plugins/content/iframe.html' diff --git a/src/project/migrations/0010_auto_20200325_1611.py b/src/project/migrations/0010_auto_20200325_1611.py new file mode 100644 index 0000000..670e3fb --- /dev/null +++ b/src/project/migrations/0010_auto_20200325_1611.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.21 on 2020-03-25 16:11 +from __future__ import unicode_literals + +from django.db import migrations +import django.db.models.deletion +import parler.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('project', '0009_auto_20190214_1250'), + ] + + operations = [ + migrations.AlterField( + model_name='notificationtranslation', + name='master', + field=parler.fields.TranslationsForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='project.Notification'), + ), + migrations.AlterField( + model_name='slideritemqualificationtranslation', + name='master', + field=parler.fields.TranslationsForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='project.SliderItemQualification'), + ), + ] diff --git a/src/project/migrations/0011_auto_20211104_1608.py b/src/project/migrations/0011_auto_20211104_1608.py new file mode 100644 index 0000000..a51424f --- /dev/null +++ b/src/project/migrations/0011_auto_20211104_1608.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.21 on 2021-11-04 16:08 +from __future__ import unicode_literals + +from django.db import migrations, models +import djangocms_text_ckeditor.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('project', '0010_auto_20200325_1611'), + ] + + operations = [ + migrations.AddField( + model_name='timetable', + name='video_cta', + field=djangocms_text_ckeditor.fields.HTMLField(blank=True, null=True, verbose_name='Video CTA'), + ), + migrations.AddField( + model_name='timetable', + name='video_vimeo_id', + field=models.IntegerField(blank=True, help_text='e.g. https://vimeo.com/131766159', null=True, verbose_name='Video Vimeo ID'), + ), + ] diff --git a/src/project/migrations/0012_iframe.py b/src/project/migrations/0012_iframe.py new file mode 100644 index 0000000..7361d28 --- /dev/null +++ b/src/project/migrations/0012_iframe.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.21 on 2024-05-23 15:51 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('cms', '0020_old_tree_cleanup'), + ('project', '0011_auto_20211104_1608'), + ] + + operations = [ + migrations.CreateModel( + name='Iframe', + fields=[ + ('cmsplugin_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='project_iframe', serialize=False, to='cms.CMSPlugin')), + ('html', models.TextField(verbose_name='HTML')), + ], + options={ + 'verbose_name': 'Iframe', + 'verbose_name_plural': 'Iframes', + 'abstract': False, + }, + bases=('cms.cmsplugin',), + ), + ] diff --git a/src/project/models.py b/src/project/models.py index bdcc755..8b4ed51 100644 --- a/src/project/models.py +++ b/src/project/models.py @@ -265,6 +265,8 @@ class Timetable(CMSPlugin): introduction = HTMLField(verbose_name='Intro', configuration='CKEDITOR_SETTINGS_INPUT') outro = HTMLField(verbose_name='Outro', configuration='CKEDITOR_SETTINGS_INPUT', blank=True, null=True) cta = HTMLField(verbose_name='CTA', configuration='CKEDITOR_SETTINGS_INPUT', blank=True, null=True) + video_vimeo_id = models.IntegerField(verbose_name='Video Vimeo ID', blank=True, null=True, help_text='e.g. https://vimeo.com/131766159') + video_cta = HTMLField(verbose_name='Video CTA', configuration='CKEDITOR_SETTINGS_INPUT', blank=True, null=True) class Meta(CMSPlugin.Meta): verbose_name = 'Timetable' @@ -347,3 +349,14 @@ class Gallery(CMSPlugin): @property def files(self): return self.folder.files.all().order_by('name') + + +class Iframe(CMSPlugin): + html = models.TextField('HTML') + + class Meta(CMSPlugin.Meta): + verbose_name = 'Iframe' + verbose_name_plural = 'Iframes' + + def __str__(self): + return self.html diff --git a/src/project/templates/main.html b/src/project/templates/main.html index b232884..8390fc1 100644 --- a/src/project/templates/main.html +++ b/src/project/templates/main.html @@ -1,4 +1,4 @@ -{% load static i18n cms_tags sekizai_tags fontawesome menu_tags %} +{% load static i18n cms_tags sekizai_tags fontawesome menu_tags recaptcha2 %} @@ -10,6 +10,8 @@ + {% recaptcha_explicit_support %} + {% block extra_meta %} @@ -175,12 +177,14 @@