Merge branch 'master' into klettertreff

docker
relnod 9 years ago committed by GitHub
commit 79a354b015

@ -62,7 +62,7 @@ ROOT_URLCONF = 'jdav_web.urls'
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [], 'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True, 'APP_DIRS': True,
'OPTIONS': { 'OPTIONS': {
'context_processors': [ 'context_processors': [
@ -129,6 +129,9 @@ USE_TZ = True
# https://docs.djangoproject.com/en/1.10/howto/static-files/ # https://docs.djangoproject.com/en/1.10/howto/static-files/
STATIC_URL = '/static/' STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static")
]
# Locale files (translations) # Locale files (translations)

@ -0,0 +1,23 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-01-14 17:13+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: templates/admin/base_site.html:5 templates/admin/base_site.html:9
msgid "JDAV Administration"
msgstr "JDAV Verwaltung"

@ -1,16 +1,27 @@
from django.contrib import admin from django.contrib import admin, messages
from django.contrib.admin import helpers from django.contrib.admin import helpers
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.shortcuts import render from django.shortcuts import render
from django.db import models
from django import forms
from .models import Message from .models import Message, Attachment
class AttachmentInline(admin.StackedInline):
model = Attachment
extra = 0
class MessageAdmin(admin.ModelAdmin): class MessageAdmin(admin.ModelAdmin):
"""Message creation view""" """Message creation view"""
list_display = ('subject', 'from_addr', 'get_groups', 'sent') list_display = ('subject', 'from_addr', 'get_groups', 'sent')
change_form_template = "mailer/change_form.html" change_form_template = "mailer/change_form.html"
formfield_overrides = {
models.ManyToManyField: {'widget': forms.CheckboxSelectMultiple}
}
inlines = [AttachmentInline]
actions = ['send_message'] actions = ['send_message']
def send_message(self, request, queryset): def send_message(self, request, queryset):
@ -30,13 +41,19 @@ class MessageAdmin(admin.ModelAdmin):
def response_change(self, request, obj): def response_change(self, request, obj):
if "_send" in request.POST: if "_send" in request.POST:
obj.submit() if not obj.submit():
messages.error(request, _("Failed to send message"))
else:
messages.info(request, _("Successfully sent message"))
return super(MessageAdmin, self).response_change(request, obj) return super(MessageAdmin, self).response_change(request, obj)
def response_add(self, request, obj): def response_add(self, request, obj):
if "_send" in request.POST: if "_send" in request.POST:
obj.submit() if not obj.submit():
return super(MessageAdmin, self).response_change(request, obj) messages.error(request, _("Failed to send message"))
else:
messages.info(request, _("Successfully sent message"))
return super(MessageAdmin, self).response_add(request, obj)
admin.site.register(Message, MessageAdmin) admin.site.register(Message, MessageAdmin)

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-11-19 16:11+0100\n" "POT-Creation-Date: 2017-01-14 17:13+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,104 +18,204 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: admin.py:20 #: mailer/admin.py:32
msgid "Message sent" msgid "Message sent"
msgstr "Nachricht gesendet" msgstr "Nachricht gesendet"
#: admin.py:28 #: mailer/admin.py:40
msgid "Send message" msgid "Send message"
msgstr "Nachricht verschicken" msgstr "Nachricht verschicken"
#: apps.py:7 #: mailer/admin.py:45 mailer/admin.py:53
msgid "Failed to send message"
msgstr "Fehler beim senden der Email"
#: mailer/admin.py:47 mailer/admin.py:55
msgid "Successfully sent message"
msgstr "Email wurde erfolgreich verschickt"
#: mailer/apps.py:7
msgid "mailer" msgid "mailer"
msgstr "Verteiler" msgstr "Verteiler"
#: models.py:9 #: mailer/models.py:34
msgid "from email" msgid "from email"
msgstr "Von Email" msgstr "Von Email"
#: models.py:10 #: mailer/models.py:35
msgid "subject" msgid "subject"
msgstr "Betreff" msgstr "Betreff"
#: models.py:11 #: mailer/models.py:36
msgid "content" msgid "content"
msgstr "Inhalt" msgstr "Inhalt"
#: models.py:12 #: mailer/models.py:38
msgid "to group" msgid "to group"
msgstr "An Gruppe" msgstr "An Gruppe"
#: models.py:13 #: mailer/models.py:39
msgid "sent" msgid "sent"
msgstr "Gesendet" msgstr "Gesendet"
#: models.py:29 #: mailer/models.py:46
msgid "recipients"
msgstr "Empfänger"
#: mailer/models.py:74
msgid "message" msgid "message"
msgstr "Nachricht" msgstr "Nachricht"
#: models.py:30 #: mailer/models.py:75
msgid "messages" msgid "messages"
msgstr "Nachrichten" msgstr "Nachrichten"
#: models.py:32 #: mailer/models.py:77
msgid "Can submit mails" msgid "Can submit mails"
msgstr "Kann Mails verschicken" msgstr "Kann Mails verschicken"
#: templates/admin/change_form.html:11 #: mailer/models.py:86
msgid "file"
msgstr "Datei"
#: mailer/models.py:92
msgid "Empty"
msgstr "Leer"
#: mailer/models.py:95
msgid "attachment"
msgstr "Anhang"
#: mailer/models.py:96
msgid "attachments"
msgstr "Anhänge"
#: mailer/templates/mailer/change_form.html:11
msgid "Save and send mail" msgid "Save and send mail"
msgstr "Speichern und Email senden" msgstr "Speichern und Email senden"
#: templates/mailer/confirm_send.html:7 #: mailer/templates/mailer/confirm_send.html:7
msgid "Do you really want to send these mails?" msgid "Do you really want to send these mails?"
msgstr "Möchtest du diese Emails wirklich verschicken?" msgstr "Möchtest du diese Emails wirklich verschicken?"
#: templates/mailer/confirm_send.html:13 #: mailer/templates/mailer/confirm_send.html:13
msgid "already sent" msgid "already sent"
msgstr "schon verschickt" msgstr "schon verschickt"
#: templates/mailer/confirm_send.html:19 #: mailer/templates/mailer/confirm_send.html:19
msgid "" msgid ""
"Some messages have already been sent! Do you really want to resend them?" "Some messages have already been sent! Do you really want to resend them?"
msgstr "" msgstr ""
"Einige Emails wurden schon versendet! Möchtest du diese wirklich nochmal " "Einige Emails wurden schon versendet! Möchtest du diese wirklich nochmal "
"senden?" "senden?"
#: templates/mailer/confirm_send.html:30 #: mailer/templates/mailer/confirm_send.html:30
msgid "Send" msgid "Send"
msgstr "Senden" msgstr "Senden"
#: templates/mailer/confirm_send.html:35 #: mailer/templates/mailer/confirm_send.html:35
msgid "Cancel" msgid "Cancel"
msgstr "Abbruch" msgstr "Abbruch"
#: templates/mailer/index.html:2 #: mailer/templates/mailer/confirmation_sent.html:4
#: mailer/templates/mailer/unsubscribe.html:5
#: mailer/templates/mailer/unsubscribe.html:25
msgid "Unsubscribe"
msgstr "Vom Newsletter abmelden"
#: mailer/templates/mailer/confirmation_sent.html:7
msgid "Sent confirmation mail to"
msgstr "Bestätigungsmail gesendet an"
#: mailer/templates/mailer/confirmation_sent.html:7
msgid "Follow the link in your mail to confirm your unsubscription."
msgstr ""
#: mailer/templates/mailer/index.html:2
msgid "This is the mailer app!" msgid "This is the mailer app!"
msgstr "Das ist die Mailer App!" msgstr "Das ist die Mailer App!"
#: templates/mailer/send.html:2 #: mailer/templates/mailer/send.html:2
msgid "Here you can send new emails!" msgid "Here you can send new emails!"
msgstr "Hier kannst du neue Emails verschicken!" msgstr "Hier kannst du neue Emails verschicken!"
#: templates/mailer/send.html:11 #: mailer/templates/mailer/send.html:11
msgid "Subject:" msgid "Subject:"
msgstr "Betreff" msgstr "Betreff"
#: templates/mailer/send.html:14 #: mailer/templates/mailer/send.html:14
msgid "Content:" msgid "Content:"
msgstr "Inhalt:" msgstr "Inhalt:"
#: templates/mailer/send.html:17 #: mailer/templates/mailer/send.html:17
msgid "Receiving group:" msgid "Receiving group:"
msgstr "Erhaltende Gruppe" msgstr "Erhaltende Gruppe"
#: templates/mailer/send.html:24 #: mailer/templates/mailer/send.html:24
msgid "Send mail" msgid "Send mail"
msgstr "Email senden" msgstr "Email senden"
#: views.py:33 #: mailer/templates/mailer/subscribe.html:5
msgid "Here you can register yourself to the newsletter"
msgstr "Hier kannst du dich für den Newsletter anmelden."
#: mailer/templates/mailer/subscribe.html:16
msgid "Prename"
msgstr "Vorname"
#: mailer/templates/mailer/subscribe.html:21
msgid "Lastname"
msgstr "Nachname"
#: mailer/templates/mailer/subscribe.html:26 mailer/views.py:61
msgid "Birthdate"
msgstr "Geburtsdatum"
#: mailer/templates/mailer/subscribe.html:37
#: mailer/templates/mailer/unsubscribe.html:20
msgid "Email address"
msgstr "Email-Adresse"
#: mailer/templates/mailer/subscribe.html:42
msgid "Register"
msgstr "Registrieren"
#: mailer/templates/mailer/subscribed.html:3
msgid "Subscribed successfully"
msgstr "Erfolgreich angemeldet"
#: mailer/templates/mailer/unsubscribe.html:9
msgid "Here you can unsubscribe from the newsletter"
msgstr "Hier kannst du dich vom Newsletter abmelden"
#: mailer/templates/mailer/unsubscribed.html:8
msgid "Successfully unsubscribed from the newsletter for "
msgstr "Newsletter erfolgreich abbestellt für "
#: mailer/views.py:35
msgid "Can't verify this link. Try again!"
msgstr "Ungültiger Link. Bitte nochmal versuchen!"
#: mailer/views.py:47
msgid "Please fill in every field"
msgstr "Bitte jedes Feld ausfüllen!"
#: mailer/views.py:84
msgid "Please fill in every field!" msgid "Please fill in every field!"
msgstr "Bitte jedes Feld ausfüllen!" msgstr "Bitte jedes Feld ausfüllen!"
#: mailer/views.py:91
msgid "Member already exists"
msgstr "Mitglied schon vorhanden"
#~ msgid "Confirmation of unsubscription"
#~ msgstr "Abmeldebestätigung"
#~ msgid ""
#~ "Click the link to unsubscribe to the newsletter of the JDAV\n"
#~ "{}"
#~ msgstr ""
#~ "Klicke auf den Link um dich von Newsletter des JDAV Ludwigsburg abzumelden"
#~ msgid "History" #~ msgid "History"
#~ msgstr "Geschichte" #~ msgstr "Geschichte"

@ -1,16 +1,26 @@
from django.core.mail import send_mass_mail, send_mail from django.core.mail import EmailMessage
def send(subject, content, sender, recipicient): def send(subject, content, sender, recipients, reply_to=None,
send_mail(subject, content, sender, [recipicient]) attachments=None):
if type(recipients) != list:
recipients = [recipients]
def send_mass(subject, content, sender, recipicients): if reply_to is not None:
data = [ kwargs = {"reply_to": [reply_to]}
(subject, content, sender, [recipicient]) else:
for recipicient in recipicients] kwargs = {}
print("sending data", data) for recipient in recipients:
send_mass_mail(data) email = EmailMessage(subject, content, sender, [recipient], **kwargs)
if attachments is not None:
for attach in attachments:
email.attach_file(attach)
try:
email.send()
except Exception as e:
print("Error when sending mail:", e)
return False
else:
return True
def get_content(content): def get_content(content):

@ -1,6 +1,31 @@
from django.db import models from django.db import models
from django.forms import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from .mailutils import send_mass, get_content from .mailutils import send, get_content
import os
class RestrictedFileField(models.FileField):
def __init__(self, *args, **kwargs):
if "max_upload_size" in kwargs:
self.max_upload_size = kwargs.pop("max_upload_size")
super(RestrictedFileField, self).__init__(*args, **kwargs)
def clean(self, *args, **kwargs):
data = super(RestrictedFileField, self).clean(*args, **kwargs)
f = data.file
try:
if f._size > self.max_upload_size:
raise forms.ValidationError('Please keep filesize under {}. '
'Current filesize: '
'{}'.format(self.max_upload_size,
f._size))
except AttributeError as e:
print(e)
return data
# Create your models here. # Create your models here.
@ -29,10 +54,21 @@ class Message(models.Model):
if not member.gets_newsletter: if not member.gets_newsletter:
continue continue
members.add(member) members.add(member)
send_mass(self.subject, get_content(self.content), attach = [a.f.path for a in Attachment.objects.filter(msg__id=self.pk)
self.from_addr, [member.email for member in members]) if a.f.name]
success = send(self.subject, get_content(self.content),
self.from_addr, [member.email for member in members],
attachments=attach)
for a in Attachment.objects.filter(msg__id=self.pk):
if a.f.name:
os.remove(a.f.path)
a.delete()
if success:
self.sent = True self.sent = True
self.save() self.save()
return True
else:
return False
class Meta: class Meta:
verbose_name = _('message') verbose_name = _('message')
@ -40,3 +76,21 @@ class Message(models.Model):
permissions = ( permissions = (
("submit_mails", _("Can submit mails")), ("submit_mails", _("Can submit mails")),
) )
class Attachment(models.Model):
"""Represents an attachment to an email"""
msg = models.ForeignKey(Message, on_delete=models.CASCADE)
print("attachment class")
# file (not naming it file because of builtin)
f = RestrictedFileField(_('file'),
upload_to='attachments',
blank=True,
max_upload_size=10485760)
def __str__(self):
return os.path.basename(self.f.name) if self.f.name else _("Empty")
class Meta:
verbose_name = _('attachment')
verbose_name_plural = _('attachments')

@ -1,3 +1,7 @@
{% load i18n %} {% load i18n %}
<head>
<title>{% trans "Unsubscribe" %}</title>
</head>
<p>{% trans "Sent confirmation mail to" %} <b>{{ email }}</b>. {% trans "Follow the link in your mail to confirm your unsubscription." %}</p> <p>{% trans "Sent confirmation mail to" %} <b>{{ email }}</b>. {% trans "Follow the link in your mail to confirm your unsubscription." %}</p>

@ -1,5 +1,11 @@
{% load i18n %} {% load i18n %}
<head>
<title>
{% trans "Unsubscribe" %}
</title>
</head>
<p><b>{% trans "Here you can unsubscribe from the newsletter" %}</b></p> <p><b>{% trans "Here you can unsubscribe from the newsletter" %}</b></p>
{% if error_message %} {% if error_message %}

@ -1,5 +1,9 @@
{% load i18n %} {% load i18n %}
<head>
<title>{% trans Unsubscribe %}</title>
</head>
<p> <p>
{% trans "Successfully unsubscribed from the newsletter for " %}<b>{{ email }}</b> {% trans "Successfully unsubscribed from the newsletter for " %}<b>{{ email }}</b>
</p> </p>

@ -46,9 +46,10 @@ def unsubscribe(request):
except (KeyError, Member.DoesNotExist): except (KeyError, Member.DoesNotExist):
return render_unsubscribe(request, _("Please fill in every field")) return render_unsubscribe(request, _("Please fill in every field"))
else: else:
send_mail(_("Confirmation of unsubscription"), send_mail("Abmeldebestätigung",
_("Click the link to unsubscribe to the newsletter of" "Klicke auf den Link, um dich vom Newsletter des JDAV "
" the JDAV\n{}".format(get_unsubscribe_link(member))), "Ludwigsburg "
"abzumelden\n{}".format(get_unsubscribe_link(member)),
mail_root, email) mail_root, email)
return render_confirmation_sent(request, email) return render_confirmation_sent(request, email)

@ -1,5 +1,5 @@
from django.contrib import admin from django.contrib import admin
from django.utils. translation import ugettext_lazy as translate from django.utils.translation import ugettext_lazy as _
from django.contrib.admin import SimpleListFilter from django.contrib.admin import SimpleListFilter
from .models import MaterialPart, Ownership from .models import MaterialPart, Ownership
@ -16,13 +16,13 @@ class OwnershipInline(admin.StackedInline):
class NotTooOldFilter(SimpleListFilter): class NotTooOldFilter(SimpleListFilter):
title = translate('Age') title = _('Age')
parameter_name = 'age' parameter_name = 'age'
def lookups(self, request, model_admin): def lookups(self, request, model_admin):
return ( return (
('too_old', translate('Not Too Old')), ('too_old', _('Not too old')),
('not_too_old', translate('Too old')), ('not_too_old', _('Too old')),
) )
def queryset(self, request, queryset): def queryset(self, request, queryset):

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-10-21 23:38+0200\n" "POT-Creation-Date: 2017-01-14 17:13+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,50 +18,70 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: apps.py:7 #: material/admin.py:19
msgid "Age"
msgstr "Alter"
#: material/admin.py:24
msgid "Not too old"
msgstr "Nicht zu alt"
#: material/admin.py:25
msgid "Too old"
msgstr "Zu alt"
#: material/apps.py:7
msgid "material" msgid "material"
msgstr "Material" msgstr "Material"
#: models.py:17 #: material/models.py:17
msgid "name" msgid "name"
msgstr "Name" msgstr "Name"
#: models.py:18 #: material/models.py:18
msgid "quantity"
msgstr "Anzahl"
#: material/models.py:19
msgid "purchase date" msgid "purchase date"
msgstr "Kaufdatum" msgstr "Kaufdatum"
#: models.py:19 #: material/models.py:20
msgid "lifetime (years)" msgid "lifetime (years)"
msgstr "Lebenszeit (Jahre)" msgstr "Lebenszeit (Jahre)"
#: models.py:21 #: material/models.py:21
msgid "photo" msgid "photo"
msgstr "Bild" msgstr "Bild"
#: models.py:35 #: material/models.py:32
msgid "Quantity"
msgstr "Anzahl"
#: material/models.py:42
msgid "Not too old?" msgid "Not too old?"
msgstr "Nicht zu alt?" msgstr "Nicht zu alt?"
#: models.py:38 #: material/models.py:45
msgid "material part" msgid "material part"
msgstr "Materialteil" msgstr "Materialteil"
#: models.py:39 #: material/models.py:46
msgid "material parts" msgid "material parts"
msgstr "Materialteile" msgstr "Materialteile"
#: models.py:45 #: material/models.py:52
msgid "owner" msgid "owner"
msgstr "Besitzer" msgstr "Besitzer"
#: models.py:46 #: material/models.py:53
msgid "count" msgid "count"
msgstr "Anzahl" msgstr "Anzahl"
#: models.py:53 #: material/models.py:60
msgid "ownership" msgid "ownership"
msgstr "Besitztum" msgstr "Besitztum"
#: models.py:54 #: material/models.py:61
msgid "ownerships" msgid "ownerships"
msgstr "Besitztümer" msgstr "Besitztümer"

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.10.2 on 2016-10-18 19:07 # Generated by Django 1.10.2 on 2016-11-19 14:56
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
@ -11,7 +11,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('members', '0004_auto_20161018_1744'), ('members', '0001_initial'),
] ]
operations = [ operations = [
@ -19,17 +19,27 @@ class Migration(migrations.Migration):
name='MaterialPart', name='MaterialPart',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=30)), ('name', models.CharField(max_length=30, verbose_name='name')),
('buy_date', models.DateField(verbose_name='purchase date')), ('buy_date', models.DateField(verbose_name='purchase date')),
('lifetime', models.DecimalField(decimal_places=0, max_digits=3, verbose_name='lifetime (years)')),
('photo', models.ImageField(blank=True, upload_to='images', verbose_name='photo')),
], ],
options={
'verbose_name_plural': 'material parts',
'verbose_name': 'material part',
},
), ),
migrations.CreateModel( migrations.CreateModel(
name='Ownership', name='Ownership',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('count', models.IntegerField(default=1)), ('count', models.IntegerField(default=1, verbose_name='count')),
('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='material.MaterialPart')), ('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='material.MaterialPart')),
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='members.Member')), ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='members.Member', verbose_name='owner')),
], ],
options={
'verbose_name_plural': 'ownerships',
'verbose_name': 'ownership',
},
), ),
] ]

