memberzone
parent
25604d1038
commit
a74f6d0ee4
@ -0,0 +1,89 @@
|
||||
$(function() {
|
||||
'use strict';
|
||||
|
||||
var $body = $('body');
|
||||
|
||||
$body.on('click', '.control__item__open', function(event) {
|
||||
event.preventDefault();
|
||||
var $button = $(this);
|
||||
var $control_item = $button.parents('.control__item');
|
||||
$control_item.addClass('loading');
|
||||
|
||||
$.get($button.attr('data-href'), function(data) {
|
||||
var $control_item_content = $control_item.find('.control__item__content');
|
||||
var $control_item_content_main = $control_item_content.find('.control__item__content__main');
|
||||
|
||||
var cleaned_data = data.replace(/autofocus/g, '');
|
||||
$control_item_content_main.html(cleaned_data);
|
||||
|
||||
$control_item_content.height($control_item_content_main.outerHeight(true));
|
||||
|
||||
$control_item.addClass('active');
|
||||
|
||||
window.on_transitionend($control_item_content, function(event) {
|
||||
if (!event || event.target === $control_item_content[0]) {
|
||||
$control_item_content.off(window.transitionend);
|
||||
$control_item_content.removeAttr('style');
|
||||
$control_item.removeClass('loading');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$body.on('click', '.control__item__close', function(event) {
|
||||
event.preventDefault();
|
||||
var $button = $(this);
|
||||
var $control_item = $button.parents('.control__item');
|
||||
|
||||
var $control_item_content = $control_item.find('.control__item__content');
|
||||
var $control_item_content_main = $control_item_content.find('.control__item__content__main');
|
||||
$control_item_content.height($control_item_content_main.outerHeight(true));
|
||||
|
||||
window.on_transitionend($control_item_content, function(event) {
|
||||
if (!event || event.target === $control_item_content[0]) {
|
||||
$control_item_content.off(window.transitionend);
|
||||
$control_item_content_main.html('');
|
||||
}
|
||||
});
|
||||
|
||||
window.requestAnimationFrame(function() {
|
||||
$control_item.removeClass('active');
|
||||
$control_item_content.removeAttr('style');
|
||||
});
|
||||
});
|
||||
|
||||
$body.on('submit', '.control__item form', function(event) {
|
||||
event.preventDefault();
|
||||
var $form = $(this);
|
||||
$.ajax({
|
||||
type: $form.attr('method'),
|
||||
url: $form.attr('action'),
|
||||
data: $form.serialize(),
|
||||
success: function(data) {
|
||||
if ($(data).hasClass('control__item__success')) {
|
||||
var $control_item = $form.parents('.control__item');
|
||||
$control_item.find('.control__item__close').trigger('click');
|
||||
} else {
|
||||
$form.replaceWith(data);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var $task_form = $('.task__form');
|
||||
|
||||
if ($task_form.hasClass('errors')) {
|
||||
window.location = '#form';
|
||||
}
|
||||
|
||||
$task_form.formset();
|
||||
$task_form.on('formAdded', function(event) {
|
||||
var $title = $(event.target).find('h3');
|
||||
var id = parseInt($title.attr('data-id').match(/\d+/)[0]);
|
||||
$title.html($title.html().replace('%(id)', id + 1));
|
||||
});
|
||||
|
||||
$task_form.on('formDeleted', function(event) {
|
||||
$(event.target).hide();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,47 @@
|
||||
$(function() {
|
||||
'use strict';
|
||||
|
||||
var $body = $('body');
|
||||
|
||||
var download_texts = [];
|
||||
|
||||
$('.downloads__item__text').each(function() {
|
||||
var text = $(this).text().toLowerCase();
|
||||
text = text + '' + $(this).next().text().toLowerCase();
|
||||
download_texts.push({
|
||||
$element: $(this).parents('.downloads__item__frame'),
|
||||
text: text
|
||||
});
|
||||
});
|
||||
|
||||
$body.on('input', '#downloads_search', function(event) {
|
||||
var query = $(this).val().toLowerCase();
|
||||
var query_list = $.trim(query).split(' ');
|
||||
var matches = [];
|
||||
|
||||
for (var i = 0; i < download_texts.length; i++) {
|
||||
var download_text_item = download_texts[i];
|
||||
var matched = false;
|
||||
if (!matched) {
|
||||
for (var ii = 0; ii < query_list.length; ii++) {
|
||||
var query_item = query_list[ii];
|
||||
if (download_text_item.text.indexOf(query_item) >= 0) {
|
||||
matched = true;
|
||||
matches.push(download_text_item.$element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$('.downloads__item__frame').each(function() {
|
||||
$(this).parents('.downloads__section').css('display', 'none');
|
||||
$(this).css('display', 'none');
|
||||
});
|
||||
for (i = 0; i < matches.length; i++) {
|
||||
matches[i].removeAttr('style');
|
||||
matches[i].parents('.downloads__section').removeAttr('style');
|
||||
}
|
||||
});
|
||||
|
||||
$('#downloads_search').trigger('input');
|
||||
});
|
||||
@ -0,0 +1,195 @@
|
||||
.control_panel {
|
||||
width: 100%;
|
||||
padding: em(100px) 0;
|
||||
will-change: transform, height;
|
||||
&.reveal_animation {
|
||||
transition: background $reveal_duration $reveal_timing_function;
|
||||
&.reveal {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
.load__replace {
|
||||
margin-top: em(40px);
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.control__list {
|
||||
li {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.control_panel__content {
|
||||
max-width: em(1440px);
|
||||
margin: 0 auto;
|
||||
padding: 0 em(80px);
|
||||
font-size: 0;
|
||||
@media screen and (max-width: $large_breakpoint) {
|
||||
padding: 0 em(60px);
|
||||
}
|
||||
@media screen and (max-width: $medium_breakpoint) {
|
||||
max-width: em(600px);
|
||||
padding: 0 em(30px);
|
||||
}
|
||||
}
|
||||
|
||||
.control__item {
|
||||
display: block;
|
||||
background: $white_gray;
|
||||
text-decoration: none;
|
||||
width: 100%;
|
||||
margin-bottom: em(15px);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.control__item__open {
|
||||
opacity: 1;
|
||||
z-index: 2;
|
||||
transition: opacity 0.2s $easeOutQuad;
|
||||
.active & {
|
||||
opacity: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.control__item__close {
|
||||
opacity: 0;
|
||||
z-index: 1;
|
||||
transition: opacity 0.2s $easeOutQuad;
|
||||
.active & {
|
||||
opacity: 1;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.control__item__success {
|
||||
max-width: em(350px);
|
||||
.tag {
|
||||
display: block;
|
||||
}
|
||||
.button {
|
||||
margin-top: em(20px);
|
||||
}
|
||||
}
|
||||
|
||||
.control__item__title {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: em(22px) em(30px);
|
||||
position: relative;
|
||||
font-size: em(18px);
|
||||
color: $dark_gray;
|
||||
.control__item--button & {
|
||||
padding-right: em(240px);
|
||||
.button {
|
||||
position: absolute;
|
||||
top: em(16px);
|
||||
right: em(30px);
|
||||
width: em(210px);
|
||||
}
|
||||
@media screen and (max-width: $small_breakpoint) {
|
||||
padding-right: em(30px);
|
||||
padding-bottom: em(75px);
|
||||
.button {
|
||||
top: auto;
|
||||
right: auto;
|
||||
left: em(30px);
|
||||
bottom: em(22px);
|
||||
}
|
||||
}
|
||||
}
|
||||
.control__item--status & {
|
||||
padding-left: em(85px);
|
||||
}
|
||||
.control__item--arrow & {
|
||||
padding-right: em(85px);
|
||||
&:hover {
|
||||
.control__item__arrow {
|
||||
transform: translateX(em(5px));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.control__item__content {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
transition: height 0.5s $easeOutQuad;
|
||||
will-change: height;
|
||||
.active & {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.control__item__content__main {
|
||||
border-top: 1px solid $gray;
|
||||
padding: em(30px);
|
||||
}
|
||||
|
||||
.control__item__fields {
|
||||
margin: em(10px) 0 em(50px);
|
||||
}
|
||||
|
||||
.control__item__status {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: em(20px);
|
||||
margin-top: em(-20px);
|
||||
border-radius: 50%;
|
||||
border: 2px solid $gray;
|
||||
svg {
|
||||
display: block;
|
||||
width: em(36px);
|
||||
height: em(36px);
|
||||
fill: $white;
|
||||
}
|
||||
.control__item--status--active & {
|
||||
background: $green;
|
||||
border-color: $green;
|
||||
}
|
||||
.control__item--status--expired & {
|
||||
background: $red;
|
||||
border-color: $red;
|
||||
}
|
||||
}
|
||||
|
||||
.control__item__arrow {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: em(20px);
|
||||
margin-top: em(-20px);
|
||||
transform: none;
|
||||
transition: transform 0.3s $easeOutQuart;
|
||||
svg {
|
||||
display: block;
|
||||
width: em(40px);
|
||||
height: em(40px);
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
.fill {
|
||||
fill: $green;
|
||||
}
|
||||
}
|
||||
|
||||
.todo {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 48%;
|
||||
margin-right: 4%;
|
||||
@media screen and (max-width: $large_breakpoint) {
|
||||
width: 100%;
|
||||
margin: 0 0 em(40px) 0;
|
||||
}
|
||||
}
|
||||
|
||||
.settings {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 48%;
|
||||
@media screen and (max-width: $large_breakpoint) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
.downloads__frame {
|
||||
width: 100%;
|
||||
padding: em(80px) 0 em(150px);
|
||||
position: relative;
|
||||
will-change: transform;
|
||||
.downloads {
|
||||
li {
|
||||
width: 33.3333%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.downloads__content {
|
||||
max-width: em(1440px);
|
||||
margin: 0 auto;
|
||||
padding: 0 em(80px);
|
||||
@media screen and (max-width: $large_breakpoint) {
|
||||
padding: 0 em(60px);
|
||||
}
|
||||
@media screen and (max-width: $medium_breakpoint) {
|
||||
max-width: em(600px);
|
||||
padding: 0 em(30px);
|
||||
}
|
||||
}
|
||||
|
||||
.downloads__section {
|
||||
margin-top: em(80px);
|
||||
.downloads--flat & {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.downloads__section__title {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
h3 {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
background: $white;
|
||||
width: 100vw;
|
||||
height: em(3px);
|
||||
opacity: 0.15;
|
||||
top: em(10.5px);
|
||||
left: 100%;
|
||||
margin-left: em(20px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.downloads__section__title--flat {
|
||||
h3 {
|
||||
&:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.downloads__list {
|
||||
display: block;
|
||||
margin: 0 em(-10px);
|
||||
font-size: 0;
|
||||
}
|
||||
@ -0,0 +1,120 @@
|
||||
import xlwt
|
||||
import json
|
||||
|
||||
from aldryn_forms.utils import get_user_model
|
||||
from django.conf.urls import url
|
||||
from django.contrib.admin.utils import unquote
|
||||
from django.http import HttpResponse
|
||||
from django.utils.text import slugify
|
||||
from django.contrib import admin
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from memberzone.models import MemberTask, MemberTaskFormField, MemberTaskRegistration, MemberDownloadFile, \
|
||||
MemberDownloadTag, Profile, MemberDownloadSection
|
||||
|
||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class ProfileInline(admin.StackedInline):
|
||||
model = Profile
|
||||
can_delete = False
|
||||
|
||||
|
||||
class UserAdmin(BaseUserAdmin):
|
||||
inlines = [ProfileInline]
|
||||
fieldsets = (
|
||||
(None, {'fields': ('username', 'password')}),
|
||||
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups')}),
|
||||
)
|
||||
|
||||
|
||||
admin.site.unregister(User)
|
||||
admin.site.register(User, UserAdmin)
|
||||
|
||||
|
||||
class MemberTaskFormFieldInlineAdmin(admin.TabularInline):
|
||||
model = MemberTaskFormField
|
||||
extra = 0
|
||||
prepopulated_fields = {
|
||||
'name': ('label',)
|
||||
}
|
||||
|
||||
|
||||
@admin.register(MemberTask)
|
||||
class MemberTaskAdmin(admin.ModelAdmin):
|
||||
list_display = ('title', 'publish_date', 'deadline')
|
||||
list_filter = ('groups',)
|
||||
filter_horizontal = ('groups',)
|
||||
prepopulated_fields = {
|
||||
'slug': ('title',)
|
||||
}
|
||||
inlines = [MemberTaskFormFieldInlineAdmin, ]
|
||||
|
||||
def get_urls(self):
|
||||
info = self.model._meta.app_label, self.model._meta.model_name
|
||||
return [
|
||||
url(
|
||||
r'^(.+)/export/$',
|
||||
self.admin_site.admin_view(self.participants_export_view),
|
||||
name='%s_%s_participants_export' % info,
|
||||
),
|
||||
] + super(MemberTaskAdmin, self).get_urls()
|
||||
|
||||
def participants_export_view(self, request, object_id, *args, **kwargs):
|
||||
obj = self.get_object(request, unquote(object_id))
|
||||
wb = xlwt.Workbook()
|
||||
ws = wb.add_sheet('Teilnehmerliste')
|
||||
r = 0
|
||||
c = 0
|
||||
fields = []
|
||||
for field in obj.fields.all():
|
||||
ws.write(r, c, field.label)
|
||||
fields.append(field.name.replace('-', '_'))
|
||||
c += 1
|
||||
for participant in obj.registrations.all():
|
||||
for data_set in json.loads(participant.form_data):
|
||||
r += 1
|
||||
c = 0
|
||||
for field in fields:
|
||||
value = data_set.get(field, '')
|
||||
if isinstance(value, list):
|
||||
value = ', '.join(value)
|
||||
ws.write(r, c, value)
|
||||
c += 1
|
||||
|
||||
response = HttpResponse(content_type="application/ms-excel")
|
||||
response['Content-Disposition'] = 'attachment; filename=%s-teilnehmer.xls' % slugify(obj.title)
|
||||
wb.save(response)
|
||||
return response
|
||||
|
||||
|
||||
@admin.register(MemberTaskRegistration)
|
||||
class MemberTaskRegistrationAdmin(admin.ModelAdmin):
|
||||
readonly_fields = ['task', 'user', 'form_data', 'cdate']
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
def has_add_permission(self, request):
|
||||
return False
|
||||
|
||||
|
||||
@admin.register(MemberDownloadSection)
|
||||
class MemberDownloadSectionAdmin(admin.ModelAdmin):
|
||||
list_display = ('title', 'ordering')
|
||||
list_editable = ['ordering']
|
||||
|
||||
|
||||
@admin.register(MemberDownloadTag)
|
||||
class MemberDownloadTagAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
@admin.register(MemberDownloadFile)
|
||||
class MemberDownloadFileAdmin(admin.ModelAdmin):
|
||||
list_display = ('label', 'ordering')
|
||||
list_editable = ['ordering']
|
||||
list_filter = ('section', 'groups')
|
||||
filter_horizontal = ('groups', 'tags')
|
||||
@ -0,0 +1,8 @@
|
||||
from django.contrib.auth.forms import AuthenticationForm
|
||||
|
||||
|
||||
class LoginForm(AuthenticationForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(LoginForm, self).__init__(*args, **kwargs)
|
||||
for _, value in self.fields.items():
|
||||
value.widget.attrs['placeholder'] = value.label
|
||||
@ -0,0 +1,158 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-03-21 12:32
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import djangocms_text_ckeditor.fields
|
||||
import filer.fields.folder
|
||||
import filer.fields.image
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('auth', '0008_alter_user_username_max_length'),
|
||||
migrations.swappable_dependency(settings.FILER_IMAGE_MODEL),
|
||||
('filer', '0007_auto_20161016_1055'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='MemberDownloadFile',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(blank=True, max_length=512, null=True, verbose_name='Name')),
|
||||
('file', models.FileField(max_length=512, upload_to='protected_files')),
|
||||
('thumbnail', models.FileField(blank=True, null=True, upload_to='protected_thumbnails')),
|
||||
('ordering', models.IntegerField(default=50, verbose_name='Sortierung')),
|
||||
('groups', models.ManyToManyField(related_name='files', to='auth.Group', verbose_name='Mitgliedergruppen')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Mitglieder Downloads',
|
||||
'verbose_name': 'Mitglieder Download',
|
||||
'ordering': ['ordering', 'name'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MemberDownloadSection',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=100, verbose_name='Title')),
|
||||
('ordering', models.IntegerField(default=50, verbose_name='Sortierung')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Mitglieder Download Sections',
|
||||
'verbose_name': 'Mitglieder Download Section',
|
||||
'ordering': ['ordering'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MemberDownloadTag',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=100, verbose_name='Name')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Mitglieder Download Tags',
|
||||
'verbose_name': 'Mitglieder Download Tag',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MemberTask',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=100, verbose_name='Title')),
|
||||
('slug', models.SlugField(unique=True, verbose_name='slug')),
|
||||
('sub_title', models.CharField(blank=True, max_length=100, verbose_name='Untertitle')),
|
||||
('bodytext', djangocms_text_ckeditor.fields.HTMLField(verbose_name='Inhalt')),
|
||||
('publish_date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Veröffentlichungsdatum')),
|
||||
('deadline', models.DateTimeField(blank=True, null=True, verbose_name='Anmeldeschluss')),
|
||||
('max_num', models.IntegerField(default=1, verbose_name='max. Formulare')),
|
||||
('submit_text', models.CharField(default='Als gelesen markieren', max_length=100, verbose_name='Formular Button Text')),
|
||||
('form_bodytext', djangocms_text_ckeditor.fields.HTMLField(blank=True, default='<h2 class="reveal reveal_animation">Jetzt anmelden</h2>', verbose_name='Formular Beschreibung')),
|
||||
('success_title', models.CharField(default='Ihre Anmeldung wurde erfasst', max_length=200, verbose_name='Bestätigungstitel')),
|
||||
('success_bodytext', djangocms_text_ckeditor.fields.HTMLField(verbose_name='Bestätigungstext')),
|
||||
('folder', filer.fields.folder.FilerFolderField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='filer.Folder', verbose_name='Bilder')),
|
||||
('groups', models.ManyToManyField(related_name='tasks', to='auth.Group', verbose_name='Mitglieder Gruppe')),
|
||||
('image', filer.fields.image.FilerImageField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.FILER_IMAGE_MODEL, verbose_name='Bild')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Mitglieder Aufgaben',
|
||||
'verbose_name': 'Mitglieder Aufgabe',
|
||||
'ordering': ['-publish_date'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MemberTaskFormField',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('type', models.CharField(choices=[('text', 'Text Feld (einzeilig)'), ('textarea', 'Text Feld (mehrzeilig)'), ('email', 'E-Mail Feld'), ('radio', 'Auswahlfeld (eine auswahl)'), ('checkbox', 'Auswahlfeld (mehrere auswahlen)')], max_length=30)),
|
||||
('label', models.CharField(max_length=100, verbose_name='Label')),
|
||||
('name', models.SlugField(verbose_name='Name')),
|
||||
('choices', models.TextField(blank=True, help_text='Werte die bei einem Auswahlfeld zur verfügung stehen sollen. Ein wert pro Zeile', verbose_name='Auswahlwerte')),
|
||||
('required', models.BooleanField(default=False, verbose_name='Pflichtfeld')),
|
||||
('ordering', models.IntegerField(default=50, verbose_name='Sortierung')),
|
||||
('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='fields', to='memberzone.MemberTask', verbose_name='Mitglieder Aufgabe')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Formularfelder',
|
||||
'verbose_name': 'Formularfeld',
|
||||
'ordering': ['ordering'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MemberTaskRegistration',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('form_data', models.TextField(verbose_name='Form Content')),
|
||||
('cdate', models.DateTimeField(auto_now_add=True)),
|
||||
('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='registrations', to='memberzone.MemberTask', verbose_name='Mitglieder Aufgabe')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='registrations', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Mitglieder Aufgaben Registierungen',
|
||||
'verbose_name': 'Mitglieder Aufgabe Registierung',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Profile',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('first_name', models.CharField(blank=True, max_length=255, null=True, verbose_name='Vorname')),
|
||||
('last_name', models.CharField(blank=True, max_length=255, null=True, verbose_name='Nachname')),
|
||||
('street', models.CharField(blank=True, max_length=255, null=True, verbose_name='Strasse')),
|
||||
('zip', models.IntegerField(blank=True, null=True, verbose_name='PLZ')),
|
||||
('place', models.CharField(blank=True, max_length=255, null=True, verbose_name='Ort')),
|
||||
('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='E-Mail')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'User Profile',
|
||||
'verbose_name': 'User Profil',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='memberdownloadfile',
|
||||
name='section',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='files', to='memberzone.MemberDownloadSection', verbose_name='Download Section'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='memberdownloadfile',
|
||||
name='tags',
|
||||
field=models.ManyToManyField(blank=True, null=True, related_name='files', to='memberzone.MemberDownloadTag', verbose_name='Suchbegriffe'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='membertaskregistration',
|
||||
unique_together=set([('user', 'task')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='membertaskformfield',
|
||||
unique_together=set([('task', 'name')]),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-03-21 12:56
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('memberzone', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='memberdownloadfile',
|
||||
name='thumbnail',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='memberdownloadfile',
|
||||
name='description',
|
||||
field=models.TextField(blank=True, null=True, verbose_name='Beschreibung'),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,219 @@
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group
|
||||
from django.core.files.storage import default_storage
|
||||
from djangocms_text_ckeditor.fields import HTMLField
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from filer.fields.folder import FilerFolderField
|
||||
from filer.fields.image import FilerImageField
|
||||
from filer.models import Image as ImageModel
|
||||
|
||||
from memberzone.storage import PrivateS3MediaStorage
|
||||
|
||||
|
||||
class Profile(models.Model):
|
||||
user = models.OneToOneField(get_user_model(), related_name='profile', on_delete=models.CASCADE)
|
||||
first_name = models.CharField(verbose_name=_('Vorname'), max_length=255)
|
||||
last_name = models.CharField(verbose_name=_('Nachname'), max_length=255)
|
||||
street = models.CharField(verbose_name=_('Strasse'), max_length=255, null=True, blank=True)
|
||||
zip = models.IntegerField(verbose_name=_('PLZ'), null=True, blank=True)
|
||||
place = models.CharField(verbose_name=_('Ort'), max_length=255, null=True, blank=True)
|
||||
email = models.EmailField(verbose_name=_('E-Mail'), null=True, blank=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('User Profil')
|
||||
verbose_name_plural = _('User Profile')
|
||||
|
||||
def __str__(self):
|
||||
return '{} {}'.format(self.first_name, self.last_name)
|
||||
|
||||
|
||||
class MemberTask(models.Model):
|
||||
groups = models.ManyToManyField(Group, verbose_name=_('Mitglieder Gruppe'), related_name='tasks')
|
||||
title = models.CharField(max_length=100, verbose_name=_('Title'))
|
||||
slug = models.SlugField(verbose_name=_('slug'), unique=True)
|
||||
sub_title = models.CharField(max_length=100, verbose_name=_('Untertitle'), blank=True)
|
||||
bodytext = HTMLField(verbose_name=_('Inhalt'), configuration='simple_ckeditor')
|
||||
image = FilerImageField(verbose_name=_('Bild'), blank=True, null=True)
|
||||
folder = FilerFolderField(verbose_name=_('Bilder'), blank=True, null=True)
|
||||
publish_date = models.DateTimeField(verbose_name=_('Veröffentlichungsdatum'), default=timezone.now)
|
||||
deadline = models.DateTimeField(verbose_name=_('Anmeldeschluss'), blank=True, null=True)
|
||||
|
||||
max_num = models.IntegerField(verbose_name=_('max. Formulare'), default=1)
|
||||
submit_text = models.CharField(max_length=100, verbose_name=_('Formular Button Text'),
|
||||
default=_('Als gelesen markieren'))
|
||||
form_bodytext = HTMLField(verbose_name=_('Formular Beschreibung'), configuration='simple_ckeditor', blank=True,
|
||||
default='<h2 class="reveal reveal_animation">Jetzt anmelden</h2>')
|
||||
|
||||
success_title = models.CharField(verbose_name=_('Bestätigungstitel'), max_length=200,
|
||||
default=_('Ihre Anmeldung wurde erfasst'))
|
||||
success_bodytext = HTMLField(verbose_name=_('Bestätigungstext'), configuration='simple_ckeditor')
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Mitglieder Aufgabe')
|
||||
verbose_name_plural = _('Mitglieder Aufgaben')
|
||||
ordering = ['-publish_date']
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
@property
|
||||
def images(self):
|
||||
return [x for x in self.folder.files if isinstance(x, ImageModel)]
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse_lazy('memberzone:task', args=[self.slug])
|
||||
|
||||
@property
|
||||
def is_expired(self):
|
||||
return self.deadline and self.deadline <= timezone.now()
|
||||
|
||||
@property
|
||||
def form_class(self):
|
||||
fields = self.fields.all()
|
||||
|
||||
class MyDynamicForm(forms.Form):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MyDynamicForm, self).__init__(*args, **kwargs)
|
||||
for f in fields:
|
||||
self.fields[f.name.replace('-', '_')] = f.form_field
|
||||
|
||||
return MyDynamicForm
|
||||
|
||||
|
||||
class MemberTaskFormField(models.Model):
|
||||
FIELD_TYPES = [
|
||||
('text', _('Text Feld (einzeilig)')),
|
||||
('textarea', _('Text Feld (mehrzeilig)')),
|
||||
('email', _('E-Mail Feld')),
|
||||
('radio', _('Auswahlfeld (eine auswahl)')),
|
||||
('checkbox', _('Auswahlfeld (mehrere auswahlen)')),
|
||||
]
|
||||
task = models.ForeignKey(MemberTask, related_name='fields', verbose_name=_('Mitglieder Aufgabe'))
|
||||
type = models.CharField(max_length=30, choices=FIELD_TYPES)
|
||||
label = models.CharField(max_length=100, verbose_name=_('Label'))
|
||||
name = models.SlugField(verbose_name=_('Name'))
|
||||
choices = models.TextField(verbose_name=_('Auswahlwerte'),
|
||||
help_text=_(
|
||||
'Werte die bei einem Auswahlfeld zur verfügung stehen sollen. Ein wert pro Zeile'),
|
||||
blank=True)
|
||||
required = models.BooleanField(default=False, verbose_name=_('Pflichtfeld'))
|
||||
|
||||
ordering = models.IntegerField(default=50, verbose_name=_('Sortierung'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Formularfeld')
|
||||
verbose_name_plural = _('Formularfelder')
|
||||
ordering = ['ordering']
|
||||
unique_together = ['task', 'name']
|
||||
|
||||
def __str__(self):
|
||||
return self.label
|
||||
|
||||
@property
|
||||
def form_choices(self):
|
||||
return [(x, x) for x in self.choices.splitlines()]
|
||||
|
||||
@property
|
||||
def form_field(self):
|
||||
if self.type == 'email':
|
||||
return forms.EmailField(label=self.label, required=self.required)
|
||||
elif self.type == 'textarea':
|
||||
return forms.CharField(label=self.label, required=self.required, widget=forms.Textarea())
|
||||
elif self.type == 'radio':
|
||||
return forms.ChoiceField(label=self.label, required=self.required, choices=self.form_choices,
|
||||
widget=forms.RadioSelect)
|
||||
elif self.type == 'checkbox':
|
||||
return forms.MultipleChoiceField(label=self.label, required=self.required, choices=self.form_choices,
|
||||
widget=forms.CheckboxSelectMultiple)
|
||||
else:
|
||||
return forms.CharField(label=self.label, required=self.required)
|
||||
|
||||
|
||||
class MemberTaskRegistration(models.Model):
|
||||
task = models.ForeignKey(MemberTask, related_name='registrations', verbose_name=_('Mitglieder Aufgabe'))
|
||||
user = models.ForeignKey(get_user_model(), related_name='registrations')
|
||||
form_data = models.TextField(verbose_name=_('Form Content'))
|
||||
cdate = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Mitglieder Aufgabe Registierung')
|
||||
verbose_name_plural = _('Mitglieder Aufgaben Registierungen')
|
||||
unique_together = ['user', 'task']
|
||||
|
||||
def __str__(self):
|
||||
return '{} {}'.format(self.user, self.task)
|
||||
|
||||
|
||||
class MemberDownloadSection(models.Model):
|
||||
title = models.CharField(max_length=100, verbose_name=_('Title'))
|
||||
ordering = models.IntegerField(default=50, verbose_name=_('Sortierung'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Mitglieder Download Section')
|
||||
verbose_name_plural = _('Mitglieder Download Sections')
|
||||
ordering = ['ordering']
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
class MemberDownloadTag(models.Model):
|
||||
name = models.CharField(max_length=100, verbose_name=_('Name'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Mitglieder Download Tag')
|
||||
verbose_name_plural = _('Mitglieder Download Tags')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
if getattr(settings, 'DEFAULT_STORAGE_DSN', None):
|
||||
protected_file_storage = PrivateS3MediaStorage()
|
||||
else:
|
||||
protected_file_storage = default_storage
|
||||
|
||||
|
||||
class MemberDownloadFile(models.Model):
|
||||
section = models.ForeignKey(MemberDownloadSection, verbose_name=_('Download Section'), related_name='files')
|
||||
groups = models.ManyToManyField(Group, verbose_name=_('Mitgliedergruppen'), related_name='files')
|
||||
tags = models.ManyToManyField(MemberDownloadTag, verbose_name=_('Suchbegriffe'), related_name='files', blank=True,
|
||||
null=True)
|
||||
|
||||
name = models.CharField(max_length=512, verbose_name=_('Name'), blank=True, null=True)
|
||||
description = models.TextField(verbose_name=_('Beschreibung'), blank=True, null=True)
|
||||
file = models.FileField(upload_to='protected_files', max_length=512, storage=protected_file_storage)
|
||||
|
||||
ordering = models.IntegerField(default=50, verbose_name=_('Sortierung'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Mitglieder Download')
|
||||
verbose_name_plural = _('Mitglieder Downloads')
|
||||
ordering = ['ordering', 'name']
|
||||
|
||||
def __str__(self):
|
||||
return self.label
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
return self.file.url
|
||||
|
||||
@property
|
||||
def extension(self):
|
||||
return self.file.name.split('.')[-1]
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
if self.name:
|
||||
return self.name
|
||||
else:
|
||||
return self.file.name
|
||||
|
||||
@property
|
||||
def tag_list(self):
|
||||
return ', '.join(list(self.tags.values_list('name', flat=True)))
|
||||
@ -0,0 +1,56 @@
|
||||
import re
|
||||
from aldryn_django.storage import S3MediaStorage
|
||||
from django.conf import settings
|
||||
|
||||
from boto.s3.connection import (
|
||||
SubdomainCallingFormat,
|
||||
OrdinaryCallingFormat,
|
||||
)
|
||||
|
||||
|
||||
class PrivateS3MediaStorage(S3MediaStorage):
|
||||
def __init__(self):
|
||||
bucket_name = settings.AWS_MEDIA_STORAGE_BUCKET_NAME
|
||||
|
||||
if '.' in bucket_name:
|
||||
calling_format = OrdinaryCallingFormat()
|
||||
else:
|
||||
calling_format = SubdomainCallingFormat()
|
||||
|
||||
# We cannot use a function call or a partial here. Instead, we have to
|
||||
# create a subclass because django tries to recreate a new object by
|
||||
# calling the __init__ of the returned object (with no arguments).
|
||||
super(S3MediaStorage, self).__init__(
|
||||
access_key=settings.AWS_MEDIA_ACCESS_KEY_ID,
|
||||
secret_key=settings.AWS_MEDIA_SECRET_ACCESS_KEY,
|
||||
bucket_name=bucket_name,
|
||||
location=settings.AWS_MEDIA_BUCKET_PREFIX,
|
||||
host=settings.AWS_MEDIA_STORAGE_HOST,
|
||||
custom_domain=settings.AWS_MEDIA_DOMAIN,
|
||||
calling_format=calling_format,
|
||||
# Setting an ACL requires us to grant the user the PutObjectAcl
|
||||
# permission as well, even if it matches the default bucket ACL.
|
||||
# XXX: Ideally we would thus set it to `None`, but due to how
|
||||
# easy_thumbnails works internally, that causes thumbnail
|
||||
# generation to fail...
|
||||
default_acl='private',
|
||||
querystring_auth=True,
|
||||
)
|
||||
# MEDIA_HEADERS is a list of tuples containing a regular expression
|
||||
# to match against a path, and a dictionary of HTTP headers to be
|
||||
# returned with the resource identified by the path when it is
|
||||
# requested.
|
||||
# The headers are applied in the order they where declared, and
|
||||
# processing stops at the first match.
|
||||
# E.g.:
|
||||
#
|
||||
# MEDIA_HEADERS = [
|
||||
# (r'media/cache/.*', {
|
||||
# 'Cache-Control': 'max-age={}'.format(3600 * 24 * 365),
|
||||
# })
|
||||
# ]
|
||||
#
|
||||
media_headers = getattr(settings, 'MEDIA_HEADERS', [])
|
||||
self.media_headers = [
|
||||
(re.compile(r), headers) for r, headers in media_headers
|
||||
]
|
||||
@ -0,0 +1,16 @@
|
||||
{% load i18n %}
|
||||
<form action="{% url 'memberzone:change_password' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<div class="control__item__fields">
|
||||
{% include 'project/content/includes/_field.html' with field=form.old_password label=True small=True %}
|
||||
{% include 'project/content/includes/_field.html' with field=form.new_password1 label=True small=True %}
|
||||
{% include 'project/content/includes/_field.html' with field=form.new_password2 label=True small=True %}
|
||||
</div>
|
||||
<button class="button button--small">
|
||||
<span class="button__wrapper">
|
||||
{% include 'project/assets/tick.svg' %}
|
||||
<b>{% trans 'ändern' %}</b>
|
||||
</span>
|
||||
{% include 'project/assets/tick.svg' %}
|
||||
</button>
|
||||
</form>
|
||||
@ -0,0 +1,11 @@
|
||||
{% load i18n %}
|
||||
<div class="control__item__success">
|
||||
<span class="tag tag--black">{% trans 'Ihr Passwort wurden erfolgreich geändert' %}</span>
|
||||
<a href="#" class="button button--small control__item__close">
|
||||
<span class="button__wrapper">
|
||||
{% include 'project/assets/close.svg' %}
|
||||
<b>{% trans 'Schliessen' %}</b>
|
||||
</span>
|
||||
{% include 'project/assets/close.svg' %}
|
||||
</a>
|
||||
</div>
|
||||
@ -0,0 +1,14 @@
|
||||
<html>
|
||||
<head>{{ subject }}</head>
|
||||
<body>
|
||||
<h1>{{ subject }}</h1>
|
||||
<hr>
|
||||
{% for data_set in data %}
|
||||
<ul>
|
||||
{% for label, value in data_set %}
|
||||
<li><strong>{{ label }}:</strong> {{ value }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,57 @@
|
||||
{% load i18n static util_tags thumbnail %}
|
||||
|
||||
|
||||
<div id="downloads" class="downloads__frame reveal_self reveal reveal_animation">
|
||||
<div class="downloads__content">
|
||||
{% if title != False %}
|
||||
<h2 class="{% if not light %}white{% endif %} reveal_self reveal reveal_animation">{% trans title %}</h2>
|
||||
{% endif %}
|
||||
{% if search %}
|
||||
<form class="downloads__filter reveal_self reveal reveal_animation" action="{{ request.path }}#downloads" method="get">
|
||||
<div class="input__frame input__frame--blue input__frame--icon">
|
||||
<input name="q" {% if request.GET.q %}value="{{ request.GET.q }}"{% endif %} id="downloads_search"
|
||||
type="text" placeholder="{% trans 'Suchbegriff eingeben' %}">
|
||||
<button>{% trans 'Suchen' %}{% include 'project/assets/search.svg' %}</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% for section in download_sections %}
|
||||
<div class="downloads__section reveal_container">
|
||||
<div class="downloads__section__title{% if flat %} downloads__section__title--flat{% endif %} reveal reveal_animation">
|
||||
<h3 class="tag{% if flat %} tag--black{% endif %}{% if not light %} white{% endif %}">{{ section.title }}</h3>
|
||||
</div>
|
||||
{% include 'project/plugins/content/download_section.html' with instance=section %}
|
||||
{# <div class="downloads__list reveal reveal_animation">#}
|
||||
{# {% for file in files %}#}
|
||||
{# <div class="downloads__item__frame">#}
|
||||
{# <a target="_blank" href="{{ file.file.url }}" class="downloads__item">#}
|
||||
{# <div class="downloads__item__main">#}
|
||||
{# <div class="downloads__item__content reveal reveal_animation">#}
|
||||
{# <span class="downloads__item__icon tag">#}
|
||||
{# {% include 'project/assets/download.svg' %}#}
|
||||
{# {{ file.file.name|split:'.'|last }}#}
|
||||
{# </span>#}
|
||||
{# {% if file.thumbnail %}#}
|
||||
{# <img class="downloads__item__thumbnail"#}
|
||||
{# src="{{ file.thumbnail.url }}">#}
|
||||
{# {% elif file.file.name|split:'.'|last == 'pdf' %}#}
|
||||
{# <img class="downloads__item__thumbnail"#}
|
||||
{# src="{% download_thumbnail file %}">#}
|
||||
{# {% else %}#}
|
||||
{# {% thumbnail file.file 118x167 crop=True as thumb %}#}
|
||||
{# {% static 'img/pdf_default.jpg' as static %}#}
|
||||
{# <img class="downloads__item__thumbnail"#}
|
||||
{# src="{{ thumb.url|default:static }}">#}
|
||||
{# {% endif %}#}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
{# <span class="p downloads__item__text">{{ file }}</span>#}
|
||||
{# <span class="downloads__item__tags">{{ file.tag_list }}</span>#}
|
||||
{# </a>#}
|
||||
{# </div>#}
|
||||
{# {% endfor %}#}
|
||||
{# </div>#}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,8 @@
|
||||
<div class="input__frame{% if blue %} input__frame--blue{% endif %}{% if label %} input__frame--label{% else %} input__frame--no-label{% endif %}{% if small %} input__frame--small{% endif %}"
|
||||
{% if hidden %}style="display: none"{% endif %}>
|
||||
<label for="id_{{ field.name }}" class="p"><span>{{ field.label }}:</span></label>
|
||||
{{ field }}
|
||||
{% if field.errors %}
|
||||
<span class="tag tag--light input__errors">{{ field.errors.0 }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
@ -0,0 +1,176 @@
|
||||
{% extends 'main.html' %}
|
||||
{% load i18n cms_tags thumbnail %}
|
||||
|
||||
{% load i18n static task_tags %}
|
||||
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="content__frame">
|
||||
{% include 'project/includes/content_intro.html' %}
|
||||
<div class="content__container">
|
||||
|
||||
|
||||
<div class="control_panel reveal_self reveal reveal_animation">
|
||||
<div class="control_panel__content">
|
||||
<div class="todo">
|
||||
<h2 class="reveal_self reveal reveal_animation">{% trans 'Aktuell' %}</h2>
|
||||
<div id="todo" class="load__frame">
|
||||
<div class="load__main">
|
||||
<ul class="control__list reveal_container">
|
||||
{% if open_tasks %}
|
||||
{% for object in open_tasks %}
|
||||
<li class="data_id_{{ forloop.counter0 }} reveal reveal_animation">
|
||||
<a href="{{ object.get_absolute_url }}"
|
||||
class="p control__item control__item--arrow control__item--status {{ object|task_status:request.user }}">
|
||||
<span class="control__item__title">
|
||||
<span class="control__item__status">
|
||||
{% if object.is_expired %}
|
||||
{% include 'project/assets/close.svg' %}
|
||||
{% else %}
|
||||
{% include 'project/assets/tick.svg' %}
|
||||
{% endif %}
|
||||
</span>
|
||||
{{ object.title }}
|
||||
<span class="control__item__arrow">
|
||||
{% include 'project/assets/arrow-bottom.svg' %}
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% for object in object_list %}
|
||||
<li class="data_id_{{ forloop.counter0 }} reveal reveal_animation">
|
||||
<a href="{{ object.get_absolute_url }}"
|
||||
class="p control__item control__item--arrow control__item--status {{ object|task_status:request.user }}">
|
||||
<span class="control__item__title">
|
||||
<span class="control__item__status">
|
||||
{% if object.is_expired %}
|
||||
{% include 'project/assets/close.svg' %}
|
||||
{% else %}
|
||||
{% include 'project/assets/tick.svg' %}
|
||||
{% endif %}
|
||||
</span>
|
||||
{{ object.title }}
|
||||
<span class="control__item__arrow">
|
||||
{% include 'project/assets/arrow-bottom.svg' %}
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% if page_obj.has_next or open_tasks and paginator.count > 0 %}
|
||||
<div class="load__replace data_id_3 reveal_self reveal reveal_animation">
|
||||
<a href="{% url 'memberzone:overview' %}?page=
|
||||
|
||||
|
||||
{% if open_tasks %}{{ page_obj.start_index }}{% else %}{{ page_obj.next_page_number }}{% endif %}"
|
||||
class="button button--load list__button button--small">
|
||||
<span class="button__wrapper">
|
||||
{% include 'project/assets/dots.svg' %}
|
||||
<b>{% trans 'Ältere laden' %}</b>
|
||||
</span>
|
||||
{% include 'project/assets/dots.svg' %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings">
|
||||
<h2 class="reveal_self reveal reveal_animation">{% trans 'Einstellungen' %}</h2>
|
||||
<ul class="control__list reveal_container">
|
||||
<li class="data_id_0 reveal reveal_animation">
|
||||
<div class="p control__item control__item--button">
|
||||
<span class="control__item__title">
|
||||
{% trans 'Benutzerdaten' %}
|
||||
<a href="#" data-href="{% url 'memberzone:profile_edit' %}"
|
||||
class="button button--small button--load control__item__open">
|
||||
<span class="button__wrapper">
|
||||
{% include 'project/assets/dots.svg' %}
|
||||
<b>{% trans 'Bearbeiten' %}</b>
|
||||
</span>
|
||||
{% include 'project/assets/dots.svg' %}
|
||||
</a>
|
||||
<a href="#" class="button button--small button--ghost control__item__close">
|
||||
<span class="button__wrapper">
|
||||
{% include 'project/assets/close.svg' %}
|
||||
<b>{% trans 'Abbrechen' %}</b>
|
||||
</span>
|
||||
{% include 'project/assets/close.svg' %}
|
||||
</a>
|
||||
</span>
|
||||
<div class="control__item__content">
|
||||
<div class="control__item__content__main">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="data_id_1 reveal reveal_animation">
|
||||
<div class="p control__item control__item--button">
|
||||
<span class="control__item__title">
|
||||
{% trans 'Passwort' %}
|
||||
<a href="#" data-href="{% url 'memberzone:change_password' %}"
|
||||
class="button button--small button--load control__item__open">
|
||||
<span class="button__wrapper">
|
||||
{% include 'project/assets/dots.svg' %}
|
||||
<b>{% trans 'Bearbeiten' %}</b>
|
||||
</span>
|
||||
{% include 'project/assets/dots.svg' %}
|
||||
</a>
|
||||
<a href="#" class="button button--small button--ghost control__item__close">
|
||||
<span class="button__wrapper">
|
||||
{% include 'project/assets/close.svg' %}
|
||||
<b>{% trans 'Abbrechen' %}</b>
|
||||
</span>
|
||||
{% include 'project/assets/close.svg' %}
|
||||
</a>
|
||||
</span>
|
||||
<div class="control__item__content">
|
||||
<div class="control__item__content__main">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{% if request.user.company_responsible %}
|
||||
<li class="data_id_2 reveal reveal_animation">
|
||||
<div class="p control__item control__item--button">
|
||||
<span class="control__item__title">
|
||||
{% trans 'Firmendaten' %}
|
||||
<a href="#" data-href="{% url 'memberzone:company_edit' %}"
|
||||
class="button button--small button--load control__item__open">
|
||||
<span class="button__wrapper">
|
||||
{% include 'project/assets/dots.svg' %}
|
||||
<b>{% trans 'Bearbeiten' %}</b>
|
||||
</span>
|
||||
{% include 'project/assets/dots.svg' %}
|
||||
</a>
|
||||
<a href="#"
|
||||
class="button button--small button--ghost control__item__close">
|
||||
<span class="button__wrapper">
|
||||
{% include 'project/assets/close.svg' %}
|
||||
<b>{% trans 'Abbrechen' %}</b>
|
||||
</span>
|
||||
{% include 'project/assets/close.svg' %}
|
||||
</a>
|
||||
</span>
|
||||
<div class="control__item__content">
|
||||
<div class="control__item__content__main">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'memberzone/includes/_downloads.html' with search=True title='Downloads' %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -0,0 +1,17 @@
|
||||
{% load i18n %}
|
||||
<form action="{% url 'memberzone:profile_edit' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<div class="control__item__fields">
|
||||
{% include 'memberzone/includes/_field.html' with field=form.gender label=True small=True %}
|
||||
{% include 'memberzone/includes/_field.html' with field=form.title label=True small=True %}
|
||||
{% include 'memberzone/includes/_field.html' with field=form.first_name label=True small=True %}
|
||||
{% include 'memberzone/includes/_field.html' with field=form.last_name label=True small=True %}
|
||||
</div>
|
||||
<button class="button button--small">
|
||||
<span class="button__wrapper">
|
||||
{% include 'project/assets/tick.svg' %}
|
||||
<b>{% trans 'Speichern' %}</b>
|
||||
</span>
|
||||
{% include 'project/assets/tick.svg' %}
|
||||
</button>
|
||||
</form>
|
||||
@ -0,0 +1,11 @@
|
||||
{% load i18n %}
|
||||
<div class="control__item__success">
|
||||
<span class="tag tag--black">{% trans 'Ihre Benutzerdaten wurden erfolgreich geändert' %}</span>
|
||||
<a href="#" class="button button--small control__item__close">
|
||||
<span class="button__wrapper">
|
||||
{% include 'project/assets/close.svg' %}
|
||||
<b>{% trans 'Schliessen' %}</b>
|
||||
</span>
|
||||
{% include 'project/assets/close.svg' %}
|
||||
</a>
|
||||
</div>
|
||||
@ -0,0 +1,139 @@
|
||||
{% extends 'project/content.html' %}
|
||||
{% load i18n static thumbnail task_tags %}
|
||||
https://source.unsplash.com/random/1200x900
|
||||
|
||||
{% block body_class %}header-close{% endblock %}
|
||||
{% block header_menu_url %}{% url 'memberzone:overview' %}{% endblock %}
|
||||
|
||||
{% block intro_class %}intro--image{% endblock %}
|
||||
{% block intro_image %}{% if object.image %}
|
||||
{% thumbnail object.image "1200x900" crop="center" subject_location=object.image.subject_location %}
|
||||
{% endif %}{% endblock %}
|
||||
|
||||
{% block content_content %}
|
||||
<div class="text text--full text--narrow reveal_container">
|
||||
<div class="text__content">
|
||||
<div class="text__content__frame">
|
||||
<div class="text__content__main">{{ object.bodytext | add_animation_classes | safe }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'project/content/includes/_grid.html' with grid=grid last=True %}
|
||||
|
||||
<div id="form"></div>
|
||||
<div class="text text--full text--narrow reveal_container">
|
||||
<div class="text__content">
|
||||
<div class="text__content__frame">
|
||||
<div class="text__content__main">
|
||||
{% if formset and not object.is_expired %}
|
||||
{{ object.form_bodytext | add_animation_classes | safe }}
|
||||
{% if formset.errors %}
|
||||
<span class="tag tag--light input__errors reveal reveal_animation">
|
||||
{% trans 'Bitte korrigieren Sie die markierten Felder.' %}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if formset.non_form_errors %}
|
||||
<span class="tag tag--light input__errors reveal reveal_animation">
|
||||
{{ formset.non_form_errors }}
|
||||
</span>
|
||||
{% endif %}
|
||||
<form action="{% url 'memberzone:task' object.slug %}" method="post"
|
||||
class="task__form text__content__form reveal reveal_animation {% if formset.errors %}errors{% endif %}"
|
||||
data-formset-prefix="{{ formset.prefix }}">
|
||||
{% csrf_token %}
|
||||
{{ formset.management_form }}
|
||||
|
||||
<div data-formset-body>
|
||||
{% for form in formset %}
|
||||
<fieldset data-formset-form>
|
||||
{% if object.max_num > 1 %}
|
||||
<div class="fieldset__title">
|
||||
<h3 class="tag" data-id="{{ forloop.counter }}">{{ forloop.counter }}.
|
||||
Person</h3>
|
||||
{% if forloop.counter0 > 0 %}
|
||||
<a class="tag" data-formset-delete-button>
|
||||
{% include 'project/assets/close.svg' %}
|
||||
{% trans 'Entfernen' %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if form.non_field_errors %}
|
||||
<span class="tag tag--light input__errors">
|
||||
{{ formset.non_field_errors }}
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
{% for field in form %}
|
||||
{% if field.name == 'DELETE' %}
|
||||
{% include 'project/content/includes/_field.html' with field=field label=True hidden=True %}
|
||||
{% else %}
|
||||
{% include 'project/content/includes/_field.html' with field=field label=True %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<script type="form-template" data-formset-empty-form>
|
||||
<fieldset data-formset-form>
|
||||
<div class="fieldset__title">
|
||||
<h3 class="tag" data-id="{{ formset.empty_form.prefix }}">%(id). Person</h3>
|
||||
<a class="tag" data-formset-delete-button>
|
||||
{% include 'project/assets/close.svg' %}
|
||||
{% trans 'Entfernen' %}
|
||||
</a>
|
||||
</div>
|
||||
{% for field in formset.empty_form %}
|
||||
{% if field.name == 'DELETE' %}
|
||||
{% include 'project/content/includes/_field.html' with field=field label=True hidden=True %}
|
||||
{% else %}
|
||||
{% include 'project/content/includes/_field.html' with field=field label=True %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
</script>
|
||||
|
||||
<div class="form__controls">
|
||||
<button class="button">
|
||||
<span class="button__wrapper">
|
||||
{% include 'project/assets/arrow-right-long.svg' %}
|
||||
<b>{{ object.submit_text }}</b>
|
||||
</span>
|
||||
{% include 'project/assets/arrow-right-long.svg' %}
|
||||
</button>
|
||||
{% if object.max_num > 1 %}
|
||||
<a class="button button--light" data-formset-add>
|
||||
<span class="button__wrapper">
|
||||
{% include 'project/assets/plus.svg' %}
|
||||
<b>{% trans 'Weitere Person hinzufügen' %}</b>
|
||||
</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
{% else %}
|
||||
{% if object.is_expired %}
|
||||
<h3 class="reveal reveal_animation">{% trans 'Die Anmeldungsfrist abgelaufen' %}</h3>
|
||||
<p class="reveal reveal_animation">
|
||||
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor
|
||||
invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et
|
||||
accusam et
|
||||
</p>
|
||||
{% else %}
|
||||
<h3 class="reveal reveal_animation">{% trans "Bereits abgeschlossen" %}</h3>
|
||||
<p class="reveal reveal_animation">
|
||||
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor
|
||||
invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et
|
||||
accusam et
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@ -0,0 +1,20 @@
|
||||
{% extends 'project/dialog.html' %}
|
||||
{% load i18n static %}
|
||||
|
||||
{% block extra_meta %}
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
{% endblock %}
|
||||
|
||||
{% block dialog_content %}
|
||||
<div class="dialog__text reveal_self reveal reveal_animation">
|
||||
{{ object.success_bodytext | safe }}
|
||||
|
||||
<a href="{% url 'memberzone:overview' %}" class="button">
|
||||
<span class="button__wrapper">
|
||||
{% include 'project/assets/arrow-right-long.svg' %}
|
||||
<b>{% trans 'Zur Accountübersicht' %}</b>
|
||||
</span>
|
||||
{% include 'project/assets/arrow-right-long.svg' %}
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -0,0 +1,16 @@
|
||||
{% extends 'main.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="content__frame">
|
||||
<div class="content__container">
|
||||
<div class="content__main content__main--center">
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form }}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -0,0 +1,34 @@
|
||||
import lxml.html
|
||||
from django import template
|
||||
from django.template.defaultfilters import stringfilter
|
||||
|
||||
from memberzone.models import MemberTaskRegistration
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter(name='task_status')
|
||||
def task_user_status(task, user):
|
||||
try:
|
||||
MemberTaskRegistration.objects.get(task=task, user=user)
|
||||
except MemberTaskRegistration.DoesNotExist:
|
||||
if task.is_expired:
|
||||
return 'control__item--status--expired'
|
||||
else:
|
||||
return ''
|
||||
else:
|
||||
return 'control__item--status--active'
|
||||
|
||||
|
||||
@register.filter
|
||||
@stringfilter
|
||||
def add_animation_classes(input_html):
|
||||
document = lxml.html.fromstring('<div>{}</div>'.format(input_html))
|
||||
for el in document.xpath('//p|ul|ol|h2|h3'):
|
||||
classes = el.attrib.get('class', '').split(' ')
|
||||
if 'reveal' not in classes:
|
||||
classes.append('reveal')
|
||||
if 'reveal_animation' not in classes:
|
||||
classes.append('reveal_animation')
|
||||
el.attrib['class'] = ' '.join(classes)
|
||||
return lxml.html.tostring(document)[5:-6]
|
||||
@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.conf.urls import url
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.views import LoginView, LogoutView, PasswordChangeView
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from memberzone.forms import LoginForm
|
||||
from memberzone.views import OverviewView, MemberTaskDetailView, MemberTaskDetailSuccessView, ProfileEditView
|
||||
|
||||
urlpatterns = [
|
||||
url(_(r'^login/$'), LoginView.as_view(
|
||||
success_url=reverse_lazy('memberzone:overview'),
|
||||
form_class=LoginForm
|
||||
), name='login'),
|
||||
url(_(r'^logout/$'), LogoutView.as_view(next_page='login'), name='logout'),
|
||||
|
||||
url(_(r'^account/change/password/$'), login_required(PasswordChangeView.as_view(
|
||||
template_name='memberzone/change_password.html',
|
||||
success_url=reverse_lazy('memberzone:change_password_done')
|
||||
), login_url=reverse_lazy('memberzone:login')), name='change_password'),
|
||||
|
||||
url(_(r'^account/change/password/succeeded/$'), login_required(TemplateView.as_view(
|
||||
template_name='memberzone/change_password_done.html'
|
||||
), login_url=reverse_lazy('memberzone:login')), name='change_password_done'),
|
||||
|
||||
url(_(r'^account/edit/$'), login_required(ProfileEditView.as_view(
|
||||
), login_url=reverse_lazy('memberzone:login')), name='profile_edit'),
|
||||
|
||||
url(_(r'^account/edit/succeeded/$'), login_required(TemplateView.as_view(
|
||||
template_name='memberzone/profile_edit_done.html'
|
||||
), login_url=reverse_lazy('memberzone:login')), name='profile_edit_done'),
|
||||
|
||||
url(_(r'^task/(?P<slug>[\w-]+)/$'), login_required(MemberTaskDetailView.as_view(
|
||||
), login_url=reverse_lazy('memberzone:login')), name='task'),
|
||||
|
||||
url(_(r'^task/(?P<slug>[\w-]+)/success/$'),
|
||||
login_required(MemberTaskDetailSuccessView.as_view(), login_url=reverse_lazy('memberzone:login')),
|
||||
name='task_success'),
|
||||
|
||||
url(_(r'^overview/$'), login_required(OverviewView.as_view(), login_url=reverse_lazy('memberzone:login')),
|
||||
name='overview'),
|
||||
]
|
||||
@ -0,0 +1,170 @@
|
||||
import json
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.forms import SetPasswordForm
|
||||
from django.contrib.auth.tokens import default_token_generator
|
||||
from django.contrib.auth.views import LoginView as DjangoLoginView
|
||||
from django.contrib.sites.shortcuts import get_current_site
|
||||
from django.core.mail import EmailMessage
|
||||
from django.db.models import Q
|
||||
from django.forms import formset_factory
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import resolve_url
|
||||
from django.template.loader import render_to_string
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.utils.encoding import force_bytes, force_text
|
||||
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import UpdateView, ListView, DetailView, FormView
|
||||
|
||||
from memberzone.models import MemberTask, MemberTaskRegistration, MemberDownloadFile, Profile
|
||||
|
||||
|
||||
class MemeberTaskQuerysetMixin(object):
|
||||
def get_queryset(self):
|
||||
return MemberTask.objects.filter(groups__in=self.request.user.groups.all()).distinct()
|
||||
|
||||
|
||||
class OverviewView(MemeberTaskQuerysetMixin, ListView):
|
||||
template_name = 'memberzone/overview.html'
|
||||
open_tasks = None
|
||||
|
||||
paginate_by = 3
|
||||
|
||||
def get_title(self):
|
||||
return _('Grüezi, {first_name} {last_name}').format(
|
||||
first_name=self.request.user.first_name,
|
||||
last_name=self.request.user.last_name,
|
||||
)
|
||||
|
||||
def get_download_sections(self):
|
||||
file_list = MemberDownloadFile.objects.filter(groups__in=self.request.user.groups.all())
|
||||
sections = {}
|
||||
for file in file_list:
|
||||
if not sections.get(file.section_id, None):
|
||||
sections[file.section_id] = {
|
||||
'section': file.section,
|
||||
'items': []
|
||||
}
|
||||
sections[file.section_id]['items'].append(file)
|
||||
|
||||
sections_list = [{'title': x['section'].title, 'items': x['items'], 'ordering': x['section'].ordering} for x in
|
||||
sections.values()]
|
||||
|
||||
return sorted(sections_list, key=lambda x: x['ordering'])
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super(OverviewView, self).get_queryset()
|
||||
return queryset.exclude(pk__in=self.get_open_tasks().values_list('pk', flat=True))
|
||||
|
||||
def get_open_tasks(self):
|
||||
yesterday = timezone.now() - timezone.timedelta(days=1)
|
||||
all_user_registrations = MemberTaskRegistration.objects.filter(user=self.request.user, cdate__lte=yesterday)
|
||||
queryset = MemeberTaskQuerysetMixin.get_queryset(self)
|
||||
queryset.exclude(registrations__in=all_user_registrations).order_by('-registrations__cdate')
|
||||
return queryset
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(OverviewView, self).get_context_data(**kwargs)
|
||||
context.update({
|
||||
'download_sections': self.get_download_sections(),
|
||||
'open_tasks': None if self.request.GET.get(self.page_kwarg, None) else self.get_open_tasks(),
|
||||
})
|
||||
return context
|
||||
|
||||
|
||||
class MemberTaskDetailView(MemeberTaskQuerysetMixin, DetailView):
|
||||
template_name = 'memberzone/task.html'
|
||||
model = MemberTask
|
||||
|
||||
def get_title(self):
|
||||
return self.object.title
|
||||
|
||||
def get_description(self):
|
||||
return self.object.sub_title
|
||||
|
||||
def get_formset(self):
|
||||
try:
|
||||
MemberTaskRegistration.objects.get(task=self.object, user=self.request.user)
|
||||
except MemberTaskRegistration.DoesNotExist:
|
||||
MemeberTaskFormset = formset_factory(
|
||||
self.object.form_class,
|
||||
extra=0,
|
||||
min_num=1,
|
||||
max_num=self.object.max_num,
|
||||
can_delete=True
|
||||
)
|
||||
formset_kwargs = {}
|
||||
if self.request.method == 'POST':
|
||||
formset_kwargs.update({
|
||||
'data': self.request.POST
|
||||
})
|
||||
return MemeberTaskFormset(**formset_kwargs)
|
||||
else:
|
||||
return None
|
||||
|
||||
def formset_valid(self, formset):
|
||||
registration = MemberTaskRegistration.objects.create(
|
||||
task=self.object,
|
||||
user=self.request.user,
|
||||
form_data=json.dumps(formset.cleaned_data)
|
||||
)
|
||||
if registration.task.fields.count() > 0:
|
||||
self.send_confirmation_email(registration=registration)
|
||||
return HttpResponseRedirect(reverse('memberzone:task_success', args=[self.object.slug]))
|
||||
|
||||
def formset_invalid(self, formset):
|
||||
return self.render_to_response(context=self.get_context_data(formset=formset, object=self.object))
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
formset = self.get_formset()
|
||||
return self.render_to_response(context=self.get_context_data(formset=formset, object=self.object))
|
||||
|
||||
def send_confirmation_email(self, registration):
|
||||
subject = _('Anmeldebestätigung: {}').format(registration.task.title)
|
||||
|
||||
confirmation_data = []
|
||||
for data_set in json.loads(registration.form_data):
|
||||
confirmation_data_set = []
|
||||
for field in registration.task.fields.all():
|
||||
value = data_set.get(field.name.replace('-', '_'), '')
|
||||
if isinstance(value, list):
|
||||
value = ','.join(value)
|
||||
confirmation_data_set.append((field.label, value,))
|
||||
confirmation_data.append(confirmation_data_set)
|
||||
|
||||
message = render_to_string('memberzone/email/member_registration_confirmation_email.html', {
|
||||
'data': confirmation_data,
|
||||
'subject': subject,
|
||||
})
|
||||
msg = EmailMessage(subject, message, settings.DEFAULT_FROM_EMAIL, to=[registration.user.email])
|
||||
msg.content_subtype = "html"
|
||||
# msg.send()
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
formset = self.get_formset()
|
||||
if formset.is_valid():
|
||||
return self.formset_valid(formset)
|
||||
else:
|
||||
return self.formset_invalid(formset)
|
||||
|
||||
|
||||
class MemberTaskDetailSuccessView(MemeberTaskQuerysetMixin, DetailView):
|
||||
template_name = 'memberzone/task_success.html'
|
||||
model = MemberTask
|
||||
|
||||
def get_title(self):
|
||||
return self.object.success_title
|
||||
|
||||
|
||||
class ProfileEditView(UpdateView):
|
||||
model = Profile
|
||||
fields = ['first_name', 'last_name', 'street', 'zip', 'place', 'email']
|
||||
template_name = 'memberzone/profile_edit.html'
|
||||
success_url = reverse_lazy('memberzone:profile_edit_done')
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return self.request.user.profile
|
||||
@ -0,0 +1,6 @@
|
||||
<svg width="35px" height="35px" viewBox="0 0 35 35" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<circle class="left" cx="7.5" cy="17.5" r="2"></circle>
|
||||
<circle class="middle" cx="17.5" cy="17.5" r="2"></circle>
|
||||
<circle class="right" cx="27.5" cy="17.5" r="2"></circle>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 341 B |
@ -0,0 +1,4 @@
|
||||
<svg width="50px" height="50px" viewBox="0 0 50 50" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<path d="M35.3991009,16.5843195 C35.9048173,16.1143809 36.6957419,16.1433845 37.1656805,16.6491009 C37.6356191,17.1548173 37.6066155,17.9457419 37.1008991,18.4156805 L20.9589061,33.4156805 C20.4523976,33.8863552 19.659996,33.8564187 19.1904442,33.348869 L12.8324372,26.4763471 C12.3636198,25.9695911 12.3943751,25.1787327 12.901131,24.7099153 C13.407887,24.2410979 14.1987454,24.2718532 14.6675628,24.7786091 L20.1748187,30.7315338 L35.3991009,16.5843195 Z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 626 B |
Loading…
Reference in New Issue