Compare commits

..

47 Commits

Author SHA1 Message Date
Simon Caminada 919dd33373 fix: local docker-compose setup 3 years ago
Simon Caminada 061a99e77e fix: update docs 3 years ago
Simon Caminada 888daefd17 fix: updated smtp password 4 years ago
Simon Caminada 80685dd71f fix: spacer not working 4 years ago
Simon Caminada e36afcb527 fix 4 years ago
Simon Caminada 2d162d0102 add static to git 4 years ago
Simon Caminada d2270543b9 add static 4 years ago
Simon Caminada b456ff219c update timetable button 4 years ago
Simon Caminada 97d972fbe1 add video to timetable 4 years ago
Simon Caminada 1918797b1a fix vimeo autoplay 4 years ago
Simon Caminada 01eb2066b9 add timeout to video play 4 years ago
Simon Caminada ccd9828781 custom aldryn_forms export 5 years ago
Simon Caminada 47a26a0656 updated requirements 5 years ago
Simon Caminada 8c5b291d24 fix password 5 years ago
Simon Caminada 9a2717d4c0 fix mail password 5 years ago
Simon Caminada 00cc0ac532 try to add static to git 5 years ago
Simon Caminada c6610b8c5c recaptcha form fix 5 years ago
Simon Caminada 322786cdba fix privacy 5 years ago
Simon Caminada 35cb78284e fix compat version 5 years ago
Simon Caminada c4a25cbe1c clean up anymail 5 years ago
Simon Caminada 4627c2342f update mail settings 5 years ago
Simon Caminada c420c84d39 fix email infos 5 years ago
root 96b61a1add Renamed and new scripts 5 years ago
Simon Caminada 47669c4d04 Merge branch 'standalone' into mprofiag
# Conflicts:
#	requirements.in
5 years ago
Simon Caminada 2dd84d27f0 add recaptcha plugin 5 years ago
root 49e1467147 Changes from server 5 years ago
Petr Šnobl f454305849 link 6 years ago
Petr Šnobl a018f86f42 bridge network mode 6 years ago
Simon Caminada 8eeb5f5947 fix 6 years ago
Simon Caminada 0dfeb359ac fix 6 years ago
Petr Šnobl a5290f84f3 Merge branch 'standalone' into mprofiag
# Conflicts:
#	requirements.in
6 years ago
Simon Caminada ee63555dd8 sentry integration 6 years ago
Petr Šnobl b065becd9a Merge branch 'standalone' into mprofiag 6 years ago
Simon Caminada 17b492c0e1 fix page item 6 years ago
Petr Šnobl 51b089f9e8 also in nginx 6 years ago
Petr Šnobl f3cd332ec4 Merge branch 'standalone' into mprofiag 6 years ago
Simon Caminada 00f5259f13 DATA_UPLOAD_MAX_MEMORY_SIZE 6 years ago
Petr Šnobl e15c162ec0 static files 6 years ago
Petr Šnobl 831b779e7a allowed docker - fix 6 years ago
Petr Šnobl 2ec3daeab7 allowed docker - fix 6 years ago
Petr Šnobl c1cd63e6a6 allowed docker 6 years ago
Petr Šnobl 2fbaa20b50 restart 6 years ago
Petr Šnobl 0cb9122f2d Creating admin user notice 6 years ago
Petr Šnobl e710dab605 creating dir 6 years ago
Petr Šnobl b8e84b204e Directories in .env 6 years ago
Petr Šnobl e6d9a13f22 git should not be in the app 6 years ago
Petr Šnobl b90dd00590 Corrected docker-compose.yml for mprofiag environment 6 years ago

@ -1,6 +1,6 @@
*.pyc
*.pyo
/.env
/data
/static_collected
/docker
/node_modules
.git

@ -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-*
DATABASE_URL=postgres://django@MuzQzD6yLyaksfw9f6NUDLsK6Tp7gD7f8uX:5432/db
DEFAULT_HAYSTACK_URL=es+https://tcjf1ngoog:qj70l67kk2@tagesschule-elementa-8329801232.eu-west-1.bonsaisearch.net/test-*

@ -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
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

17
.gitignore vendored