@ -31,7 +31,6 @@ class MaterialPart(models.Model):
quantity_real.admin_order_field = 'quantity' quantity_real.admin_order_field = 'quantity'
quantity_real.short_description = _('Quantity') quantity_real.short_description = _('Quantity')
def not_too_old(self): def not_too_old(self):
"""Returns wether the part should be replaced cause of age""" """Returns wether the part should be replaced cause of age"""
buy_time = timezone.make_aware(datetime.combine(self.buy_date, buy_time = timezone.make_aware(datetime.combine(self.buy_date,

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 KiB

@ -1,21 +1,76 @@
\documentclass{article} \documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{booktabs} \usepackage{booktabs}
\usepackage{amssymb}
\usepackage{cmbright} \usepackage{cmbright}
\usepackage[margin=1in,landscape]{geometry} \usepackage{graphicx}
\usepackage{textpos}
\usepackage[colorlinks]{hyperref}
\usepackage{float}
\usepackage[margin=1in]{geometry}
\newcommand{\picpos}[4]{
\begin{textblock*}{#1}(#2, #3)
\includegraphics[width=\textwidth]{#4}
\end{textblock*}
}
\renewcommand{\arraystretch}{1.5}
\newcommand{\tickedbox}{
\makebox[0pt][l]{$\square$}\raisebox{.15ex}{\hspace{0.1em}$\checkmark$}
}
\newcommand{\checkbox}{
\makebox[0pt][l]{$\square$}
}
\begin{document} \begin{document}
\noindent % HEADER RIGHT
{\LARGE\textsc{MEMBERLIST-TITLE}}\\ \picpos{4.5cm}{11.5cm}{0cm}{dav_logo.png}
\begin{textblock*}{5cm}(11.5cm, 2.3cm)
\begin{flushright}
\small
\noindent Deutscher Alpenverein e. V. \\
Sektion Ludwigsburg\\
Imbröderstraße 14\\
71634 Ludwigsburg\\
Tel.: 07141 927893\\
Fax: 07141 924042\\
info@alpenverein-ludwigsburg.de\\
\end{flushright}
\end{textblock*}
% HEADLINE
{\noindent\LARGE\textsc{Teilnehmerliste \\Sektionsveranstaltung}}\\
\textit{Erstellt: MEMBERLIST-DATE}\\ \textit{Erstellt: MEMBERLIST-DATE}\\
MEMBERLIST-COMMENTS
\begin{table}[h] % DESCRIPTION TABLE
\begin{tabular}{llll} \begin{table}[H]
\begin{tabular}{ll}
\large Aktivität: & ACTIVITY \\
\large Gruppe: & GROUP \\
\large Ziel: & DESTINATION \\
\large Stützpunkt: & PLACE \\
\large Zeitraum: & TIME-PERIOD \\
\large Betreuer: & JUGENDLEITER \\
\end{tabular}
\end{table}
\noindent TOUR-TYPE
\begin{table}[H]
\begin{tabular*}{1\linewidth}{@{\extracolsep{\fill}}llll}
\toprule \toprule
Name & Vorname & Geburtsdatum & Kommentare \\ \textbf{Name} & \textbf{Anschrift} & \textbf{Telefon} & \textbf{E-Mail} \\
\midrule \midrule
\input{TABLE-NAME} \input{TABLE-NAME}
\bottomrule \bottomrule
\end{tabular} \end{tabular*}
\end{table} \end{table}
\vspace{1cm}
\noindent Bitte die ausgefüllte Teilnehmerliste vor Antritt der Aktivität per E-Mail an
\href{mailto:info@alpenverein-ludwigsburg.de}{info@alpenverein.de} und
\href{mailto:vorstand@alpenverein-ludwigsburg.de}{vorstand@alpenverein-ludwigsburg.de} senden.
\end{document} \end{document}

@ -11,7 +11,7 @@ from django import forms
from django.contrib import admin from django.contrib import admin
from django.contrib.admin import DateFieldListFilter from django.contrib.admin import DateFieldListFilter
from django.utils.translation import ugettext_lazy as translate from django.utils.translation import ugettext_lazy as translate
from django.db.models import TextField from django.db.models import TextField, ManyToManyField
from django.forms import Textarea from django.forms import Textarea
from django.shortcuts import render from django.shortcuts import render
@ -23,9 +23,11 @@ from .models import (Member, Group, MemberList, MemberOnList, Klettertreff,
class MemberAdmin(admin.ModelAdmin): class MemberAdmin(admin.ModelAdmin):
fields = ['prename', 'lastname', 'email', 'street', 'town', 'phone_number', 'phone_number_parents', 'birth_date', 'group', fields = ['prename', 'lastname', 'email', 'street', 'town', 'phone_number', 'phone_number_parents', 'birth_date', 'group',
'gets_newsletter', 'comments'] 'gets_newsletter', 'comments']
list_display = ('name', 'street', 'town', 'phone_number', list_display = ('name', 'birth_date', 'gets_newsletter', 'get_group', 'comments')
'phone_number_parents', 'birth_date', 'gets_newsletter', 'get_group', 'comments')
list_filter = ('group', 'gets_newsletter') list_filter = ('group', 'gets_newsletter')
formfield_overrides = {
ManyToManyField: {'widget': forms.CheckboxSelectMultiple}
}
class GroupAdmin(admin.ModelAdmin): class GroupAdmin(admin.ModelAdmin):
@ -37,12 +39,13 @@ class MemberListAdminForm(forms.ModelForm):
model = MemberList model = MemberList
exclude = ['add_member'] exclude = ['add_member']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(MemberListAdminForm, self).__init__(*args, **kwargs) super(MemberListAdminForm, self).__init__(*args, **kwargs)
if self.instance.pk: self.fields['jugendleiter'].queryset = Member.objects.filter(group__name='Jugendleiter')
pass
#self.fields['add_member'].queryset = Member.objects.filter(prename__startswith='F') #self.fields['add_member'].queryset = Member.objects.filter(prename__startswith='F')
class MemberOnListInline(admin.StackedInline): class MemberOnListInline(admin.StackedInline):
model = MemberOnList model = MemberOnList
extra = 0 extra = 0
@ -53,9 +56,13 @@ class MemberOnListInline(admin.StackedInline):
} }
class MemberListAdmin(admin.ModelAdmin): class MemberListAdmin(admin.ModelAdmin):
inlines = [MemberOnListInline]
form = MemberListAdminForm form = MemberListAdminForm
list_display = ['__str__', 'date']
actions = ['convert_to_pdf'] actions = ['convert_to_pdf']
inlines = [MemberOnListInline] formfield_overrides = {
ManyToManyField: {'widget': forms.CheckboxSelectMultiple}
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(MemberListAdmin, self).__init__(*args, **kwargs) super(MemberListAdmin, self).__init__(*args, **kwargs)
@ -65,10 +72,9 @@ class MemberListAdmin(admin.ModelAdmin):
""" """
for memberlist in queryset: for memberlist in queryset:
# build a unique filename # create a unique filename
filename = memberlist.name + "_" + datetime.today().strftime("%d_%m_%Y") filename = memberlist.name + "_" + datetime.today().strftime("%d_%m_%Y")
filename = filename.replace(' ', '_') filename = filename.replace(' ', '_')
filename_table = 'table_' + filename filename_table = 'table_' + filename
filename_tex = filename + '.tex' filename_tex = filename + '.tex'
filename_pdf = filename + '.pdf' filename_pdf = filename + '.pdf'
@ -77,9 +83,9 @@ class MemberListAdmin(admin.ModelAdmin):
with open('media/memberlists/'+filename_table, 'w+') as f: with open('media/memberlists/'+filename_table, 'w+') as f:
for memberonlist in memberlist.memberonlist_set.all(): for memberonlist in memberlist.memberonlist_set.all():
# write table of members in latex compatible format # write table of members in latex compatible format
line = '{0} & {1} & {2} & {3} \\\\ \n'.format(memberonlist.member.prename, line = '{0} {1} & {2}, {3} & {4} & {5} \\\\ \n'.format(memberonlist.member.prename,
memberonlist.member.lastname, memberonlist.member.lastname, memberonlist.member.street,
memberonlist.member.birth_date.strftime('%d.%m.%Y'), memberonlist.comments) memberonlist.member.town, memberonlist.member.phone_number, memberonlist.member.email)
f.write(line) f.write(line)
# copy and adapt latex memberlist template # copy and adapt latex memberlist template
@ -91,13 +97,36 @@ class MemberListAdmin(admin.ModelAdmin):
template_content = f.read() template_content = f.read()
# adapt template # adapt template
template_content = template_content.replace('MEMBERLIST-TITLE', memberlist.name) template_content = template_content.replace('ACTIVITY', memberlist.name)
groups = ', '.join(g.name for g in memberlist.groups.all())
template_content = template_content.replace('GROUP', groups)
template_content = template_content.replace('DESTINATION', memberlist.destination)
template_content = template_content.replace('PLACE', memberlist.place)
template_content = template_content.replace('MEMBERLIST-DATE', template_content = template_content.replace('MEMBERLIST-DATE',
memberlist.date.strftime('%d.%m.%Y')) datetime.today().strftime('%d.%m.%Y'))
template_content = template_content.replace('MEMBERLIST-COMMENTS', time_period = memberlist.date.strftime('%d.%m.%Y')
memberlist.comment) if memberlist.end != memberlist.date:
time_period += " - " + memberlist.end.strftime('%d.%m.%Y')
template_content = template_content.replace('TIME-PERIOD', time_period)
jugendleiter = ', '.join(j.name for j in memberlist.jugendleiter.all())
template_content = template_content.replace('JUGENDLEITER', jugendleiter)
# create tickboxes for tour type
tour_type = ''
for tt in ['Gemeinschaftstour', 'Führungstour', 'Ausbildung']:
print(memberlist.tour_type)
if tt in memberlist.tour_type:
tour_type += '\\tickedbox ' + tt
else:
tour_type += '\\checkbox'
tour_type += '\\enspace ' + tt
tour_type += '\\qquad \\qquad '
template_content = template_content.replace('TOUR-TYPE', tour_type)
template_content = template_content.replace('TABLE-NAME', template_content = template_content.replace('TABLE-NAME',
filename_table) filename_table)
# write adapted template to file # write adapted template to file
with open('media/memberlists/' + filename_tex, 'w') as f: with open('media/memberlists/' + filename_tex, 'w') as f:
f.write(template_content) f.write(template_content)
@ -126,6 +155,7 @@ class MemberListAdmin(admin.ModelAdmin):
return response return response
class KlettertreffAdminForm(forms.ModelForm): class KlettertreffAdminForm(forms.ModelForm):
class Meta: class Meta:
model = Klettertreff model = Klettertreff
@ -178,6 +208,10 @@ class KlettertreffAdmin(admin.ModelAdmin):
return render(request, 'admin/klettertreff_overview.html', return render(request, 'admin/klettertreff_overview.html',
context) context)
formfield_overrides = {
ManyToManyField: {'widget': forms.CheckboxSelectMultiple}
}
admin.site.register(Member, MemberAdmin) admin.site.register(Member, MemberAdmin)
admin.site.register(Group, GroupAdmin) admin.site.register(Group, GroupAdmin)

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-10-21 23:37+0200\n" "POT-Creation-Date: 2017-01-14 17:13+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,38 +18,119 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: apps.py:7 models.py:45 #: members/apps.py:7 members/models.py:81
msgid "members" msgid "members"
msgstr "Teilnehmer" msgstr "Teilnehmer"
#: models.py:10 #: members/models.py:13
msgid "name" msgid "name"
msgstr "Name" msgstr "Name"
#: models.py:12 #: members/models.py:15
msgid "minimum age (years)" msgid "minimum age (years)"
msgstr "Mindestalter (Jahre)" msgstr "Mindestalter (Jahre)"
#: models.py:19 #: members/models.py:22 members/models.py:39
msgid "group" msgid "group"
msgstr "Gruppe" msgstr "Gruppe"
#: models.py:20 #: members/models.py:23
msgid "groups" msgid "groups"
msgstr "Gruppen" msgstr "Gruppen"
#: models.py:28 #: members/models.py:31
msgid "prename" msgid "prename"
msgstr "Vorname" msgstr "Vorname"
#: models.py:29 #: members/models.py:32
msgid "last name" msgid "last name"
msgstr "Nachname" msgstr "Nachname"
#: models.py:31 #: members/models.py:33
msgid "street"
msgstr "Straße"
#: members/models.py:34
msgid "town"
msgstr "Stadt"
#: members/models.py:35
msgid "phone number"
msgstr "Telefonnummer"
#: members/models.py:36
msgid "parents phone number"
msgstr "Telefonnummer der Eltern"
#: members/models.py:38
msgid "birth date" msgid "birth date"
msgstr "Geburtsdatum" msgstr "Geburtsdatum"
#: models.py:44 #: members/models.py:40
msgid "receives newsletter"
msgstr "Erhält den Newsletter"
#: members/models.py:44
msgid "comments"
msgstr "Kommentare"
#: members/models.py:77
msgid "Group"
msgstr "Gruppe"
#: members/models.py:80
msgid "member" msgid "member"
msgstr "Teilnehmer" msgstr "Teilnehmer"
#: members/models.py:86
msgid "Listname"
msgstr "Name der Liste"
#: members/models.py:88
msgid "date"
msgstr "Datum"
#: members/models.py:89
msgid "Comments"
msgstr "Kommentare"
#: members/models.py:96
msgid "Memberlist"
msgstr "Teilnehmerliste"
#: members/models.py:97
msgid "Memberlists"
msgstr "Teilnehmerlisten"
#: members/models.py:104 members/models.py:109 members/models.py:139
#: members/models.py:143
msgid "Member"
msgstr "Teilnehmer"
#: members/models.py:106
msgid "Comment"
msgstr "Kommentar"
#: members/models.py:110 members/models.py:144
msgid "Members"
msgstr "Teilnehmer"
#: members/models.py:119
msgid "Date"
msgstr "Datum"
#: members/models.py:120
msgid "Location"
msgstr "Ort"
#: members/models.py:130
msgid "Jugendleiter"
msgstr "Jugendleiter"
#: members/models.py:133
msgid "Klettertreff"
msgstr "Klettertreff"
#: members/models.py:134
msgid "Klettertreffs"
msgstr "Klettertreffs"

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.10.2 on 2016-10-18 17:36 # Generated by Django 1.10.2 on 2016-11-19 14:56
from __future__ import unicode_literals from __future__ import unicode_literals
import datetime
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -18,18 +18,36 @@ class Migration(migrations.Migration):
name='Group', name='Group',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=20)), ('name', models.CharField(max_length=20, verbose_name='name')),
('min_age', models.IntegerField(default=5)), ('min_age', models.IntegerField(default=5, verbose_name='minimum age (years)')),
], ],
options={
'verbose_name_plural': 'groups',
'verbose_name': 'group',
},
), ),
migrations.CreateModel( migrations.CreateModel(
name='Member', name='Member',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('prename', models.CharField(max_length=20)), ('prename', models.CharField(max_length=20, verbose_name='prename')),
('lastname', models.CharField(max_length=20)), ('lastname', models.CharField(max_length=20, verbose_name='last name')),
('email', models.EmailField(default='', max_length=100)),
('birth_date', models.DateField(verbose_name='birth date')), ('birth_date', models.DateField(verbose_name='birth date')),
('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='members.Group')), ('group', models.ManyToManyField(to='members.Group')),
],
options={
'verbose_name_plural': 'members',
'verbose_name': 'member',
},
),
migrations.CreateModel(
name='MemberList',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(default='', max_length=50, verbose_name='List Name')),
('date', models.DateField(default=datetime.datetime.today)),
('add_member', models.ManyToManyField(to='members.Member')),
], ],
), ),
] ]

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.2 on 2016-10-18 17:41
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('members', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='member',
name='email',
field=models.CharField(default='', max_length=100),
),
]

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.2 on 2016-10-18 17:42
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('members', '0002_member_email'),
]
operations = [
migrations.AlterField(
model_name='member',
name='email',
field=models.EmailField(default='', max_length=100),
),
]

