memberzone

standalone
Simon Caminada 8 years ago
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');
});

@ -1,4 +1,5 @@
$white: #FFFFFF;
$white_gray: #F9F9F9;
$light_gray: #F4F4F4;
$medium_light_gray: #E6E6E6;
$gray: #ADADAD;

@ -13,6 +13,8 @@
@import "modules/_contact.scss";
@import "modules/_content.scss";
@import "modules/_admin_editor.scss";
@import "modules/_downloads.scss";
@import "modules/_control-panel.scss";
@import "modules/plugins/_quote.scss";
@import "modules/plugins/_slider.scss";
@import "modules/plugins/_section.scss";

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

@ -67,6 +67,8 @@
font-size: em(11px);
color: $white;
font-weight: 500;
padding: 0 em(5px);
text-shadow: 0 1px 0 $green, 1px 0 0 $green, 0 -1px 0 $green, -1px 0 0 $green;
&:before, &:after {
display: block;
content: '';

@ -10,6 +10,7 @@ https://control.divio.com/api/v1/apps/serve/djangocms-picture/2.0.6/005e8663-d1c
https://control.divio.com/api/v1/apps/serve/djangocms-text-ckeditor/3.5.3/a7b5179f-cea5-4af8-b235-6b7f709c4e6a/djangocms-text-ckeditor-3.5.3.tar.gz#egg=djangocms-text-ckeditor==3.5.3
https://control.divio.com/api/v1/apps/serve/django-filer/1.3.0.1/bcb7d25b-6922-48a9-a252-9bc165f6403e/django-filer-1.3.0.1.tar.gz#egg=django-filer==1.3.0.1
# </INSTALLED_ADDONS>
lxml
Whoosh==2.7.4
aldryn-search==0.5.0
django-fontawesome==0.3.1

@ -25,6 +25,7 @@ aldryn_addons.settings.load(locals())
INSTALLED_APPS.extend([
'project',
'memberzone',
'fontawesome',
'haystack',
'aldryn_search',

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

@ -118,7 +118,7 @@
</div>
<div class="footer">
<a href="#" class="footer__login header__button header__button--light data_id_1">
<a href="{% url 'memberzone:overview' %}" class="footer__login header__button header__button--light data_id_1">
<span class="header__button__icon">
{% include 'project/assets/arrow-right-long.svg' %}
</span>

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

@ -18,7 +18,7 @@
{% endfor %}
</ul>
</div>
<a href="#" class="footer__login header__button header__button--light header__button--reverse data_id_1">
<a href="{% url 'memberzone:overview' %}" class="footer__login header__button header__button--light header__button--reverse data_id_1">
<span class="header__button__icon">
{% include 'project/assets/arrow-right-long.svg' %}
</span>

@ -12,6 +12,9 @@
<span class="download__item__icon">
{% include 'project/assets/download.svg' %}
</span>
{% if item.tag_list %}
<span class="downloads__item__tags">{{ file.tag_list }}</span>
{% endif %}
</a>
</li>
{% endfor %}

@ -10,4 +10,4 @@ def page_image(id):
try:
return Page.objects.get(pk=id).imageextension.image
except:
return None
return None

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from django.conf.urls import url
from django.conf.urls import url, include
from django.utils.translation import ugettext_lazy as _
from django.views.generic.base import TemplateView
@ -11,4 +11,6 @@ urlpatterns = [
url(r'^newsletter/subscription/success/$',
TemplateView.as_view(template_name='project/newsletter/subscription.html'), kwargs={'success': True},
name='newsletter_subscription_success'),
url(_(r'^memberzone/'), include('memberzone.urls', namespace='memberzone')),
]

Loading…
Cancel
Save