@ -15,16 +15,23 @@ Thumbs.db
# Aldryn
.aldryn
/data
/data.tar.gz
/static_collected
/docker/static_collected
/node_modules
# </DEFAULT>
/static/css/
/static/js/
/static/fonts/
/static/img/
/static/animation/
/.idea
/requirements.txt
/conf/certbot/
/docker/conf/certbot/
/docker/pgdata/**
/docker/data
.env-nginx
.env-db
.env
docker/storage/**
.bash_history
.cache/
.local/
.ssh/

@ -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

@ -54,8 +54,8 @@ RUN pip-reqs compile && \
COPY . /app
# </SOURCE>
RUN mkdir /app/static_collected
RUN mkdir /app/data/media
RUN mkdir -p /app/static_collected
RUN mkdir -p /app/data/media
# <GULP>
ENV GULP_MODE=production

@ -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()

@ -1,2 +1,2 @@
#/bin/bash
exec docker-compose run --rm web gulp "$@" --debug
exec docker-compose run --rm web gulp "$@" --inspect

@ -0,0 +1,2 @@
#!/bin/bash
cd /var/www/tagesschule && git pull && docker-compose build web

@ -1,45 +1,16 @@
version: '3'
services:
nginx:
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
ports:
- "80:80"
- "443:443"
command: "/bin/sh -c 'while :; do sleep 6h & 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;'"
web:
build: "."
links:
- "db:postgres"
ports:
- "8007:80"
volumes:
- ".:/app:rw"
- "./data:/data:rw"
- ./static_collected:/app/static_collected
- ./data/media:/app/data/media
networks:
- nginx_network
- db_network
command: "/bin/sh -c '/app/wait-for-postgres.sh postgres /app/run.sh'"
command: python manage.py runserver 0.0.0.0:80
env_file:
- ./.env
db:
@ -48,12 +19,3 @@ services:
- ./.env-db
volumes:
- ".:/app:rw"
- "./pgdata:/var/lib/postgresql/data:rw"
networks:
- db_network
networks:
nginx_network:
driver: bridge
db_network:
driver: bridge

@ -0,0 +1,2 @@
#!/bin/bash
cd /var/www/tagesschule && docker-compose logs --tail=100 -f

@ -0,0 +1,2 @@
#!/bin/bash
cd /var/www/tagesschule && git pull && docker-compose restart web

@ -0,0 +1,2 @@
#!/bin/bash
cd /var/www/tagesschule && docker-compose up

@ -0,0 +1,2 @@
#!/bin/bash
cd /var/www/tagesschule && git pull && docker-compose build web && docker-compose up

@ -0,0 +1,2 @@
#!/bin/bash
cd /var/www/tagesschule && docker-compose exec web bash

@ -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]\.";

@ -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'));
});

@ -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();
}
});
});

@ -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: '<div class="gallery__iframe"><div class="gallery__iframe__main"><iframe src="https://player.vimeo.com/video/' + vimeo_id + '" width="100%" height="100%" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe></div></div>',
},
];
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();
});
});

@ -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();
});

@ -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;

@ -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;

@ -6,4 +6,8 @@
width: 100%;
display: block;
height: 1px;
}
}
.spacer ~ .spacer {
height: em(50px);
}

@ -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%);
// }
}
}

@ -15,9 +15,26 @@ 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
gunicorn==19.9.0
sentry-sdk==0.14.3
django-recaptcha2==1.4.1
# compat versions
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
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

@ -1,6 +1,17 @@
# -*- coding: utf-8 -*-
import copy
import os
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
sentry_sdk.init(
dsn="https://460e310d034c49a794941e087c4fcc6e@o74423.ingest.sentry.io/1196285",
integrations=[DjangoIntegration()],
# If you wish to associate users to errors (assuming you are using
# django.contrib.auth) you may enable sending PII data.
send_default_pii=True
)
INSTALLED_ADDONS = [
# <INSTALLED_ADDONS> # Warning: text inside the INSTALLED_ADDONS tags is auto-generated. Manual changes will be overwritten.
@ -27,6 +38,7 @@ aldryn_addons.settings.load(locals())
INSTALLED_APPS.insert(0, 'admin_view_permission')
INSTALLED_APPS.extend([
'snowpenguin.django.recaptcha2',
'portal',
'project',
'fontawesome',
@ -192,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
@ -242,4 +255,12 @@ 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 = '6LeILd0ZAAAAAB9xO_y8kS292wv2ikl0M8s7zFn9'
RECAPTCHA_PRIVATE_KEY = '6LeILd0ZAAAAAOGF1AvxdiGcXWLjr2BzHaQ8Zush'
RECAPTCHA_SCORE_THRESHOLD = 0.5

@ -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'),
),
]

@ -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')

@ -8,6 +8,9 @@ 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, \
@ -203,12 +206,25 @@ class FormPlugin(_FormPlugin):
module = 'Content'
name = 'Form'
child_classes = ['TextField', 'TextAreaField', 'EmailField', 'RadioSelectField', 'MultipleSelectField',
'SubmitButton']
'SubmitButton', 'ReCaptchaFieldPlugin']
def send_notifications(self, instance, form):
if dict(form.get_serialized_field_choices()).get('honeypot', ''):
return []
return super(FormPlugin, self).send_notifications(instance, form)
@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):

@ -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'),
),
]

@ -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/<b>131766159</b>', null=True, verbose_name='Video Vimeo ID'),
),
]

@ -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/<b>131766159</b>')
video_cta = HTMLField(verbose_name='Video CTA', configuration='CKEDITOR_SETTINGS_INPUT', blank=True, null=True)
class Meta(CMSPlugin.Meta):
verbose_name = 'Timetable'

@ -1,4 +1,4 @@
{% load static i18n cms_tags sekizai_tags fontawesome menu_tags %}<!DOCTYPE html>
{% load static i18n cms_tags sekizai_tags fontawesome menu_tags recaptcha2 %}<!DOCTYPE html>
<html class="r" lang="{{ LANGUAGE_CODE }}">
<head>
<meta http-equiv="x-ua-compatible" content="ie=edge">
@ -10,6 +10,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="google" content="notranslate">
{% recaptcha_explicit_support %}
<meta name="msvalidate.01" content="7E7A1E68251AEDF0AF320164C774E3E8"/>
{% block extra_meta %}
@ -233,5 +235,7 @@
});
});
</script>
{% recaptcha_explicit_init LANGUAGE_CODE %}
</body>
</html>

@ -25,7 +25,7 @@
{% if search %}
<p>{% highlight page.text with request.GET.q %}</p>
{% else %}
{% elif text and text != 'None' %}
<p>{{ text }}</p>
{% endif %}

@ -1,7 +1,7 @@
{% load i18n thumbnail util_tags %}
<div class="video reveal_container">
<iframe class="video__frame" src="https://player.vimeo.com/video/{{ instance.vimeo_id }}?dnt=1" width="640" height="360" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
<iframe class="video__frame" src="https://player.vimeo.com/video/{{ instance.vimeo_id }}?dnt=1" width="640" height="360" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen allow="autoplay"></iframe>
<a href="#" class="video__thumbnail reveal reveal_animation">
<span class="video__play">
{% include 'project/assets/play.svg' %}

@ -1,6 +1,15 @@
{% load i18n cms_tags thumbnail util_tags %}
<div id="timetable" data-active="0" data-last="{{ instance.child_plugin_instances|length }}">
{% if instance.video_vimeo_id and instance.video_cta %}
<div class="timetable__button reveal reveal_animation data_delay_8" data-video-vimeo-id="{{ instance.video_vimeo_id }}">
<a href="#video" class="button button--white button--small button--icon">
<span class="button__icon">{% include 'project/assets/play.svg' %}</span>
<span class="button__text">{{ instance.video_cta }}</span>
</a>
</div>
{% endif %}
<div class="timetable__clock__frame">
<div class="timetable__clock__main">
<div class="timetable__start reveal reveal_animation data_delay_8"></div>

@ -4,7 +4,10 @@ from cms.models import Page
from django.core.urlresolvers import reverse_lazy
from django.views.generic import RedirectView
from django.views.generic.edit import FormView
from aldryn_forms.admin.views import FormExportWizardView
from aldryn_forms.admin.exporter import Exporter
from tablib import Dataset
from django.http import HttpResponse
from project.forms import NewsletterSubscriptionForm
@ -45,3 +48,57 @@ class MediaRedirectView(RedirectView):
def get_redirect_url(self, *args, **kwargs):
return self.request.GET.get('url', None)
class CustomExporter(Exporter):
custom_fields = ['sent_at']
def get_dataset(self, fields):
headers = [field.rpartition('-')[0] for field in fields]
for custom_field in self.custom_fields:
headers.append(custom_field)
dataset = Dataset(headers=headers)
for submission in self.queryset.only('data').iterator():
row_data = []
form_fields = [field for field in submission.get_form_data()
if field.field_id in fields]
for header in fields:
for field in form_fields:
if field.field_id == header:
row_data.append(field.value)
break
else:
row_data.append('')
if row_data:
for custom_field in self.custom_fields:
row_data.append(getattr(submission, custom_field).strftime('%d.%m.%Y %H:%M:%S'))
dataset.append(row_data)
return dataset
class CustomFormExportWizardView(FormExportWizardView):
def done(self, form_list, **kwargs):
"""
this step only runs if all forms are valid.
"""
form_iter = iter(form_list)
step_1_form = next(form_iter)
step_2_form = next(form_iter)
fields = step_2_form.get_fields()
queryset = step_1_form.get_queryset()
dataset = CustomExporter(queryset=queryset).get_dataset(fields=fields)
filename = step_1_form.get_filename(extension=self.file_type)
content_type = self.get_content_type()
response = HttpResponse(dataset.xls, content_type=content_type)
response['Content-Disposition'] = 'attachment; filename=%s' % filename
return response

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,3 +1,5 @@
{% for name, value in form_data %}
<p>{{ name }}: {{ value|default_if_none:"—" }}</p>
{% if name %}
<p>{{ name }}: {{ value|default_if_none:"—" }}</p>
{% endif %}
{% endfor %}

@ -7,10 +7,10 @@ host="$1"
shift
cmd="$@"
until PGPASSWORD=$POSTGRES_PASSWORD psql -h "$host" -U "postgres" -c '\q'; do
until PGPASSWORD=$POSTGRES_PASSWORD psql -h "$host" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c '\q'; do
>&2 echo "Postgres is unavailable - sleeping"
sleep 1
done
>&2 echo "Postgres is up - executing command"
exec $cmd
exec $cmd

Loading…
Cancel
Save