@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.2 on 2016-10-18 17:44
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('members', '0003_auto_20161018_1742'),
]
operations = [
migrations.RemoveField(
model_name='member',
name='group',
),
migrations.AddField(
model_name='member',
name='group',
field=models.ManyToManyField(to='members.Group'),
),
]

@ -1,9 +1,11 @@
from datetime import datetime from datetime import datetime
import uuid import uuid
from django import forms
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils import timezone from django.utils import timezone
from multiselectfield import MultiSelectField
class Group(models.Model): class Group(models.Model):
""" """
@ -36,7 +38,7 @@ class Member(models.Model):
phone_number_parents = models.CharField(max_length=12, verbose_name=_('parents phone number'), default='', blank=True) phone_number_parents = models.CharField(max_length=12, verbose_name=_('parents phone number'), default='', blank=True)
email = models.EmailField(max_length=100, default="") email = models.EmailField(max_length=100, default="")
birth_date = models.DateField(_('birth date')) # to determine the age birth_date = models.DateField(_('birth date')) # to determine the age
group = models.ManyToManyField(Group) group = models.ManyToManyField(Group, verbose_name=_('group'))
gets_newsletter = models.BooleanField(_('receives newsletter'), gets_newsletter = models.BooleanField(_('receives newsletter'),
default=True) default=True)
unsubscribe_key = models.CharField(max_length=32, default="") unsubscribe_key = models.CharField(max_length=32, default="")
@ -83,30 +85,48 @@ class Member(models.Model):
class MemberList(models.Model): class MemberList(models.Model):
"""Lets the user create a list of members in pdf format. """ """Lets the user create a list of members in pdf format. """
name = models.CharField(verbose_name='List Name', default='',
name = models.CharField(verbose_name='Activity', default='',
max_length=50) max_length=50)
place = models.CharField(verbose_name=_('Place'), default='', max_length=50)
destination = models.CharField(verbose_name=_('Destination (optional)'), default='', max_length=50, blank=True)
date = models.DateField(default=datetime.today) date = models.DateField(default=datetime.today)
comment = models.TextField(_('Comments'), default='') end = models.DateField(verbose_name=_('End (optional)'), blank=True, default=datetime.today)
#comment = models.TextField(_('Comments'), default='', blank=True)
groups = models.ManyToManyField(Group)
jugendleiter = models.ManyToManyField(Member)
tour_type_choices = (('Gemeinschaftstour','Gemeinschaftstour'), ('Führungstour', 'Führungstour'),
('Ausbildung', 'Ausbildung'))
tour_type = MultiSelectField(choices=tour_type_choices, default='', max_choices=1)
def __str__(self): def __str__(self):
"""String represenation""" """String represenation"""
return self.name return self.name
class Meta:
verbose_name = _('Memberlist')
verbose_name_plural = _('Memberlists')
class MemberOnList(models.Model): class MemberOnList(models.Model):
""" """
Connects members to a list of members. Connects members to a list of members.
""" """
member = models.ForeignKey(Member) member = models.ForeignKey(Member, verbose_name=_('Member'))
memberlist = models.ForeignKey(MemberList) memberlist = models.ForeignKey(MemberList)
comments = models.TextField(_('Comment'), default='') comments = models.TextField(_('Comment'), default='', blank=True)
class Meta:
verbose_name = _('Member')
verbose_name_plural = _('Members')
class Klettertreff(models.Model): class Klettertreff(models.Model):
""" This model represents a Klettertreff event. """ This model represents a Klettertreff event.
A Klettertreff can take a date, location, Jugendleiter, attending members as A Klettertreff can take a date, location, Jugendleiter, attending members
input. as input.
""" """
date = models.DateField(_('Date'), default=datetime.today) date = models.DateField(_('Date'), default=datetime.today)
location = models.CharField(_('Location'), default='', max_length=60) location = models.CharField(_('Location'), default='', max_length=60)
@ -117,8 +137,7 @@ class Klettertreff(models.Model):
return self.location + ' ' + self.date.strftime('%d.%m.%Y') return self.location + ' ' + self.date.strftime('%d.%m.%Y')
def get_jugendleiter(self): def get_jugendleiter(self):
jl_string = ''.join(j.name + ',\n' for j in self.jugendleiter.all()) jl_string = ', '.join(j.name for j in self.jugendleiter.all())
jl_string = jl_string[:-2]
return jl_string return jl_string
def has_attendee(self, member): def has_attendee(self, member):
@ -137,9 +156,16 @@ class Klettertreff(models.Model):
get_jugendleiter.short_description = _('Jugendleiter') get_jugendleiter.short_description = _('Jugendleiter')
class Meta:
verbose_name = _('Klettertreff')
verbose_name_plural = _('Klettertreffs')
class KlettertreffAttendee(models.Model): class KlettertreffAttendee(models.Model):
"""Connects members to Klettertreffs.""" """Connects members to Klettertreffs."""
member = models.ForeignKey(Member) member = models.ForeignKey(Member, verbose_name=_('Member'))
klettertreff = models.ForeignKey(Klettertreff) klettertreff = models.ForeignKey(Klettertreff)
class Meta:
verbose_name = _('Member')
verbose_name_plural = _('Members')

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-10-22 00:04+0200\n" "POT-Creation-Date: 2017-01-14 17:13+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,6 +18,6 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: templates/startpage/index.html:2 #: startpage/templates/startpage/index.html:2
msgid "Awesome JDAV website being able to do a lot!" msgid "Awesome JDAV website being able to do a lot!"
msgstr "Tolle JDAV Webseite die ganz viel kann!" msgstr "Tolle JDAV Webseite die ganz viel kann!"

@ -0,0 +1,120 @@
/* This is the extension of the base.css file. If you need to change anything in the base.css
* copy the extract inside this file and do your changes.
*
* To change the global colors, edit the colors.css file */
@import url('colors.css');
a:link, a:visited {
color: var(--link);
text-decoration: none;
}
a:focus, a:hover {
color: var(--link-hover);
}
#user-tools a {
border-bottom: 1px solid rgba(255, 255, 255, 0.25);
}
#user-tools a:focus, #user-tools a:hover {
text-decoration: none;
border-bottom-color: var(--link-hover);
color: var(--link-hover);
}
/* HEADER */
#header {
width: auto;
height: 40px;
padding: 10px 40px;
background: var(--primary);
line-height: 40px;
color: #ffc;
overflow: hidden;
}
#branding h1 {
padding: 0;
margin: 0 20px 0 0;
font-weight: 300;
font-size: 24px;
color: var(--text-light);
}
#branding h1, #branding h1 a:link, #branding h1 a:visited {
color: var(--text-light);
}
#logo {
float: left;
margin: 0 20px 0 0;
display: block;
width: 128px;
height: auto;
}
.module h2, .module caption, .inline-group h2 {
margin: 0;
padding: 8px;
font-weight: 400;
font-size: 13px;
text-align: left;
background: var(--primary-dark);
color: var(--text-light);
}
/* FORM BUTTONS */
.button, input[type=submit], input[type=button], .submit-row input, a.button {
background: var(--primary-dark);
padding: 10px 15px;
border: none;
border-radius: 4px;
color: var(--text-light);
cursor: pointer;
}
.button:active, input[type=submit]:active, input[type=button]:active,
.button:focus, input[type=submit]:focus, input[type=button]:focus,
.button:hover, input[type=submit]:hover, input[type=button]:hover {
background: var(--primary-dark);
}
.button.default, input[type=submit].default, .submit-row input.default {
float: right;
border: none;
font-weight: 400;
background: var(--primary-dark);
}
.button.default:active, input[type=submit].default:active,
.button.default:focus, input[type=submit].default:focus,
.button.default:hover, input[type=submit].default:hover {
background: var(--primary-dark);
}
/* BREADCRUMBS */
div.breadcrumbs {
background: var(--primary-dark);
padding: 10px 40px;
border: none;
font-size: 14px;
color: var(--link-dark);
text-align: left;
}
div.breadcrumbs a {
color: var(--text-light);
}
div.breadcrumbs a:focus, div.breadcrumbs a:hover {
color: var(--link-hover);
}
/* OBJECT TOOLS */
.object-tools a:focus, .object-tools a:hover {
background-color: var(--primary-dark);
}

@ -0,0 +1,971 @@
/*
DJANGO Admin styles
*/
@import url(fonts.css);
body {
margin: 0;
padding: 0;
font-size: 14px;
font-family: "Roboto","Lucida Grande","DejaVu Sans","Bitstream Vera Sans",Verdana,Arial,sans-serif;
color: #333;
background: #fff;
}
/* LINKS */
a:link, a:visited {
color: #447e9b;
text-decoration: none;
}
a:focus, a:hover {
color: #036;
}
a:focus {
text-decoration: underline;
}
a img {
border: none;
}
a.section:link, a.section:visited {
color: #fff;
text-decoration: none;
}
a.section:focus, a.section:hover {
text-decoration: underline;
}
/* GLOBAL DEFAULTS */
p, ol, ul, dl {
margin: .2em 0 .8em 0;
}
p {
padding: 0;
line-height: 140%;
}
h1,h2,h3,h4,h5 {
font-weight: bold;
}
h1 {
margin: 0 0 20px;
font-weight: 300;
font-size: 20px;
color: #666;
}
h2 {
font-size: 16px;
margin: 1em 0 .5em 0;
}
h2.subhead {
font-weight: normal;
margin-top: 0;
}
h3 {
font-size: 14px;
margin: .8em 0 .3em 0;
color: #666;
font-weight: bold;
}
h4 {
font-size: 12px;
margin: 1em 0 .8em 0;
padding-bottom: 3px;
}
h5 {
font-size: 10px;
margin: 1.5em 0 .5em 0;
color: #666;
text-transform: uppercase;
letter-spacing: 1px;
}
ul li {
list-style-type: square;
padding: 1px 0;
}
li ul {
margin-bottom: 0;
}
li, dt, dd {
font-size: 13px;
line-height: 20px;
}
dt {
font-weight: bold;
margin-top: 4px;
}
dd {
margin-left: 0;
}
form {
margin: 0;
padding: 0;
}
fieldset {
margin: 0;
padding: 0;
border: none;
border-top: 1px solid #eee;
}
blockquote {
font-size: 11px;
color: #777;
margin-left: 2px;
padding-left: 10px;
border-left: 5px solid #ddd;
}
code, pre {
font-family: "Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace;
color: #666;
font-size: 12px;
}
pre.literal-block {
margin: 10px;
background: #eee;
padding: 6px 8px;
}
code strong {
color: #930;
}
hr {
clear: both;
color: #eee;
background-color: #eee;
height: 1px;
border: none;
margin: 0;
padding: 0;
font-size: 1px;
line-height: 1px;
}
/* TEXT STYLES & MODIFIERS */
.small {
font-size: 11px;
}
.tiny {
font-size: 10px;
}
p.tiny {
margin-top: -2px;
}
.mini {
font-size: 10px;
}
p.mini {
margin-top: -3px;
}
.help, p.help, form p.help {
font-size: 11px;
color: #999;
}
.help-tooltip {
cursor: help;
}
p img, h1 img, h2 img, h3 img, h4 img, td img {
vertical-align: middle;
}
.quiet, a.quiet:link, a.quiet:visited {
color: #999;
font-weight: normal;
}
.float-right {
float: right;
}
.float-left {
float: left;
}
.clear {
clear: both;
}
.align-left {
text-align: left;
}
.align-right {
text-align: right;
}
.example {
margin: 10px 0;
padding: 5px 10px;
background: #efefef;
}
.nowrap {
white-space: nowrap;
}
/* TABLES */
table {
border-collapse: collapse;
border-color: #ccc;
}
td, th {
font-size: 13px;
line-height: 16px;
border-bottom: 1px solid #eee;
vertical-align: top;
padding: 8px;
font-family: "Roboto", "Lucida Grande", Verdana, Arial, sans-serif;
}
th {
font-weight: 600;
text-align: left;
}
thead th,
tfoot td {
color: #666;
padding: 5px 10px;
font-size: 11px;
background: #fff;
border: none;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
}
tfoot td {
border-bottom: none;
border-top: 1px solid #eee;
}
thead th.required {
color: #000;
}
tr.alt {
background: #f6f6f6;
}
.row1 {
background: #fff;
}
.row2 {
background: #f9f9f9;
}
/* SORTABLE TABLES */
thead th {
padding: 5px 10px;
line-height: normal;
text-transform: uppercase;
background: #f6f6f6;
}
thead th a:link, thead th a:visited {
color: #666;
}
thead th.sorted {
background: #eee;
}
thead th.sorted .text {
padding-right: 42px;
}
table thead th .text span {
padding: 8px 10px;
display: block;
}
table thead th .text a {
display: block;
cursor: pointer;
padding: 8px 10px;
}
table thead th .text a:focus, table thead th .text a:hover {
background: #eee;
}
thead th.sorted a.sortremove {
visibility: hidden;
}
table thead th.sorted:hover a.sortremove {
visibility: visible;
}
table thead th.sorted .sortoptions {
display: block;
padding: 9px 5px 0 5px;
float: right;
text-align: right;
}
table thead th.sorted .sortpriority {
font-size: .8em;
min-width: 12px;
text-align: center;
vertical-align: 3px;
margin-left: 2px;
margin-right: 2px;
}
table thead th.sorted .sortoptions a {
position: relative;
width: 14px;
height: 14px;
display: inline-block;
background: url(../img/sorting-icons.svg) 0 0 no-repeat;
background-size: 14px auto;
}
table thead th.sorted .sortoptions a.sortremove {
background-position: 0 0;
}
table thead th.sorted .sortoptions a.sortremove:after {
content: '\\';
position: absolute;
top: -6px;
left: 3px;
font-weight: 200;
font-size: 18px;
color: #999;
}
table thead th.sorted .sortoptions a.sortremove:focus:after,
table thead th.sorted .sortoptions a.sortremove:hover:after {
color: #447e9b;
}
table thead th.sorted .sortoptions a.sortremove:focus,
table thead th.sorted .sortoptions a.sortremove:hover {
background-position: 0 -14px;
}
table thead th.sorted .sortoptions a.ascending {
background-position: 0 -28px;
}
table thead th.sorted .sortoptions a.ascending:focus,
table thead th.sorted .sortoptions a.ascending:hover {
background-position: 0 -42px;
}
table thead th.sorted .sortoptions a.descending {
top: 1px;
background-position: 0 -56px;
}
table thead th.sorted .sortoptions a.descending:focus,
table thead th.sorted .sortoptions a.descending:hover {
background-position: 0 -70px;
}
/* FORM DEFAULTS */
input, textarea, select, .form-row p, form .button {
margin: 2px 0;
padding: 2px 3px;
vertical-align: middle;
font-family: "Roboto", "Lucida Grande", Verdana, Arial, sans-serif;
font-weight: normal;
font-size: 13px;
}
textarea {
vertical-align: top;
}
input[type=text], input[type=password], input[type=email], input[type=url],
input[type=number], textarea, select, .vTextField {
border: 1px solid #ccc;
border-radius: 4px;
padding: 5px 6px;
margin-top: 0;
}
input[type=text]:focus, input[type=password]:focus, input[type=email]:focus,
input[type=url]:focus, input[type=number]:focus, textarea:focus, select:focus,
.vTextField:focus {
border-color: #999;
}
select {
height: 30px;
}
select[multiple] {
min-height: 150px;
}
/* FORM BUTTONS */
.button, input[type=submit], input[type=button], .submit-row input, a.button {
background: #000;
padding: 10px 15px;
border: none;
border-radius: 4px;
color: #fff;
cursor: pointer;
}
a.button {
padding: 4px 5px;
}
.button:active, input[type=submit]:active, input[type=button]:active,
.button:focus, input[type=submit]:focus, input[type=button]:focus,
.button:hover, input[type=submit]:hover, input[type=button]:hover {
background: #609ab6;
}
.button[disabled], input[type=submit][disabled], input[type=button][disabled] {
opacity: 0.4;
}
.button.default, input[type=submit].default, .submit-row input.default {
float: right;
border: none;
font-weight: 400;
background: #417690;
}
.button.default:active, input[type=submit].default:active,
.button.default:focus, input[type=submit].default:focus,
.button.default:hover, input[type=submit].default:hover {
background: #205067;
}
.button[disabled].default,
input[type=submit][disabled].default,
input[type=button][disabled].default {
opacity: 0.4;
}
/* MODULES */
.module {
border: none;
margin-bottom: 30px;
background: #fff;
}
.module p, .module ul, .module h3, .module h4, .module dl, .module pre {
padding-left: 10px;
padding-right: 10px;
}
.module blockquote {
margin-left: 12px;
}
.module ul, .module ol {
margin-left: 1.5em;
}
.module h3 {
margin-top: .6em;
}
.module h2, .module caption, .inline-group h2 {
margin: 0;
padding: 8px;
font-weight: 400;
font-size: 13px;
text-align: left;
background: #000;
color: #fff;
}
.module caption,
.inline-group h2 {
font-size: 12px;
letter-spacing: 0.5px;
text-transform: uppercase;
}
.module table {
border-collapse: collapse;
}
/* MESSAGES & ERRORS */
ul.messagelist {
padding: 0;
margin: 0;
}
ul.messagelist li {
display: block;
font-weight: 400;
font-size: 13px;
padding: 10px 10px 10px 65px;
margin: 0 0 10px 0;
background: #dfd url(../img/icon-yes.svg) 40px 12px no-repeat;
background-size: 16px auto;
color: #333;
}
ul.messagelist li.warning {
background: #ffc url(../img/icon-alert.svg) 40px 14px no-repeat;
background-size: 14px auto;
}
ul.messagelist li.error {
background: #ffefef url(../img/icon-no.svg) 40px 12px no-repeat;
background-size: 16px auto;
}
.errornote {
font-size: 14px;
font-weight: 700;
display: block;
padding: 10px 12px;
margin: 0 0 10px 0;
color: #ba2121;
border: 1px solid #ba2121;
border-radius: 4px;
background-color: #fff;
background-position: 5px 12px;
}
ul.errorlist {
margin: 0 0 4px;
padding: 0;
color: #ba2121;
background: #fff;
}
ul.errorlist li {
font-size: 13px;
display: block;
margin-bottom: 4px;
}
ul.errorlist li:first-child {
margin-top: 0;
}
ul.errorlist li a {
color: inherit;
text-decoration: underline;
}
td ul.errorlist {
margin: 0;
padding: 0;
}
td ul.errorlist li {
margin: 0;
}
.form-row.errors {
margin: 0;
border: none;
border-bottom: 1px solid #eee;
background: none;
}
.form-row.errors ul.errorlist li {
padding-left: 0;
}
.errors input, .errors select, .errors textarea {
border: 1px solid #ba2121;
}
div.system-message {
background: #ffc;
margin: 10px;
padding: 6px 8px;
font-size: .8em;
}
div.system-message p.system-message-title {
padding: 4px 5px 4px 25px;
margin: 0;
color: #c11;
background: #ffefef url(../img/icon-no.svg) 5px 5px no-repeat;
}
.description {
font-size: 12px;
padding: 5px 0 0 12px;
}
/* BREADCRUMBS */
div.breadcrumbs {
background: #000;
padding: 10px 40px;
border: none;
font-size: 14px;
color: #c4dce8;
text-align: left;
}
div.breadcrumbs a {
color: #fff;
}
div.breadcrumbs a:focus, div.breadcrumbs a:hover {
color: #c4dce8;
}
/* ACTION ICONS */
.addlink {
padding-left: 16px;
background: url(../img/icon-addlink.svg) 0 1px no-repeat;
}
.changelink, .inlinechangelink {
padding-left: 16px;
background: url(../img/icon-changelink.svg) 0 1px no-repeat;
}
.deletelink {
padding-left: 16px;
background: url(../img/icon-deletelink.svg) 0 1px no-repeat;
}
a.deletelink:link, a.deletelink:visited {
color: #CC3434;
}
a.deletelink:focus, a.deletelink:hover {
color: #993333;
text-decoration: none;
}
/* OBJECT TOOLS */
.object-tools {
font-size: 10px;
font-weight: bold;
padding-left: 0;
float: right;
position: relative;
margin-top: -48px;
}
.form-row .object-tools {
margin-top: 5px;
margin-bottom: 5px;
float: none;
height: 2em;
padding-left: 3.5em;
}
.object-tools li {
display: block;
float: left;
margin-left: 5px;
height: 16px;
}
.object-tools a {
border-radius: 15px;
}
.object-tools a:link, .object-tools a:visited {
display: block;
float: left;
padding: 3px 12px;
background: #999;
font-weight: 400;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #fff;
}
.object-tools a:focus, .object-tools a:hover {
background-color: #417690;
}
.object-tools a:focus{
text-decoration: none;
}
.object-tools a.viewsitelink, .object-tools a.golink,.object-tools a.addlink {
background-repeat: no-repeat;
background-position: 93% center;
padding-right: 26px;
}
.object-tools a.viewsitelink, .object-tools a.golink {
background-image: url(../img/tooltag-arrowright.svg);
}
.object-tools a.addlink {
background-image: url(../img/tooltag-add.svg);
}
/* OBJECT HISTORY */
table#change-history {
width: 100%;
}
table#change-history tbody th {
width: 16em;
}
/* PAGE STRUCTURE */
#container {
position: relative;
width: 100%;
min-width: 980px;
padding: 0;
}
#content {
padding: 20px 40px;
}
.dashboard #content {
width: 600px;
}
#content-main {
float: left;
width: 100%;
}
#content-related {
float: right;
width: 260px;
position: relative;
margin-right: -300px;
}
#footer {
clear: both;
padding: 10px;
}
/* COLUMN TYPES */
.colMS {
margin-right: 300px;
}
.colSM {
margin-left: 300px;
}
.colSM #content-related {
float: left;
margin-right: 0;
margin-left: -300px;
}
.colSM #content-main {
float: right;
}
.popup .colM {
width: auto;
}
/* HEADER */
#header {
width: auto;
height: 40px;
padding: 10px 40px;
background: #417690;
line-height: 40px;
color: #ffc;
overflow: hidden;
}
#header a:link, #header a:visited {
color: #fff;
}
#header a:focus , #header a:hover {
text-decoration: underline;
}
#branding {
float: left;
}
#branding h1 {
padding: 0;
margin: 0 20px 0 0;
font-weight: 300;
font-size: 24px;
color: #f5dd5d;
}
#branding h1, #branding h1 a:link, #branding h1 a:visited {
color: #f5dd5d;
}
#branding h2 {
padding: 0 10px;
font-size: 14px;
margin: -8px 0 8px 0;
font-weight: normal;
color: #ffc;
}
#branding a:hover {
text-decoration: none;
}
#user-tools {
float: right;
padding: 0;
margin: 0 0 0 20px;
font-weight: 300;
font-size: 11px;
letter-spacing: 0.5px;
text-transform: uppercase;
text-align: right;
}
#user-tools a {
border-bottom: 1px solid rgba(255, 255, 255, 0.25);
}
#user-tools a:focus, #user-tools a:hover {
text-decoration: none;
border-bottom-color: #000;
color: #000;
}
/* SIDEBAR */
#content-related {
background: #f8f8f8;
}
#content-related .module {
background: none;
}
#content-related h3 {
font-size: 14px;
color: #666;
padding: 0 16px;
margin: 0 0 16px;
}
#content-related h4 {
font-size: 13px;
}
#content-related p {
padding-left: 16px;
padding-right: 16px;
}
#content-related .actionlist {
padding: 0;
margin: 16px;
}
#content-related .actionlist li {
line-height: 1.2;
margin-bottom: 10px;
padding-left: 18px;
}
#content-related .module h2 {
background: none;
padding: 16px;
margin-bottom: 16px;
border-bottom: 1px solid #eaeaea;
font-size: 18px;
color: #333;
}
.delete-confirmation form input[type="submit"] {
background: #ba2121;
border-radius: 4px;
padding: 10px 15px;
color: #fff;
}
.delete-confirmation form input[type="submit"]:active,
.delete-confirmation form input[type="submit"]:focus,
.delete-confirmation form input[type="submit"]:hover {
background: #a41515;
}
.delete-confirmation form .cancel-link {
display: inline-block;
vertical-align: middle;
height: 15px;
line-height: 15px;
background: #ddd;
border-radius: 4px;
padding: 10px 15px;
color: #333;
margin: 0 0 0 10px;
}
.delete-confirmation form .cancel-link:active,
.delete-confirmation form .cancel-link:focus,
.delete-confirmation form .cancel-link:hover {
background: #ccc;
}
/* POPUP */
.popup #content {
padding: 20px;
}
.popup #container {
min-width: 0;
}
.popup #header {
padding: 10px 20px;
}

@ -0,0 +1,351 @@
:root {
--primary: #5fad00;
--primary-dark: #6dad1e;
--link: #666;
--link-dark: #222;
--link-hover: #444;
--text-light: #fff;
}
/* CHANGELISTS */
#changelist {
position: relative;
width: 100%;
}
#changelist table {
width: 100%;
}
.change-list .hiddenfields { display:none; }
.change-list .filtered table {
border-right: none;
}
.change-list .filtered {
min-height: 400px;
}
.change-list .filtered .results, .change-list .filtered .paginator,
.filtered #toolbar, .filtered div.xfull {
margin-right: 280px;
width: auto;
}
.change-list .filtered table tbody th {
padding-right: 1em;
}
#changelist-form .results {
overflow-x: auto;
}
#changelist .toplinks {
border-bottom: 1px solid #ddd;
}
#changelist .paginator {
color: #666;
border-bottom: 1px solid #eee;
background: #fff;
overflow: hidden;
}
/* CHANGELIST TABLES */
#changelist table thead th {
padding: 0;
white-space: nowrap;
vertical-align: middle;
}
#changelist table thead th.action-checkbox-column {
width: 1.5em;
text-align: center;
}
#changelist table tbody td.action-checkbox {
text-align: center;
}
#changelist table tfoot {
color: #666;
}
/* TOOLBAR */
#changelist #toolbar {
padding: 8px 10px;
margin-bottom: 15px;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
background: #f8f8f8;
color: #666;
}
#changelist #toolbar form input {
border-radius: 4px;
font-size: 14px;
padding: 5px;
color: #333;
}
#changelist #toolbar form #searchbar {
height: 19px;
border: 1px solid #ccc;
padding: 2px 5px;
margin: 0;
vertical-align: top;
font-size: 13px;
}
#changelist #toolbar form #searchbar:focus {
border-color: #999;
}
#changelist #toolbar form input[type="submit"] {
border: 1px solid #ccc;
padding: 2px 10px;
margin: 0;
vertical-align: middle;
background: #fff;
box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset;
cursor: pointer;
color: #333;
}
#changelist #toolbar form input[type="submit"]:focus,
#changelist #toolbar form input[type="submit"]:hover {
border-color: #999;
}
#changelist #changelist-search img {
vertical-align: middle;
margin-right: 4px;
}
/* FILTER COLUMN */
#changelist-filter {
position: absolute;
top: 0;
right: 0;
z-index: 1000;
width: 240px;
background: #f8f8f8;
border-left: none;
margin: 0;
}
#changelist-filter h2 {
font-size: 14px;
text-transform: uppercase;
letter-spacing: 0.5px;
padding: 5px 15px;
margin-bottom: 12px;
border-bottom: none;
}
#changelist-filter h3 {
font-weight: 400;
font-size: 14px;
padding: 0 15px;
margin-bottom: 10px;
}
#changelist-filter ul {
margin: 5px 0;
padding: 0 15px 15px;
border-bottom: 1px solid #eaeaea;
}
#changelist-filter ul:last-child {
border-bottom: none;
padding-bottom: none;
}
#changelist-filter li {
list-style-type: none;
margin-left: 0;
padding-left: 0;
}
#changelist-filter a {
display: block;
color: #999;
}
#changelist-filter li.selected {
border-left: 5px solid #eaeaea;
padding-left: 10px;
margin-left: -15px;
}
#changelist-filter li.selected a {
color: #5b80b2;
}
#changelist-filter a:focus, #changelist-filter a:hover,
#changelist-filter li.selected a:focus,
#changelist-filter li.selected a:hover {
color: #036;
}
/* DATE DRILLDOWN */
.change-list ul.toplinks {
display: block;
float: left;
padding: 0;
margin: 0;
width: 100%;
}
.change-list ul.toplinks li {
padding: 3px 6px;
font-weight: bold;
list-style-type: none;
display: inline-block;
}
.change-list ul.toplinks .date-back a {
color: #999;
}
.change-list ul.toplinks .date-back a:focus,
.change-list ul.toplinks .date-back a:hover {
color: #036;
}
/* PAGINATOR */
.paginator {
font-size: 13px;
padding-top: 10px;
padding-bottom: 10px;
line-height: 22px;
margin: 0;
border-top: 1px solid #ddd;
}
.paginator a:link, .paginator a:visited {
padding: 2px 6px;
background: #000;
text-decoration: none;
color: #fff;
}
.paginator a.showall {
padding: 0;
border: none;
background: none;
color: #5b80b2;
}
.paginator a.showall:focus, .paginator a.showall:hover {
background: none;
color: #036;
}
.paginator .end {
margin-right: 6px;
}
.paginator .this-page {
padding: 2px 6px;
font-weight: bold;
font-size: 13px;
vertical-align: top;
}
.paginator a:focus, .paginator a:hover {
color: white;
background: #036;
}
/* ACTIONS */
.filtered .actions {
margin-right: 280px;
border-right: none;
}
#changelist table input {
margin: 0;
vertical-align: baseline;
}
#changelist table tbody tr.selected {
background-color: #FFFFCC;
}
#changelist .actions {
padding: 10px;
background: #fff;
border-top: none;
border-bottom: none;
line-height: 24px;
color: #999;
}
#changelist .actions.selected {
background: #fffccf;
border-top: 1px solid #fffee8;
border-bottom: 1px solid #edecd6;
}
#changelist .actions span.all,
#changelist .actions span.action-counter,
#changelist .actions span.clear,
#changelist .actions span.question {
font-size: 13px;
margin: 0 0.5em;
display: none;
}
#changelist .actions:last-child {
border-bottom: none;
}
#changelist .actions select {
vertical-align: top;
height: 24px;
background: none;
color: #000;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 14px;
padding: 0 0 0 4px;
margin: 0;
margin-left: 10px;
}
#changelist .actions select:focus {
border-color: #999;
}
#changelist .actions label {
display: inline-block;
vertical-align: middle;
font-size: 13px;
}
#changelist .actions .button {
font-size: 13px;
border: 1px solid #ccc;
border-radius: 4px;
background: #fff;
box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset;
cursor: pointer;
height: 24px;
line-height: 1;
padding: 4px 8px;
margin: 0;
color: #333;
}
#changelist .actions .button:focus, #changelist .actions .button:hover {
border-color: #999;
}

@ -0,0 +1,9 @@
/* Admin color file. Keep the number of different colors low to increase readability of the webpage */
:root {
--primary: #5fad00;
--primary-dark: #6dad1e;
--link: #666;
--link-dark: #222;
--link-hover: #444;
--text-light: #fff;
}

@ -0,0 +1,566 @@
@import('colors.css');
/* SELECTOR (FILTER INTERFACE) */
.selector {
width: 800px;
float: left;
}
.selector select {
width: 380px;
height: 17.2em;
}
.selector-available, .selector-chosen {
float: left;
width: 380px;
text-align: center;
margin-bottom: 5px;
}
.selector-chosen select {
border-top: none;
}
.selector-available h2, .selector-chosen h2 {
border: 1px solid #ccc;
border-radius: 4px 4px 0 0;
}
.selector-chosen h2 {
background: var(--primary-dark);
color: #fff;
}
.selector .selector-available h2 {
background: #f8f8f8;
color: #666;
}
.selector .selector-filter {
background: white;
border: 1px solid #ccc;
border-width: 0 1px;
padding: 8px;
color: #999;
font-size: 10px;
margin: 0;
text-align: left;
}
.selector .selector-filter label,
.inline-group .aligned .selector .selector-filter label {
float: left;
margin: 7px 0 0;
width: 18px;
height: 18px;
padding: 0;
overflow: hidden;
line-height: 1;
}
.selector .selector-available input {
width: 320px;
margin-left: 8px;
}
.selector ul.selector-chooser {
float: left;
width: 22px;
background-color: #eee;
border-radius: 10px;
margin: 10em 5px 0 5px;
padding: 0;
}
.selector-chooser li {
margin: 0;
padding: 3px;
list-style-type: none;
}
.selector select {
padding: 0 10px;
margin: 0 0 10px;
border-radius: 0 0 4px 4px;
}
.selector-add, .selector-remove {
width: 16px;
height: 16px;
display: block;
text-indent: -3000px;
overflow: hidden;
cursor: default;
opacity: 0.3;
}
.active.selector-add, .active.selector-remove {
opacity: 1;
}
.active.selector-add:hover, .active.selector-remove:hover {
cursor: pointer;
}
.selector-add {
background: url(../img/selector-icons.svg) 0 -96px no-repeat;
}
.active.selector-add:focus, .active.selector-add:hover {
background-position: 0 -112px;
}
.selector-remove {
background: url(../img/selector-icons.svg) 0 -64px no-repeat;
}
.active.selector-remove:focus, .active.selector-remove:hover {
background-position: 0 -80px;
}
a.selector-chooseall, a.selector-clearall {
display: inline-block;
height: 16px;
text-align: left;
margin: 1px auto 3px;
overflow: hidden;
font-weight: bold;
line-height: 16px;
color: #666;
text-decoration: none;
opacity: 0.3;
}
a.active.selector-chooseall:focus, a.active.selector-clearall:focus,
a.active.selector-chooseall:hover, a.active.selector-clearall:hover {
color: #447e9b;
}
a.active.selector-chooseall, a.active.selector-clearall {
opacity: 1;
}
a.active.selector-chooseall:hover, a.active.selector-clearall:hover {
cursor: pointer;
}
a.selector-chooseall {
padding: 0 18px 0 0;
background: url(../img/selector-icons.svg) right -160px no-repeat;
cursor: default;
}
a.active.selector-chooseall:focus, a.active.selector-chooseall:hover {
background-position: 100% -176px;
}
a.selector-clearall {
padding: 0 0 0 18px;
background: url(../img/selector-icons.svg) 0 -128px no-repeat;
cursor: default;
}
a.active.selector-clearall:focus, a.active.selector-clearall:hover {
background-position: 0 -144px;
}
/* STACKED SELECTORS */
.stacked {
float: left;
width: 490px;
}
.stacked select {
width: 480px;
height: 10.1em;
}
.stacked .selector-available, .stacked .selector-chosen {
width: 480px;
}
.stacked .selector-available {
margin-bottom: 0;
}
.stacked .selector-available input {
width: 422px;
}
.stacked ul.selector-chooser {
height: 22px;
width: 50px;
margin: 0 0 10px 40%;
background-color: #eee;
border-radius: 10px;
}
.stacked .selector-chooser li {
float: left;
padding: 3px 3px 3px 5px;
}
.stacked .selector-chooseall, .stacked .selector-clearall {
display: none;
}
.stacked .selector-add {
background: url(../img/selector-icons.svg) 0 -32px no-repeat;
cursor: default;
}
.stacked .active.selector-add {
background-position: 0 -48px;
cursor: pointer;
}
.stacked .selector-remove {
background: url(../img/selector-icons.svg) 0 0 no-repeat;
cursor: default;
}
.stacked .active.selector-remove {
background-position: 0 -16px;
cursor: pointer;
}
.selector .help-icon {
background: url(../img/icon-unknown.svg) 0 0 no-repeat;
display: inline-block;
vertical-align: middle;
margin: -2px 0 0 2px;
width: 13px;
height: 13px;
}
.selector .selector-chosen .help-icon {
background: url(../img/icon-unknown-alt.svg) 0 0 no-repeat;
}
.selector .search-label-icon {
background: url(../img/search.svg) 0 0 no-repeat;
display: inline-block;
height: 18px;
width: 18px;
}
/* DATE AND TIME */
p.datetime {
line-height: 20px;
margin: 0;
padding: 0;
color: #666;
font-weight: bold;
}
.datetime span {
white-space: nowrap;
font-weight: normal;
font-size: 11px;
color: #ccc;
}
.datetime input, .form-row .datetime input.vDateField, .form-row .datetime input.vTimeField {
min-width: 0;
margin-left: 5px;
margin-bottom: 4px;
}
table p.datetime {
font-size: 11px;
margin-left: 0;
padding-left: 0;
}
.datetimeshortcuts .clock-icon, .datetimeshortcuts .date-icon {
position: relative;
display: inline-block;
vertical-align: middle;
height: 16px;
width: 16px;
overflow: hidden;
}
.datetimeshortcuts .clock-icon {
background: url(../img/icon-clock.svg) 0 0 no-repeat;
}
.datetimeshortcuts a:focus .clock-icon,
.datetimeshortcuts a:hover .clock-icon {
background-position: 0 -16px;
}
.datetimeshortcuts .date-icon {
background: url(../img/icon-calendar.svg) 0 0 no-repeat;
top: -1px;
}
.datetimeshortcuts a:focus .date-icon,
.datetimeshortcuts a:hover .date-icon {
background-position: 0 -16px;
}
.timezonewarning {
font-size: 11px;
color: #999;
}
/* URL */
p.url {
line-height: 20px;
margin: 0;
padding: 0;
color: #666;
font-size: 11px;
font-weight: bold;
}
.url a {
font-weight: normal;
}
/* FILE UPLOADS */
p.file-upload {
line-height: 20px;
margin: 0;
padding: 0;
color: #666;
font-size: 11px;
font-weight: bold;
}
.aligned p.file-upload {
margin-left: 170px;
}
.file-upload a {
font-weight: normal;
}
.file-upload .deletelink {
margin-left: 5px;
}
span.clearable-file-input label {
color: #333;
font-size: 11px;
display: inline;
float: none;
}
/* CALENDARS & CLOCKS */
.calendarbox, .clockbox {
margin: 5px auto;
font-size: 12px;
width: 19em;
text-align: center;
background: white;
border: 1px solid #ddd;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
overflow: hidden;
position: relative;
}
.clockbox {
width: auto;
}
.calendar {
margin: 0;
padding: 0;
}
.calendar table {
margin: 0;
padding: 0;
border-collapse: collapse;
background: white;
width: 100%;
}
.calendar caption, .calendarbox h2 {
margin: 0;
text-align: center;
border-top: none;
background: #f5dd5d;
font-weight: 700;
font-size: 12px;
color: #333;
}
.calendar th {
padding: 8px 5px;
background: #f8f8f8;
border-bottom: 1px solid #ddd;
font-weight: 400;
font-size: 12px;
text-align: center;
color: #666;
}
.calendar td {
font-weight: 400;
font-size: 12px;
text-align: center;
padding: 0;
border-top: 1px solid #eee;
border-bottom: none;
}
.calendar td.selected a {
background: #000;
color: #fff;
}
.calendar td.nonday {
background: #f8f8f8;
}
.calendar td.today a {
font-weight: 700;
}
.calendar td a, .timelist a {
display: block;
font-weight: 400;
padding: 6px;
text-decoration: none;
color: #444;
}
.calendar td a:focus, .timelist a:focus,
.calendar td a:hover, .timelist a:hover {
background: #000;
color: white;
}
.calendar td a:active, .timelist a:active {
background: #417690;
color: white;
}
.calendarnav {
font-size: 10px;
text-align: center;
color: #ccc;
margin: 0;
padding: 1px 3px;
}
.calendarnav a:link, #calendarnav a:visited,
#calendarnav a:focus, #calendarnav a:hover {
color: #999;
}
.calendar-shortcuts {
background: white;
font-size: 11px;
line-height: 11px;
border-top: 1px solid #eee;
padding: 8px 0;
color: #ccc;
}
.calendarbox .calendarnav-previous, .calendarbox .calendarnav-next {
display: block;
position: absolute;
top: 8px;
width: 15px;
height: 15px;
text-indent: -9999px;
padding: 0;
}
.calendarnav-previous {
left: 10px;
background: url(../img/calendar-icons.svg) 0 0 no-repeat;
}
.calendarbox .calendarnav-previous:focus,
.calendarbox .calendarnav-previous:hover {
background-position: 0 -15px;
}
.calendarnav-next {
right: 10px;
background: url(../img/calendar-icons.svg) 0 -30px no-repeat;
}
.calendarbox .calendarnav-next:focus,
.calendarbox .calendarnav-next:hover {
background-position: 0 -45px;
}
.calendar-cancel {
margin: 0;
padding: 4px 0;
font-size: 12px;
background: #eee;
border-top: 1px solid #ddd;
color: #333;
}
.calendar-cancel:focus, .calendar-cancel:hover {
background: #ddd;
}
.calendar-cancel a {
color: black;
display: block;
}
ul.timelist, .timelist li {
list-style-type: none;
margin: 0;
padding: 0;
}
.timelist a {
padding: 2px;
}
/* EDIT INLINE */
.inline-deletelink {
float: right;
text-indent: -9999px;
background: url(../img/inline-delete.svg) 0 0 no-repeat;
width: 16px;
height: 16px;
border: 0px none;
}
.inline-deletelink:focus, .inline-deletelink:hover {
cursor: pointer;
}
/* RELATED WIDGET WRAPPER */
.related-widget-wrapper {
float: left; /* display properly in form rows with multiple fields */
overflow: hidden; /* clear floated contents */
}
.related-widget-wrapper-link {
opacity: 0.3;
}
.related-widget-wrapper-link:link {
opacity: .8;
}
.related-widget-wrapper-link:link:focus,
.related-widget-wrapper-link:link:hover {
opacity: 1;
}
select + .related-widget-wrapper-link,
.related-widget-wrapper-link + .related-widget-wrapper-link {
margin-left: 7px;
}

@ -0,0 +1,90 @@
{% load i18n static %}<!DOCTYPE html>
{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
<html lang="{{ LANGUAGE_CODE|default:"en-us" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}>
<head>
<title>{% block title %}{% endblock %}</title>
<link rel="stylesheet" type="text/css" href="{% block stylesheet %}{% static "admin/css/base.css" %}{% endblock %}" />
<link rel="stylesheet" type="text/css" href="{% static "admin/css/advanced.css" %}" />
{% block extrastyle %}{% endblock %}
{% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% block stylesheet_rtl %}{% static "admin/css/rtl.css" %}{% endblock %}" />{% endif %}
{% block extrahead %}{% endblock %}
{% block blockbots %}<meta name="robots" content="NONE,NOARCHIVE" />{% endblock %}
</head>
{% load i18n %}
<body class="{% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}"
data-admin-utc-offset="{% now "Z" %}">
<!-- Container -->
<div id="container">
{% if not is_popup %}
<!-- Header -->
<div id="header">
<img id="logo" src="{% static "admin/images/jdav_logo_small.png" %}">
<div id="branding">
{% block branding %}{% endblock %}
</div>
{% block usertools %}
{% if has_permission %}
<div id="user-tools">
{% block welcome-msg %}
{% trans 'Welcome,' %}
<strong>{% firstof user.get_short_name user.get_username %}</strong>.
{% endblock %}
{% block userlinks %}
{% if site_url %}
<a href="{{ site_url }}">{% trans 'View site' %}</a> /
{% endif %}
{% if user.is_active and user.is_staff %}
{% url 'django-admindocs-docroot' as docsroot %}
{% if docsroot %}
<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> /
{% endif %}
{% endif %}
{% if user.has_usable_password %}
<a href="{% url 'admin:password_change' %}">{% trans 'Change password' %}</a> /
{% endif %}
<a href="{% url 'admin:logout' %}">{% trans 'Log out' %}</a>
{% endblock %}
</div>
{% endif %}
{% endblock %}
{% block nav-global %}{% endblock %}
</div>
<!-- END Header -->
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
{% if title %} &rsaquo; {{ title }}{% endif %}
</div>
{% endblock %}
{% endif %}
{% block messages %}
{% if messages %}
<ul class="messagelist">{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message|capfirst }}</li>
{% endfor %}</ul>
{% endif %}
{% endblock messages %}
<!-- Content -->
<div id="content" class="{% block coltype %}colM{% endblock %}">
{% block pretitle %}{% endblock %}
{% block content_title %}{% if title %}<h1>{{ title }}</h1>{% endif %}{% endblock %}
{% block content %}
{% block object-tools %}{% endblock %}
{{ content }}
{% endblock %}
{% block sidebar %}{% endblock %}
<br class="clear" />
</div>
<!-- END Content -->
{% block footer %}<div id="footer"></div>{% endblock %}
</div>
<!-- END Container -->
</body>
</html>

@ -0,0 +1,12 @@
{% extends "admin/base.html" %}
{% load i18n %}
{% block title %}
{{ title }} | {% trans "JDAV LB Administration" %}
{% endblock %}
{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">{% trans "JDAV LB Administration" %}</a></h1>
{% endblock %}
{% block nav-global %}{% endblock %}
Loading…
Cancel
Save