Compare commits
14 Commits
2138f6da24
...
6d5694a153
| Author | SHA1 | Date | |
|---|---|---|---|
| 6d5694a153 | |||
| c897b5be08 | |||
| fcf63d8bf9 | |||
| f0d0c44fd6 | |||
| f999a98d7c | |||
| 9b05663cf5 | |||
| 532b834629 | |||
| 57b3e2144e | |||
| 5cac8f8275 | |||
| dd401538d3 | |||
| 08a0f43fff | |||
| 9663df15d3 | |||
| bf13b1ebd2 | |||
| e80094f1bf |
@@ -1,6 +0,0 @@
|
|||||||
DEBUG=FALSE
|
|
||||||
HOST_NAME=uat1.2020.fet.at
|
|
||||||
SECRET_KEY=235t3groing43qo4ntgigon43qgi3q4gong
|
|
||||||
MYSQL_USER=user
|
|
||||||
MYSQL_PASSWORD=hgu
|
|
||||||
MYSQL_PORT=3306
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"folders": [
|
|
||||||
|
|
||||||
{
|
|
||||||
"path": "fet2020"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"settings": {
|
|
||||||
"launch": {},
|
|
||||||
"python.linting.flake8Enabled": true,
|
|
||||||
"python.linting.banditEnabled": true,
|
|
||||||
"python.testing.pytestEnabled": true,
|
|
||||||
"python.linting.flake8Args": [
|
|
||||||
"--max-line-length=100"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "Python: Django",
|
|
||||||
"type": "python",
|
|
||||||
"request": "launch",
|
|
||||||
"program": "manage.py",
|
|
||||||
"console": "integratedTerminal",
|
|
||||||
"args": [
|
|
||||||
"runserver",
|
|
||||||
"0.0.0.0:8000"
|
|
||||||
],
|
|
||||||
"django": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"python.testing.pytestArgs": [
|
|
||||||
"tests"
|
|
||||||
],
|
|
||||||
"python.testing.unittestEnabled": false,
|
|
||||||
"python.testing.nosetestsEnabled": false,
|
|
||||||
"python.testing.pytestEnabled": true,
|
|
||||||
"python.pythonPath": "/usr/local/bin/python3",
|
|
||||||
"python.linting.pylintEnabled": true,
|
|
||||||
"python.linting.flake8Enabled": true,
|
|
||||||
"python.linting.banditEnabled": true,
|
|
||||||
"python.linting.enabled": true
|
|
||||||
}
|
|
||||||
@@ -4,116 +4,11 @@ from ldap3 import HASHED_SALTED_SHA, MODIFY_REPLACE, Connection, Server
|
|||||||
from ldap3.core.exceptions import LDAPBindError
|
from ldap3.core.exceptions import LDAPBindError
|
||||||
from ldap3.utils.hashed import hashed
|
from ldap3.utils.hashed import hashed
|
||||||
|
|
||||||
from members.models import Member
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
host = "ldap://juri.fet.htu.tuwien.ac.at"
|
host = "ldap://juri.fet.htu.tuwien.ac.at"
|
||||||
port = 389
|
port = 389
|
||||||
|
|
||||||
|
|
||||||
def authentication(username, password):
|
|
||||||
# no empty passwords
|
|
||||||
if password is None or password.strip() == "":
|
|
||||||
return None
|
|
||||||
|
|
||||||
server = Server(host, port=port)
|
|
||||||
userdn = f"uid={username},ou=user,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at"
|
|
||||||
|
|
||||||
firstname = ""
|
|
||||||
surname = ""
|
|
||||||
mail = ""
|
|
||||||
|
|
||||||
try:
|
|
||||||
c = Connection(server, user=userdn, password=password, auto_bind=True)
|
|
||||||
|
|
||||||
if not c.extend.standard.who_am_i():
|
|
||||||
logger.info("Username '%s' is not in the list.", username)
|
|
||||||
return None
|
|
||||||
|
|
||||||
# get member infos from ldap
|
|
||||||
c.search(
|
|
||||||
search_base=userdn,
|
|
||||||
search_filter=f"(uid={username})",
|
|
||||||
search_scope="SUBTREE",
|
|
||||||
attributes=["givenName", "sn", "mail"],
|
|
||||||
)
|
|
||||||
|
|
||||||
firstname = c.response[0]["attributes"]["givenName"][0]
|
|
||||||
surname = c.response[0]["attributes"]["sn"][0]
|
|
||||||
mail = c.response[0]["attributes"]["mail"][0]
|
|
||||||
|
|
||||||
except LDAPBindError as e:
|
|
||||||
logger.info("LDAP Bind error from username '%s'. Error: %s", username, e)
|
|
||||||
return None
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.info("Auth exception from username '%s'. Error: %s", username, e)
|
|
||||||
return None
|
|
||||||
|
|
||||||
# get member or if not then create a new member
|
|
||||||
try:
|
|
||||||
member = Member.objects.get(mailaccount=mail)
|
|
||||||
|
|
||||||
# set username if not equal
|
|
||||||
if member.username != username:
|
|
||||||
member.username = username
|
|
||||||
logger.info("User '%s' saved.", username)
|
|
||||||
member.save()
|
|
||||||
|
|
||||||
except Member.DoesNotExist:
|
|
||||||
member = Member()
|
|
||||||
member.firstname = firstname
|
|
||||||
member.surname = surname
|
|
||||||
member.username = username
|
|
||||||
member.nickname = username
|
|
||||||
member.mailaccount = mail
|
|
||||||
logger.info("Member '%s' created.", username)
|
|
||||||
member.save()
|
|
||||||
|
|
||||||
logger.info("User '%s' logged in.", username)
|
|
||||||
return username
|
|
||||||
|
|
||||||
|
|
||||||
def get_finance_perm(username, password):
|
|
||||||
# no empty passwords
|
|
||||||
if password is None or password.strip() == "":
|
|
||||||
return None
|
|
||||||
|
|
||||||
server = Server(host, port=port)
|
|
||||||
userdn = f"uid={username},ou=user,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at"
|
|
||||||
|
|
||||||
finance_perm = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
c = Connection(server, user=userdn, password=password, auto_bind=True)
|
|
||||||
|
|
||||||
if not c.extend.standard.who_am_i():
|
|
||||||
logger.info("Username '%s' is not in the list.", username)
|
|
||||||
return None
|
|
||||||
|
|
||||||
# check if member has finance permission
|
|
||||||
c.search(
|
|
||||||
search_base="CN=finance,OU=Groups,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at",
|
|
||||||
search_filter="(objectClass=posixGroup)",
|
|
||||||
search_scope="SUBTREE",
|
|
||||||
attributes=["memberUid"],
|
|
||||||
)
|
|
||||||
|
|
||||||
if username in c.response[0]["attributes"]["memberUid"]:
|
|
||||||
logger.info("User '%s' has finance permission.", username)
|
|
||||||
finance_perm = True
|
|
||||||
|
|
||||||
except LDAPBindError as e:
|
|
||||||
logger.info("LDAP Bind error from username '%s'. Error: %s", username, e)
|
|
||||||
return None
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.info("Auth exception from username '%s'. Error: %s", username, e)
|
|
||||||
return None
|
|
||||||
|
|
||||||
return finance_perm
|
|
||||||
|
|
||||||
|
|
||||||
def change_password(username, old_password, new_password):
|
def change_password(username, old_password, new_password):
|
||||||
server = Server(host, port=port, use_ssl=True)
|
server = Server(host, port=port, use_ssl=True)
|
||||||
userdn = f"uid={username},ou=user,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at"
|
userdn = f"uid={username},ou=user,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at"
|
||||||
|
|||||||
161
fet2020/authentications/backends.py
Normal file
161
fet2020/authentications/backends.py
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from django.contrib.auth.backends import ModelBackend
|
||||||
|
from django.contrib.auth.models import Group, User
|
||||||
|
from ldap3 import Connection, Server
|
||||||
|
from ldap3.core.exceptions import LDAPBindError
|
||||||
|
|
||||||
|
from members.models import Member
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
host = "ldap://juri.fet.htu.tuwien.ac.at"
|
||||||
|
port = 389
|
||||||
|
|
||||||
|
|
||||||
|
class LdapBackend(ModelBackend):
|
||||||
|
def _check_ldap_user(self, username: str, password: str) -> bool:
|
||||||
|
server = Server(host, port=port)
|
||||||
|
userdn = f"uid={username},ou=user,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at"
|
||||||
|
|
||||||
|
try:
|
||||||
|
c = Connection(server, user=userdn, password=password, auto_bind=True)
|
||||||
|
|
||||||
|
if not c.extend.standard.who_am_i():
|
||||||
|
logger.info("Username '%s' is not in the list.", username)
|
||||||
|
return False
|
||||||
|
except LDAPBindError as e:
|
||||||
|
logger.info("LDAP Bind error from username '%s'. Error: %s", username, e)
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
logger.info("Auth exception from username '%s'. Error: %s", username, e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _check_fet_member(self, username: str, password: str) -> bool:
|
||||||
|
server = Server(host, port=port)
|
||||||
|
userdn = f"uid={username},ou=user,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at"
|
||||||
|
|
||||||
|
try:
|
||||||
|
c = Connection(server, user=userdn, password=password, auto_bind=True)
|
||||||
|
|
||||||
|
# Get member infos from ldap
|
||||||
|
c.search(
|
||||||
|
search_base=userdn,
|
||||||
|
search_filter=f"(uid={username})",
|
||||||
|
search_scope="SUBTREE",
|
||||||
|
attributes=["givenName", "sn", "mail"],
|
||||||
|
)
|
||||||
|
|
||||||
|
firstname = c.response[0]["attributes"]["givenName"][0]
|
||||||
|
surname = c.response[0]["attributes"]["sn"][0]
|
||||||
|
mail = c.response[0]["attributes"]["mail"][0]
|
||||||
|
except LDAPBindError as e:
|
||||||
|
logger.info("LDAP Bind error from username '%s'. Error: %s", username, e)
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
logger.info("Auth exception from username '%s'. Error: %s", username, e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Try to get the member. If it not exists, then create a new member.
|
||||||
|
try:
|
||||||
|
member = Member.objects.get(mailaccount=mail)
|
||||||
|
|
||||||
|
# Set username if not equal
|
||||||
|
if member.username != username:
|
||||||
|
member.username = username
|
||||||
|
logger.info("Member '%s' saved.", username)
|
||||||
|
member.save()
|
||||||
|
except Member.DoesNotExist:
|
||||||
|
member = Member()
|
||||||
|
member.firstname = firstname
|
||||||
|
member.surname = surname
|
||||||
|
member.username = username
|
||||||
|
member.nickname = username
|
||||||
|
member.mailaccount = mail
|
||||||
|
logger.info("Member '%s' created.", username)
|
||||||
|
member.save()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _check_finance_perm(self, username: str, password: str) -> bool:
|
||||||
|
server = Server(host, port=port)
|
||||||
|
userdn = f"uid={username},ou=user,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at"
|
||||||
|
|
||||||
|
try:
|
||||||
|
c = Connection(server, user=userdn, password=password, auto_bind=True)
|
||||||
|
|
||||||
|
# Check if member has finance permission
|
||||||
|
c.search(
|
||||||
|
search_base="CN=finance,OU=Groups,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at",
|
||||||
|
search_filter="(objectClass=posixGroup)",
|
||||||
|
search_scope="SUBTREE",
|
||||||
|
attributes=["memberUid"],
|
||||||
|
)
|
||||||
|
|
||||||
|
if username in c.response[0]["attributes"]["memberUid"]:
|
||||||
|
logger.info("User '%s' has finance permission.", username)
|
||||||
|
return True
|
||||||
|
|
||||||
|
except LDAPBindError as e:
|
||||||
|
logger.info("LDAP Bind error from username '%s'. Error: %s", username, e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.info("Auth exception from username '%s'. Error: %s", username, e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def authenticate(self, request, username=None, password=None):
|
||||||
|
if username is None or password is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Set username to lower because fet2020 can only work with lowercase letters.
|
||||||
|
username = username.lower()
|
||||||
|
|
||||||
|
if not self._check_ldap_user(username, password):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self._check_fet_member(username, password):
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = User.objects.get(username)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
user = User.objects.create_user(username)
|
||||||
|
|
||||||
|
if not self.user_can_authenticate(user):
|
||||||
|
logger.info("User '%s' is inactive.", user.get_username())
|
||||||
|
return
|
||||||
|
|
||||||
|
# Add user to all groups
|
||||||
|
for elem in Group.objects.all():
|
||||||
|
elem.user_set.add(user)
|
||||||
|
|
||||||
|
# Delete finance group if no permission
|
||||||
|
if not self._check_finance_perm(username, password):
|
||||||
|
finance_group = Group.objects.get(name="finance")
|
||||||
|
finance_group.user_set.remove(user)
|
||||||
|
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
class DebugBackend(ModelBackend):
|
||||||
|
def authenticate(self, request, username, password, **kwargs):
|
||||||
|
if (user := super().authenticate(request, username, password, **kwargs)) is None:
|
||||||
|
logger.info("User '%s' can't login. The user may not be a superuser.", username)
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Try to get the member. If it not exists, then create a new member.
|
||||||
|
try:
|
||||||
|
member = Member.objects.get(mailaccount=user.email)
|
||||||
|
except Member.DoesNotExist:
|
||||||
|
member = Member()
|
||||||
|
member.username = username
|
||||||
|
member.nickname = username
|
||||||
|
member.mailaccount = user.email
|
||||||
|
logger.info("Member '%s' created.", username)
|
||||||
|
member.save()
|
||||||
|
|
||||||
|
return user
|
||||||
@@ -1,54 +1,16 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm
|
from django.contrib.auth.forms import PasswordChangeForm
|
||||||
from django.contrib.auth.models import Group, User
|
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
|
|
||||||
from .authentications import authentication, change_password, get_finance_perm
|
from .authentications import change_password
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class LoginForm(AuthenticationForm):
|
# TODO: fix me
|
||||||
def clean(self):
|
|
||||||
username = self.cleaned_data.get("username").lower()
|
|
||||||
password = self.cleaned_data.get("password")
|
|
||||||
|
|
||||||
if username is not None and password:
|
|
||||||
if (auth_user := authentication(username, password)) is None:
|
|
||||||
raise ValidationError(
|
|
||||||
"Bitte Benutzername und Passwort korrekt eingeben.",
|
|
||||||
code="invalid_login",
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.user_cache = User.objects.get(username=auth_user.lower())
|
|
||||||
except User.DoesNotExist:
|
|
||||||
self.user_cache = User.objects.create_user(auth_user.lower())
|
|
||||||
finally:
|
|
||||||
self.confirm_login_allowed(self.user_cache)
|
|
||||||
|
|
||||||
# add user to all groups
|
|
||||||
for elem in Group.objects.all():
|
|
||||||
elem.user_set.add(self.user_cache)
|
|
||||||
|
|
||||||
# delete finance group if no permission
|
|
||||||
if not get_finance_perm(username, password):
|
|
||||||
finance_group = Group.objects.get(name="finance")
|
|
||||||
finance_group.user_set.remove(self.user_cache)
|
|
||||||
|
|
||||||
return self.cleaned_data
|
|
||||||
|
|
||||||
|
|
||||||
class LdapPasswordChangeForm(PasswordChangeForm):
|
class LdapPasswordChangeForm(PasswordChangeForm):
|
||||||
def clean_old_password(self):
|
def clean_old_password(self):
|
||||||
old_password = self.cleaned_data.get("old_password")
|
old_password = self.cleaned_data.get("old_password")
|
||||||
if not authentication(self.user, old_password):
|
|
||||||
raise ValidationError(
|
|
||||||
self.error_messages["password_incorrect"],
|
|
||||||
code="password_incorrect",
|
|
||||||
)
|
|
||||||
|
|
||||||
return old_password
|
return old_password
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
|
|||||||
@@ -10,11 +10,10 @@ from django.urls import reverse_lazy
|
|||||||
from documents.etherpadlib import del_ep_cookie
|
from documents.etherpadlib import del_ep_cookie
|
||||||
|
|
||||||
from .decorators import authenticated_user
|
from .decorators import authenticated_user
|
||||||
from .forms import LdapPasswordChangeForm, LoginForm
|
from .forms import LdapPasswordChangeForm
|
||||||
|
|
||||||
|
|
||||||
class AuthLoginView(LoginView):
|
class AuthLoginView(LoginView):
|
||||||
authentication_form = LoginForm
|
|
||||||
redirect_authenticated_user = True
|
redirect_authenticated_user = True
|
||||||
template_name = "authentications/login.html"
|
template_name = "authentications/login.html"
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class CustomFlatPageAdmin(FlatPageAdmin):
|
|||||||
"url",
|
"url",
|
||||||
"title",
|
"title",
|
||||||
"content",
|
"content",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ class FETHeaderMiddleware(RemoteUserMiddleware):
|
|||||||
|
|
||||||
def process_request(self, request):
|
def process_request(self, request):
|
||||||
request.META[self.header] = request.META.get(
|
request.META[self.header] = request.META.get(
|
||||||
self.header, request.headers.get(self.header, None)
|
self.header,
|
||||||
|
request.headers.get(self.header, None),
|
||||||
)
|
)
|
||||||
|
|
||||||
super().process_request(request)
|
super().process_request(request)
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import environ
|
|||||||
|
|
||||||
env = environ.Env(
|
env = environ.Env(
|
||||||
# set casting, default value
|
# set casting, default value
|
||||||
DEBUG=(bool, True),
|
DEBUG=(str, "True"),
|
||||||
|
LDAP=(str, "False"),
|
||||||
MYSQL_HOST=(str, "mysql"),
|
MYSQL_HOST=(str, "mysql"),
|
||||||
MYSQL_PORT=(int, 3306),
|
MYSQL_PORT=(int, 3306),
|
||||||
MYSQL_DATABASE=(str, "fet2020db"),
|
MYSQL_DATABASE=(str, "fet2020db"),
|
||||||
@@ -23,7 +24,8 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|||||||
|
|
||||||
|
|
||||||
# DEBUGGING
|
# DEBUGGING
|
||||||
DEBUG = env("DEBUG")
|
DEBUG = env("DEBUG").lower() == "true"
|
||||||
|
LDAP = env("LDAP").lower() == "true"
|
||||||
|
|
||||||
|
|
||||||
# MODELS
|
# MODELS
|
||||||
@@ -63,9 +65,14 @@ INSTALLED_APPS = [
|
|||||||
|
|
||||||
|
|
||||||
# AUTHENTICATIONS
|
# AUTHENTICATIONS
|
||||||
AUTHENTICATION_BACKENDS = [
|
if not DEBUG and LDAP:
|
||||||
"django.contrib.auth.backends.ModelBackend",
|
AUTHENTICATION_BACKENDS = [
|
||||||
]
|
"authentications.backends.LdapBackend",
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
AUTHENTICATION_BACKENDS = [
|
||||||
|
"authentications.backends.DebugBackend",
|
||||||
|
]
|
||||||
|
|
||||||
LOGIN_REDIRECT_URL = "home"
|
LOGIN_REDIRECT_URL = "home"
|
||||||
LOGIN_URL = "/auth/login"
|
LOGIN_URL = "/auth/login"
|
||||||
@@ -77,7 +84,7 @@ if DEBUG:
|
|||||||
"default": {
|
"default": {
|
||||||
"ENGINE": "django.db.backends.sqlite3",
|
"ENGINE": "django.db.backends.sqlite3",
|
||||||
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
|
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
@@ -88,7 +95,7 @@ else:
|
|||||||
"PASSWORD": env("MYSQL_PASSWORD"),
|
"PASSWORD": env("MYSQL_PASSWORD"),
|
||||||
"HOST": env("MYSQL_HOST"),
|
"HOST": env("MYSQL_HOST"),
|
||||||
"PORT": env("MYSQL_PORT"),
|
"PORT": env("MYSQL_PORT"),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -99,9 +106,20 @@ EMAIL_PORT = 587
|
|||||||
EMAIL_USE_TLS = True
|
EMAIL_USE_TLS = True
|
||||||
|
|
||||||
|
|
||||||
|
# STATIC FILES
|
||||||
|
STATIC_URL = "static/" if DEBUG else "assets/"
|
||||||
|
|
||||||
|
# Use for collectstatic/production folder.
|
||||||
|
# --- Saving directory for production folder.
|
||||||
|
STATIC_ROOT = "assets/"
|
||||||
|
|
||||||
|
# --- Get files from following directory for production folder.
|
||||||
|
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")]
|
||||||
|
|
||||||
|
|
||||||
# FILE UPLOADS
|
# FILE UPLOADS
|
||||||
MEDIA_ROOT = os.path.join(BASE_DIR, "files/")
|
|
||||||
MEDIA_URL = "files/"
|
MEDIA_URL = "files/"
|
||||||
|
MEDIA_ROOT = os.path.join(BASE_DIR, "files")
|
||||||
|
|
||||||
|
|
||||||
# GLOBALIZATION
|
# GLOBALIZATION
|
||||||
@@ -167,20 +185,6 @@ DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
|||||||
SITE_ID = 1
|
SITE_ID = 1
|
||||||
|
|
||||||
|
|
||||||
# STATIC FILES
|
|
||||||
STATIC_ROOT = "assets/"
|
|
||||||
|
|
||||||
if DEBUG:
|
|
||||||
STATIC_URL = "static/"
|
|
||||||
else:
|
|
||||||
STATIC_URL = "assets/"
|
|
||||||
|
|
||||||
STATICFILES_DIRS = [
|
|
||||||
os.path.join(BASE_DIR, "gallery/static"),
|
|
||||||
os.path.join(BASE_DIR, "static"),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# SECURITY
|
# SECURITY
|
||||||
# TODO: Security Warning
|
# TODO: Security Warning
|
||||||
# CSRF_COOKIE_SECURE = True
|
# CSRF_COOKIE_SECURE = True
|
||||||
@@ -188,10 +192,7 @@ CSRF_TRUSTED_ORIGINS = [
|
|||||||
"https://" + env("HOST_NAME"),
|
"https://" + env("HOST_NAME"),
|
||||||
]
|
]
|
||||||
|
|
||||||
if DEBUG:
|
SECRET_KEY = "r37-i7l)vrduzz2-gira+z#u!p!di9#f+%s*5-bb($hg)55@ns" if DEBUG else env("SECRET_KEY")
|
||||||
SECRET_KEY = "r37-i7l)vrduzz2-gira+z#u!p!di9#f+%s*5-bb($hg)55@ns"
|
|
||||||
else:
|
|
||||||
SECRET_KEY = env("SECRET_KEY")
|
|
||||||
|
|
||||||
|
|
||||||
# TEMPLATES
|
# TEMPLATES
|
||||||
@@ -209,6 +210,7 @@ TEMPLATES = [
|
|||||||
"django.contrib.auth.context_processors.auth",
|
"django.contrib.auth.context_processors.auth",
|
||||||
"django.contrib.messages.context_processors.messages",
|
"django.contrib.messages.context_processors.messages",
|
||||||
"django.template.context_processors.i18n",
|
"django.template.context_processors.i18n",
|
||||||
|
"django.template.context_processors.media",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -350,14 +352,14 @@ if DEBUG:
|
|||||||
ETHERPAD_CLIENT = {
|
ETHERPAD_CLIENT = {
|
||||||
"url": "http://etherpad:" + env("ETHERPAD_PORT"),
|
"url": "http://etherpad:" + env("ETHERPAD_PORT"),
|
||||||
"exturl": ETHERPAD_HOST,
|
"exturl": ETHERPAD_HOST,
|
||||||
"apikey": "/srv/etherpad/APIKEY.txt",
|
"apikey": "/etherpad/APIKEY.txt",
|
||||||
"group": env("ETHERPAD_GROUP"),
|
"group": env("ETHERPAD_GROUP"),
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
ETHERPAD_CLIENT = {
|
ETHERPAD_CLIENT = {
|
||||||
"url": "http://etherpad:" + env("ETHERPAD_PORT"),
|
"url": "http://etherpad:" + env("ETHERPAD_PORT"),
|
||||||
"exturl": urljoin("https://" + env("HOST_NAME"), "etherpad/"),
|
"exturl": urljoin("https://" + env("HOST_NAME"), "etherpad/"),
|
||||||
"apikey": "/app/etherpad/APIKEY.txt",
|
"apikey": os.path.join(BASE_DIR, "etherpad/APIKEY.txt"),
|
||||||
"group": env("ETHERPAD_GROUP"),
|
"group": env("ETHERPAD_GROUP"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,11 +11,7 @@ def add_log_action(request, form, app_label, model, add=True):
|
|||||||
obj = form.save()
|
obj = form.save()
|
||||||
content_type = ContentType.objects.get(app_label=app_label, model=model)
|
content_type = ContentType.objects.get(app_label=app_label, model=model)
|
||||||
change_message = construct_change_message(form, None, add)
|
change_message = construct_change_message(form, None, add)
|
||||||
|
action_flag = ADDITION if add else CHANGE
|
||||||
if add:
|
|
||||||
action_flag = ADDITION
|
|
||||||
else:
|
|
||||||
action_flag = CHANGE
|
|
||||||
|
|
||||||
LogEntry.objects.log_action(
|
LogEntry.objects.log_action(
|
||||||
user_id=request.user.pk,
|
user_id=request.user.pk,
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ class BillAdmin(admin.ModelAdmin):
|
|||||||
"fields": (
|
"fields": (
|
||||||
"bill_creator",
|
"bill_creator",
|
||||||
"resolution",
|
"resolution",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -227,7 +227,7 @@ class BillAdmin(admin.ModelAdmin):
|
|||||||
"bankdata",
|
"bankdata",
|
||||||
("get_bankdata_name", "get_bankdata_iban", "get_bankdata_bic"),
|
("get_bankdata_name", "get_bankdata_iban", "get_bankdata_bic"),
|
||||||
"get_qrcode",
|
"get_qrcode",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -241,7 +241,7 @@ class BillAdmin(admin.ModelAdmin):
|
|||||||
"amount",
|
"amount",
|
||||||
"only_digital",
|
"only_digital",
|
||||||
"file_field",
|
"file_field",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -251,7 +251,7 @@ class BillAdmin(admin.ModelAdmin):
|
|||||||
"comment",
|
"comment",
|
||||||
"wiref",
|
"wiref",
|
||||||
"status",
|
"status",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -308,7 +308,7 @@ class BillAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
@admin.display(description="QR Code")
|
@admin.display(description="QR Code")
|
||||||
def get_qrcode(self, obj):
|
def get_qrcode(self, obj):
|
||||||
if obj.status != "C":
|
if obj.status != Bill.Status.CLEARED:
|
||||||
return "-"
|
return "-"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -329,24 +329,26 @@ class BillAdmin(admin.ModelAdmin):
|
|||||||
return "Daten für QR Code ungültig"
|
return "Daten für QR Code ungültig"
|
||||||
|
|
||||||
uri = qrcode.png_data_uri(scale=3.0)
|
uri = qrcode.png_data_uri(scale=3.0)
|
||||||
return format_html(f'<img src="{uri}">')
|
return format_html('<img src="{}">', uri)
|
||||||
|
|
||||||
@admin.display(description="Status")
|
@admin.display(description="Status")
|
||||||
def status_colored(self, obj):
|
def status_colored(self, obj):
|
||||||
# TODO: if there is a status without color, set nothing.
|
# TODO: if there is a status without color, set nothing.
|
||||||
colors = {
|
colors = {
|
||||||
"S": "red",
|
Bill.Status.SUBMITTED: "red",
|
||||||
"C": "darkorange",
|
Bill.Status.CLEARED: "darkorange",
|
||||||
"F": "green",
|
Bill.Status.FINISHED: "green",
|
||||||
"I": "blue",
|
Bill.Status.INCOMPLETED: "blue",
|
||||||
}
|
}
|
||||||
return format_html(
|
return format_html(
|
||||||
f'<b style="background:{colors[obj.status]};">{obj.get_status_display()}</b>'
|
'<b style="background:{color};">{status}</b>',
|
||||||
|
color=colors[obj.status],
|
||||||
|
status=obj.get_status_display(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@admin.action(description="Als 'Abgerechnet' markieren.")
|
@admin.action(description="Als 'Abgerechnet' markieren.")
|
||||||
def make_cleared(self, request, queryset):
|
def make_cleared(self, request, queryset):
|
||||||
updated = queryset.update(status="C")
|
updated = queryset.update(status=Bill.Status.CLEARED)
|
||||||
self.message_user(
|
self.message_user(
|
||||||
request,
|
request,
|
||||||
ngettext(
|
ngettext(
|
||||||
@@ -360,7 +362,7 @@ class BillAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
@admin.action(description="Als 'Abgeschlossen' markieren.")
|
@admin.action(description="Als 'Abgeschlossen' markieren.")
|
||||||
def make_finished(self, request, queryset):
|
def make_finished(self, request, queryset):
|
||||||
updated = queryset.update(status="F")
|
updated = queryset.update(status=Bill.Status.FINISHED)
|
||||||
self.message_user(
|
self.message_user(
|
||||||
request,
|
request,
|
||||||
ngettext(
|
ngettext(
|
||||||
@@ -403,7 +405,7 @@ class ResolutionAdmin(admin.ModelAdmin):
|
|||||||
"date",
|
"date",
|
||||||
"option",
|
"option",
|
||||||
"is_visible",
|
"is_visible",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -413,7 +415,7 @@ class ResolutionAdmin(admin.ModelAdmin):
|
|||||||
"budget",
|
"budget",
|
||||||
"total",
|
"total",
|
||||||
"budget_remaining",
|
"budget_remaining",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -463,7 +465,7 @@ class ResolutionAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
if fetmeeting is not None:
|
if fetmeeting is not None:
|
||||||
link = f"https://{settings.HOST_NAME}/posts/{fetmeeting.slug}/"
|
link = f"https://{settings.HOST_NAME}/posts/{fetmeeting.slug}/"
|
||||||
return format_html(f"<a href='{link}' target='_blank'>Link zur Fachschaftssitzung</a>")
|
return format_html('<a href="{}" target="_blank">Link zur Fachschaftssitzung</a>', link)
|
||||||
|
|
||||||
return format_html("-")
|
return format_html("-")
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ def get_cleaned_data(cleaned_data):
|
|||||||
if resolution != "":
|
if resolution != "":
|
||||||
try:
|
try:
|
||||||
cleaned_data["resolution"] = Resolution.objects.get(
|
cleaned_data["resolution"] = Resolution.objects.get(
|
||||||
Q(id=resolution) | Q(name=resolution)
|
Q(id=resolution) | Q(name=resolution),
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
raise ValidationError({"resolution_text": "Es gibt keinen Beschluss mit dieser ID."})
|
raise ValidationError({"resolution_text": "Es gibt keinen Beschluss mit dieser ID."})
|
||||||
@@ -68,7 +68,10 @@ class BillCreateForm(forms.ModelForm):
|
|||||||
|
|
||||||
# Resolution
|
# Resolution
|
||||||
resolution_text = forms.CharField(
|
resolution_text = forms.CharField(
|
||||||
required=False, label="Beschlussnummer", initial="", max_length=128
|
required=False,
|
||||||
|
label="Beschlussnummer",
|
||||||
|
initial="",
|
||||||
|
max_length=128,
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -122,7 +125,7 @@ class BillCreateForm(forms.ModelForm):
|
|||||||
self.fields["payer"].autofocus = True
|
self.fields["payer"].autofocus = True
|
||||||
|
|
||||||
bank_data = BankData.objects.filter(
|
bank_data = BankData.objects.filter(
|
||||||
Q(bankdata_creator=member) & Q(is_disabled=False)
|
Q(bankdata_creator=member) & Q(is_disabled=False),
|
||||||
).first()
|
).first()
|
||||||
if bank_data is not None:
|
if bank_data is not None:
|
||||||
self.fields["name_text"].initial = bank_data.name
|
self.fields["name_text"].initial = bank_data.name
|
||||||
@@ -147,12 +150,17 @@ class BillUpdateForm(forms.ModelForm):
|
|||||||
|
|
||||||
# only digital bill
|
# only digital bill
|
||||||
only_digital_new = forms.BooleanField(
|
only_digital_new = forms.BooleanField(
|
||||||
required=False, label="Ich habe eine neue digitale Rechnung.", initial=False
|
required=False,
|
||||||
|
label="Ich habe eine neue digitale Rechnung.",
|
||||||
|
initial=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Resolution
|
# Resolution
|
||||||
resolution_text = forms.CharField(
|
resolution_text = forms.CharField(
|
||||||
required=False, label="Beschlussnummer", initial="", max_length=128
|
required=False,
|
||||||
|
label="Beschlussnummer",
|
||||||
|
initial="",
|
||||||
|
max_length=128,
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -398,8 +406,10 @@ class BillAdminForm(forms.ModelForm):
|
|||||||
self.fields["bill_creator"].widget.can_change_related = False
|
self.fields["bill_creator"].widget.can_change_related = False
|
||||||
self.fields["bill_creator"].widget.can_delete_related = False
|
self.fields["bill_creator"].widget.can_delete_related = False
|
||||||
|
|
||||||
# delete wiref id from list if there are already 8 bills in wiref form.
|
# Delete wiref id from list if there are already 10 bills in wiref form.
|
||||||
qs = self.fields["wiref"].queryset.annotate(num_bills=Count("bill")).filter(num_bills__lt=8)
|
qs = (
|
||||||
|
self.fields["wiref"].queryset.annotate(num_bills=Count("bill")).filter(num_bills__lt=10)
|
||||||
|
)
|
||||||
|
|
||||||
# delete wiref id from if status is not opened.
|
# delete wiref id from if status is not opened.
|
||||||
qs = qs.filter(status=Wiref.Status.OPENED)
|
qs = qs.filter(status=Wiref.Status.OPENED)
|
||||||
|
|||||||
@@ -53,7 +53,10 @@ class Resolution(models.Model):
|
|||||||
voting_text = models.TextField(verbose_name="Abstimmungstext")
|
voting_text = models.TextField(verbose_name="Abstimmungstext")
|
||||||
|
|
||||||
budget = models.DecimalField(
|
budget = models.DecimalField(
|
||||||
max_digits=7, decimal_places=2, default=0.00, verbose_name="Budget (EUR)"
|
max_digits=7,
|
||||||
|
decimal_places=2,
|
||||||
|
default=0.00,
|
||||||
|
verbose_name="Budget (EUR)",
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -155,7 +158,9 @@ class Bill(models.Model):
|
|||||||
REPRESENTATION = "R", "Bundesvertretung"
|
REPRESENTATION = "R", "Bundesvertretung"
|
||||||
|
|
||||||
affiliation = models.CharField(
|
affiliation = models.CharField(
|
||||||
max_length=1, choices=Affiliation.choices, verbose_name="Abrechnungsbudget"
|
max_length=1,
|
||||||
|
choices=Affiliation.choices,
|
||||||
|
verbose_name="Abrechnungsbudget",
|
||||||
)
|
)
|
||||||
|
|
||||||
class Payer(models.TextChoices):
|
class Payer(models.TextChoices):
|
||||||
|
|||||||
Binary file not shown.
BIN
fet2020/finance/static/wiref/Vorlage.pdf
Normal file
BIN
fet2020/finance/static/wiref/Vorlage.pdf
Normal file
Binary file not shown.
@@ -4,25 +4,33 @@ import os
|
|||||||
|
|
||||||
from django.core.files import File
|
from django.core.files import File
|
||||||
from pypdf import PdfReader, PdfWriter
|
from pypdf import PdfReader, PdfWriter
|
||||||
|
from pypdf.constants import FieldDictionaryAttributes as FA
|
||||||
|
|
||||||
from .models import Bill, Wiref
|
from .models import Bill, Wiref
|
||||||
|
|
||||||
|
|
||||||
def generate_pdf(wiref):
|
def generate_pdf(wiref):
|
||||||
if wiref is not None and wiref.status == Wiref.Status.OPENED:
|
if wiref is None or wiref.status != Wiref.Status.OPENED:
|
||||||
|
return False
|
||||||
|
|
||||||
bills = Bill.objects.filter(wiref=wiref).order_by("date")
|
bills = Bill.objects.filter(wiref=wiref).order_by("date")
|
||||||
|
|
||||||
# Get data for pdf
|
# Get data for pdf
|
||||||
data = {}
|
data = {}
|
||||||
|
data_invoice = {} # Own dict for fixing text to multiline
|
||||||
for count, elem in enumerate(bills):
|
for count, elem in enumerate(bills):
|
||||||
data.update(
|
data.update(
|
||||||
{
|
{
|
||||||
f"Datum.{count}": str(elem.date.strftime("%d.%m.%Y")),
|
f"DatumRow{count + 1}": str(elem.date.strftime("%d.%m.%Y")),
|
||||||
f"Aussteller.{count}": elem.invoice,
|
f"VerwendungszweckRow{count + 1}": elem.purpose,
|
||||||
f"Verwendungszweck.{count}": elem.purpose,
|
|
||||||
# Replace decimal separator from '.' to ','
|
# Replace decimal separator from '.' to ','
|
||||||
f"Betrag.{count}": str(elem.amount).replace(".", ","),
|
f"BetragRow{count + 1}": str(elem.amount).replace(".", ","),
|
||||||
}
|
},
|
||||||
|
)
|
||||||
|
data_invoice.update(
|
||||||
|
{
|
||||||
|
f"RechnungsaustellerinRow{count + 1}": elem.invoice,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get budget year
|
# Get budget year
|
||||||
@@ -39,15 +47,15 @@ def generate_pdf(wiref):
|
|||||||
|
|
||||||
data.update(
|
data.update(
|
||||||
{
|
{
|
||||||
"Laufende Nummer": str(wiref.wiref_id),
|
"Rechnungsnummer": str(wiref.wiref_id),
|
||||||
"Budgetjahr": budget_year,
|
"Budgetjahr": budget_year,
|
||||||
# Replace decimal separator from '.' to ','
|
# Replace decimal separator from '.' to ','
|
||||||
"Summe": str(total).replace(".", ","),
|
"Summe": str(total).replace(".", ","),
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# Write data in pdf
|
# Write data in pdf
|
||||||
pdf_path = os.path.join(os.path.dirname(__file__), "static/Vorlage.pdf")
|
pdf_path = os.path.join(os.path.dirname(__file__), "static/wiref/Vorlage.pdf")
|
||||||
reader = PdfReader(pdf_path)
|
reader = PdfReader(pdf_path)
|
||||||
writer = PdfWriter()
|
writer = PdfWriter()
|
||||||
writer.append(reader)
|
writer.append(reader)
|
||||||
@@ -57,6 +65,13 @@ def generate_pdf(wiref):
|
|||||||
data,
|
data,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Add invoices and fix text to multiline
|
||||||
|
writer.update_page_form_field_values(
|
||||||
|
writer.pages[0],
|
||||||
|
data_invoice,
|
||||||
|
flags=FA.FfBits.Multiline,
|
||||||
|
)
|
||||||
|
|
||||||
with io.BytesIO() as bytes_stream:
|
with io.BytesIO() as bytes_stream:
|
||||||
writer.write(bytes_stream)
|
writer.write(bytes_stream)
|
||||||
|
|
||||||
@@ -65,5 +80,3 @@ def generate_pdf(wiref):
|
|||||||
wiref.file_field.save(wiref_name, File(bytes_stream, wiref_name))
|
wiref.file_field.save(wiref_name, File(bytes_stream, wiref_name))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ def set_bankdata(creator, name, iban, bic, saving):
|
|||||||
if saving is True:
|
if saving is True:
|
||||||
# Disable old bank data.
|
# Disable old bank data.
|
||||||
qs = BankData.objects.filter(
|
qs = BankData.objects.filter(
|
||||||
~Q(id=obj.id) & Q(bankdata_creator=obj.bankdata_creator) & Q(is_disabled=False)
|
~Q(id=obj.id) & Q(bankdata_creator=obj.bankdata_creator) & Q(is_disabled=False),
|
||||||
)
|
)
|
||||||
qs.update(is_disabled=True)
|
qs.update(is_disabled=True)
|
||||||
|
|
||||||
@@ -114,11 +114,7 @@ class BillUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
|
|||||||
|
|
||||||
# Call bill if it's only yours.
|
# Call bill if it's only yours.
|
||||||
def test_func(self):
|
def test_func(self):
|
||||||
if self.get_object().bill_creator.username == self.request.user.username:
|
return self.get_object().bill_creator.username == self.request.user.username
|
||||||
return True
|
|
||||||
|
|
||||||
# Call handle_no_permissions method.
|
|
||||||
return False
|
|
||||||
|
|
||||||
def handle_no_permission(self):
|
def handle_no_permission(self):
|
||||||
return redirect("finance:bill_list")
|
return redirect("finance:bill_list")
|
||||||
|
|||||||
@@ -14,12 +14,18 @@ class Album(models.Model):
|
|||||||
thumbnail = models.CharField(verbose_name="Thumbnail", max_length=200, null=True, blank=True)
|
thumbnail = models.CharField(verbose_name="Thumbnail", max_length=200, null=True, blank=True)
|
||||||
|
|
||||||
event_date = models.DateField(
|
event_date = models.DateField(
|
||||||
verbose_name="Event Datum", null=True, blank=True, default=timezone.now
|
verbose_name="Event Datum",
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
default=timezone.now,
|
||||||
)
|
)
|
||||||
event_place = models.CharField(max_length=200, blank=True)
|
event_place = models.CharField(max_length=200, blank=True)
|
||||||
|
|
||||||
photographer = models.CharField(
|
photographer = models.CharField(
|
||||||
verbose_name="Fotograph(en)", max_length=200, null=True, blank=True
|
verbose_name="Fotograph(en)",
|
||||||
|
max_length=200,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
DRAFT = "10"
|
DRAFT = "10"
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ def get_image_list(folder_name):
|
|||||||
img_dict = {
|
img_dict = {
|
||||||
"title": img,
|
"title": img,
|
||||||
"image_url": os.path.join(
|
"image_url": os.path.join(
|
||||||
settings.MEDIA_URL + gallery_path, folder_name + "/" + img
|
settings.MEDIA_URL + gallery_path,
|
||||||
|
folder_name + "/" + img,
|
||||||
),
|
),
|
||||||
"thumb_url": thumb_url,
|
"thumb_url": thumb_url,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ def index(request):
|
|||||||
slug=slugify(folder),
|
slug=slugify(folder),
|
||||||
folder_name=folder,
|
folder_name=folder,
|
||||||
event_date=None,
|
event_date=None,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# show only PUBLIC albums.
|
# show only PUBLIC albums.
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ class TopicGroupAdmin(admin.ModelAdmin):
|
|||||||
"slug",
|
"slug",
|
||||||
"order",
|
"order",
|
||||||
"short_description",
|
"short_description",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -118,7 +118,7 @@ class TopicAdmin(admin.ModelAdmin):
|
|||||||
"topic_group",
|
"topic_group",
|
||||||
"archive",
|
"archive",
|
||||||
"description",
|
"description",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -163,7 +163,7 @@ class AttachmentAdmin(admin.ModelAdmin):
|
|||||||
"title",
|
"title",
|
||||||
"topic",
|
"topic",
|
||||||
"description",
|
"description",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ class TopicGroup(models.Model):
|
|||||||
short_description = models.TextField(null=True, blank=True)
|
short_description = models.TextField(null=True, blank=True)
|
||||||
|
|
||||||
order = models.PositiveSmallIntegerField(
|
order = models.PositiveSmallIntegerField(
|
||||||
verbose_name="Reihenfolge", unique=True, null=True, blank=True
|
verbose_name="Reihenfolge",
|
||||||
|
unique=True,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = models.Manager()
|
objects = models.Manager()
|
||||||
@@ -53,7 +56,9 @@ class Topic(models.Model):
|
|||||||
description = models.TextField(blank=True, null=True)
|
description = models.TextField(blank=True, null=True)
|
||||||
|
|
||||||
topic_group = models.ForeignKey(
|
topic_group = models.ForeignKey(
|
||||||
TopicGroup, on_delete=models.CASCADE, verbose_name="Themenbereich"
|
TopicGroup,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
verbose_name="Themenbereich",
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = models.Manager()
|
objects = models.Manager()
|
||||||
@@ -65,7 +70,8 @@ class Topic(models.Model):
|
|||||||
constraints = [
|
constraints = [
|
||||||
UniqueConstraint(fields=["slug", "topic_group"], name="unique_intern_slug_topic_group"),
|
UniqueConstraint(fields=["slug", "topic_group"], name="unique_intern_slug_topic_group"),
|
||||||
UniqueConstraint(
|
UniqueConstraint(
|
||||||
fields=["title", "topic_group"], name="unique_intern_title_topic_group"
|
fields=["title", "topic_group"],
|
||||||
|
name="unique_intern_title_topic_group",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -126,7 +132,9 @@ class Etherpad(models.Model):
|
|||||||
date = models.DateField(default=date.today, verbose_name="Datum")
|
date = models.DateField(default=date.today, verbose_name="Datum")
|
||||||
|
|
||||||
attachment = models.ForeignKey(
|
attachment = models.ForeignKey(
|
||||||
Attachment, on_delete=models.CASCADE, verbose_name="Anhang Ordner"
|
Attachment,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
verbose_name="Anhang Ordner",
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = models.Manager()
|
objects = models.Manager()
|
||||||
@@ -178,7 +186,9 @@ class FileUpload(models.Model):
|
|||||||
date = models.DateField(default=date.today, verbose_name="Datum")
|
date = models.DateField(default=date.today, verbose_name="Datum")
|
||||||
|
|
||||||
attachment = models.ForeignKey(
|
attachment = models.ForeignKey(
|
||||||
Attachment, on_delete=models.CASCADE, verbose_name="Anhang Ordner"
|
Attachment,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
verbose_name="Anhang Ordner",
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = models.Manager()
|
objects = models.Manager()
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ logger = logging.getLogger(__name__)
|
|||||||
@authenticated_user
|
@authenticated_user
|
||||||
def index(request):
|
def index(request):
|
||||||
topic = Topic.objects.filter(archive=False).order_by(
|
topic = Topic.objects.filter(archive=False).order_by(
|
||||||
F("topic_group__order").asc(nulls_last=True), "topic_group", "title"
|
F("topic_group__order").asc(nulls_last=True),
|
||||||
|
"topic_group",
|
||||||
|
"title",
|
||||||
)
|
)
|
||||||
empty_topic_groups = TopicGroup.objects.filter(topic=None)
|
empty_topic_groups = TopicGroup.objects.filter(topic=None)
|
||||||
archive_topic = Topic.objects.filter(archive=True)
|
archive_topic = Topic.objects.filter(archive=True)
|
||||||
@@ -107,7 +109,7 @@ class AttachmentCreateView(LoginRequiredMixin, CreateView):
|
|||||||
topic_group_slug = self.kwargs.get("topic_group_slug")
|
topic_group_slug = self.kwargs.get("topic_group_slug")
|
||||||
topic_slug = self.kwargs.get("slug")
|
topic_slug = self.kwargs.get("slug")
|
||||||
context["topic"] = Topic.objects.get(
|
context["topic"] = Topic.objects.get(
|
||||||
Q(topic_group__slug=topic_group_slug) & Q(slug=topic_slug)
|
Q(topic_group__slug=topic_group_slug) & Q(slug=topic_slug),
|
||||||
)
|
)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@@ -143,7 +145,7 @@ class AttachmentDetailView(LoginRequiredMixin, DetailView):
|
|||||||
topic_group_slug = self.kwargs.get("topic_group_slug")
|
topic_group_slug = self.kwargs.get("topic_group_slug")
|
||||||
topic_slug = self.kwargs.get("topic_slug")
|
topic_slug = self.kwargs.get("topic_slug")
|
||||||
return Attachment.objects.filter(
|
return Attachment.objects.filter(
|
||||||
Q(topic__topic_group__slug=topic_group_slug) & Q(topic__slug=topic_slug)
|
Q(topic__topic_group__slug=topic_group_slug) & Q(topic__slug=topic_slug),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -166,7 +168,7 @@ class AttachmentUpdateView(LoginRequiredMixin, UpdateView):
|
|||||||
topic_group_slug = self.kwargs.get("topic_group_slug")
|
topic_group_slug = self.kwargs.get("topic_group_slug")
|
||||||
topic_slug = self.kwargs.get("topic_slug")
|
topic_slug = self.kwargs.get("topic_slug")
|
||||||
return Attachment.objects.filter(
|
return Attachment.objects.filter(
|
||||||
Q(topic__topic_group__slug=topic_group_slug) & Q(topic__slug=topic_slug)
|
Q(topic__topic_group__slug=topic_group_slug) & Q(topic__slug=topic_slug),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -186,7 +188,7 @@ class EtherpadCreateView(LoginRequiredMixin, CreateView):
|
|||||||
topic_slug = self.kwargs.get("topic_slug")
|
topic_slug = self.kwargs.get("topic_slug")
|
||||||
slug = self.kwargs.get("slug")
|
slug = self.kwargs.get("slug")
|
||||||
attachment = Attachment.objects.get(
|
attachment = Attachment.objects.get(
|
||||||
Q(topic__topic_group__slug=topic_group_slug) & Q(topic__slug=topic_slug) & Q(slug=slug)
|
Q(topic__topic_group__slug=topic_group_slug) & Q(topic__slug=topic_slug) & Q(slug=slug),
|
||||||
)
|
)
|
||||||
context["attachment"] = attachment
|
context["attachment"] = attachment
|
||||||
return context
|
return context
|
||||||
@@ -211,7 +213,7 @@ class FileUploadCreateView(LoginRequiredMixin, CreateView):
|
|||||||
topic_slug = self.kwargs.get("topic_slug")
|
topic_slug = self.kwargs.get("topic_slug")
|
||||||
slug = self.kwargs.get("slug")
|
slug = self.kwargs.get("slug")
|
||||||
attachment = Attachment.objects.get(
|
attachment = Attachment.objects.get(
|
||||||
Q(topic__topic_group__slug=topic_group_slug) & Q(topic__slug=topic_slug) & Q(slug=slug)
|
Q(topic__topic_group__slug=topic_group_slug) & Q(topic__slug=topic_slug) & Q(slug=slug),
|
||||||
)
|
)
|
||||||
context["attachment"] = attachment
|
context["attachment"] = attachment
|
||||||
return context
|
return context
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ class MemberAdmin(admin.ModelAdmin):
|
|||||||
"birthday",
|
"birthday",
|
||||||
"phone",
|
"phone",
|
||||||
"address",
|
"address",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -137,7 +137,7 @@ class JobAdmin(admin.ModelAdmin):
|
|||||||
"fields": (
|
"fields": (
|
||||||
"name",
|
"name",
|
||||||
"job_group",
|
"job_group",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -146,7 +146,7 @@ class JobAdmin(admin.ModelAdmin):
|
|||||||
"fields": (
|
"fields": (
|
||||||
"shortterm",
|
"shortterm",
|
||||||
"slug",
|
"slug",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -193,7 +193,7 @@ class JobGroupAdmin(admin.ModelAdmin):
|
|||||||
"fields": (
|
"fields": (
|
||||||
"name",
|
"name",
|
||||||
"description",
|
"description",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -202,7 +202,7 @@ class JobGroupAdmin(admin.ModelAdmin):
|
|||||||
"fields": (
|
"fields": (
|
||||||
"shortterm",
|
"shortterm",
|
||||||
"slug",
|
"slug",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -46,8 +46,7 @@ class MemberForm(forms.ModelForm):
|
|||||||
|
|
||||||
help_texts = {
|
help_texts = {
|
||||||
"image": (
|
"image": (
|
||||||
"Mindestdimension: 150*150 px, maximale Größe: 10MB, erlaubtes Format: "
|
"Mindestdimension: 150*150 px, maximale Größe: 10MB, erlaubtes Format: Bildformate."
|
||||||
"Bildformate."
|
|
||||||
),
|
),
|
||||||
"mailaccount": "Die Mailadresse mit '@fet.at' angeben.",
|
"mailaccount": "Die Mailadresse mit '@fet.at' angeben.",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class ActiveJobMemberManager(models.Manager):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return qs.filter(
|
return qs.filter(
|
||||||
Q(member__role="A") & (Q(job_end__gt=date_today) | Q(job_end__isnull=True))
|
Q(member__role="A") & (Q(job_end__gt=date_today) | Q(job_end__isnull=True)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ class InactiveJobMemberManager(models.Manager):
|
|||||||
|
|
||||||
return qs.filter(
|
return qs.filter(
|
||||||
Q(member__role="P")
|
Q(member__role="P")
|
||||||
| (Q(job_end__lt=date_today + timedelta(days=1)) & Q(job_end__isnull=False))
|
| (Q(job_end__lt=date_today + timedelta(days=1)) & Q(job_end__isnull=False)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -56,7 +56,9 @@ class JobMemberManager(models.Manager):
|
|||||||
|
|
||||||
def get_all_jobs_sorted(self):
|
def get_all_jobs_sorted(self):
|
||||||
qs = self.get_queryset().order_by(
|
qs = self.get_queryset().order_by(
|
||||||
F("job_end").desc(nulls_first=True), "-job_start", "job__name"
|
F("job_end").desc(nulls_first=True),
|
||||||
|
"-job_start",
|
||||||
|
"job__name",
|
||||||
)
|
)
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ def get_jobs_sidebar(slug):
|
|||||||
job_groups.remove(elem)
|
job_groups.remove(elem)
|
||||||
|
|
||||||
job_members = JobMember.active_member.get_all(slug=slug).order_by(
|
job_members = JobMember.active_member.get_all(slug=slug).order_by(
|
||||||
F("job__order").asc(nulls_last=True), "job__name"
|
F("job__order").asc(nulls_last=True),
|
||||||
|
"job__name",
|
||||||
)
|
)
|
||||||
active_job_group = JobGroup.objects.filter(slug=slug).first()
|
active_job_group = JobGroup.objects.filter(slug=slug).first()
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ def jobs(request, slug=None):
|
|||||||
raise Http404("wrong job")
|
raise Http404("wrong job")
|
||||||
|
|
||||||
job_members = JobMember.active_member.get_all(slug=slug).order_by(
|
job_members = JobMember.active_member.get_all(slug=slug).order_by(
|
||||||
F("job__order").asc(nulls_last=True), "job__name"
|
F("job__order").asc(nulls_last=True),
|
||||||
|
"job__name",
|
||||||
)
|
)
|
||||||
active_job_group = JobGroup.objects.filter(slug=slug).first()
|
active_job_group = JobGroup.objects.filter(slug=slug).first()
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class PostAdmin(admin.ModelAdmin):
|
|||||||
"all": [
|
"all": [
|
||||||
"jquery-ui/jquery-ui.min.css",
|
"jquery-ui/jquery-ui.min.css",
|
||||||
"jquery-ui/ui-lightness/theme.css",
|
"jquery-ui/ui-lightness/theme.css",
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
js = [
|
js = [
|
||||||
"jquery-ui/jquery-ui.min.js",
|
"jquery-ui/jquery-ui.min.js",
|
||||||
@@ -108,7 +108,7 @@ class NewsAdmin(PostAdmin):
|
|||||||
"title",
|
"title",
|
||||||
"subtitle",
|
"subtitle",
|
||||||
"tags",
|
"tags",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -117,7 +117,7 @@ class NewsAdmin(PostAdmin):
|
|||||||
"fields": (
|
"fields": (
|
||||||
"status",
|
"status",
|
||||||
"is_pinned",
|
"is_pinned",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -126,7 +126,7 @@ class NewsAdmin(PostAdmin):
|
|||||||
"fields": (
|
"fields": (
|
||||||
"image",
|
"image",
|
||||||
"body",
|
"body",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -136,7 +136,7 @@ class NewsAdmin(PostAdmin):
|
|||||||
"slug",
|
"slug",
|
||||||
"author",
|
"author",
|
||||||
"public_date",
|
"public_date",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -161,7 +161,7 @@ class EventAdmin(PostAdmin):
|
|||||||
"title",
|
"title",
|
||||||
"subtitle",
|
"subtitle",
|
||||||
"tags",
|
"tags",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -170,7 +170,7 @@ class EventAdmin(PostAdmin):
|
|||||||
"fields": (
|
"fields": (
|
||||||
"status",
|
"status",
|
||||||
"is_pinned",
|
"is_pinned",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -180,7 +180,7 @@ class EventAdmin(PostAdmin):
|
|||||||
"event_start",
|
"event_start",
|
||||||
"event_end",
|
"event_end",
|
||||||
"event_place",
|
"event_place",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -189,7 +189,7 @@ class EventAdmin(PostAdmin):
|
|||||||
"fields": (
|
"fields": (
|
||||||
"image",
|
"image",
|
||||||
"body",
|
"body",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -199,7 +199,7 @@ class EventAdmin(PostAdmin):
|
|||||||
"slug",
|
"slug",
|
||||||
"author",
|
"author",
|
||||||
"public_date",
|
"public_date",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -223,7 +223,7 @@ class FetMeetingAdmin(EventAdmin):
|
|||||||
"event_end",
|
"event_end",
|
||||||
"event_place",
|
"event_place",
|
||||||
"tags",
|
"tags",
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -149,10 +149,14 @@ class PostSearchForm(forms.Form):
|
|||||||
month = forms.ChoiceField(label="Monat", choices=month_choices, required=False)
|
month = forms.ChoiceField(label="Monat", choices=month_choices, required=False)
|
||||||
|
|
||||||
compact_view = forms.BooleanField(
|
compact_view = forms.BooleanField(
|
||||||
label="Kompakte Ansicht", required=False, widget=CheckboxInput
|
label="Kompakte Ansicht",
|
||||||
|
required=False,
|
||||||
|
widget=CheckboxInput,
|
||||||
)
|
)
|
||||||
fet_meeting_only = forms.BooleanField(
|
fet_meeting_only = forms.BooleanField(
|
||||||
label="nur FET Sitzungen", required=False, widget=CheckboxInput
|
label="nur FET Sitzungen",
|
||||||
|
required=False,
|
||||||
|
widget=CheckboxInput,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|||||||
@@ -10,20 +10,14 @@ class PublishedManager(models.Manager):
|
|||||||
"""
|
"""
|
||||||
publish all posts with status 'PUBLIC'
|
publish all posts with status 'PUBLIC'
|
||||||
"""
|
"""
|
||||||
if public:
|
qs = self.get_queryset().filter(status="20") if public else self.get_queryset()
|
||||||
qs = self.get_queryset().filter(status="20")
|
|
||||||
else:
|
|
||||||
qs = self.get_queryset()
|
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
def published_all(self, public=True):
|
def published_all(self, public=True):
|
||||||
"""
|
"""
|
||||||
publish all posts with status 'PUBLIC' and 'ONLY_INTERN'
|
publish all posts with status 'PUBLIC' and 'ONLY_INTERN'
|
||||||
"""
|
"""
|
||||||
if public:
|
qs = self.get_queryset().filter(~Q(status="10")) if public else self.get_queryset()
|
||||||
qs = self.get_queryset().filter(~Q(status="10"))
|
|
||||||
else:
|
|
||||||
qs = self.get_queryset()
|
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
|
||||||
@@ -35,7 +29,7 @@ class PostManager(PublishedManager, models.Manager):
|
|||||||
When(post_type="N", then="public_date"),
|
When(post_type="N", then="public_date"),
|
||||||
When(post_type="E", then="event_start__date"),
|
When(post_type="E", then="event_start__date"),
|
||||||
When(post_type="F", then="event_start__date"),
|
When(post_type="F", then="event_start__date"),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
return qs.order_by("-date", "-id")
|
return qs.order_by("-date", "-id")
|
||||||
|
|
||||||
@@ -74,7 +68,7 @@ class ArticleManager(PublishedManager, models.Manager):
|
|||||||
date=Case(
|
date=Case(
|
||||||
When(post_type="N", then="public_date"),
|
When(post_type="N", then="public_date"),
|
||||||
When(post_type="E", then="event_start__date"),
|
When(post_type="E", then="event_start__date"),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
return qs.order_by("-date", "-id")
|
return qs.order_by("-date", "-id")
|
||||||
|
|
||||||
@@ -87,7 +81,7 @@ class ArticleManager(PublishedManager, models.Manager):
|
|||||||
__month = post_date.month
|
__month = post_date.month
|
||||||
__year = post_date.year
|
__year = post_date.year
|
||||||
|
|
||||||
if __month != 0:
|
if __month != 1:
|
||||||
__month -= 1
|
__month -= 1
|
||||||
else:
|
else:
|
||||||
# If the current month is January, you get the date from December of previous year.
|
# If the current month is January, you get the date from December of previous year.
|
||||||
@@ -104,7 +98,7 @@ class ArticleManager(PublishedManager, models.Manager):
|
|||||||
.filter(is_pinned=True)
|
.filter(is_pinned=True)
|
||||||
.filter(
|
.filter(
|
||||||
(Q(post_type="N") & Q(public_date__gt=post_date))
|
(Q(post_type="N") & Q(public_date__gt=post_date))
|
||||||
| (Q(post_type="E") & Q(event_end__date__gt=event_date))
|
| (Q(post_type="E") & Q(event_end__date__gt=event_date)),
|
||||||
)
|
)
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
@@ -120,7 +114,7 @@ class NewsManager(PublishedManager, models.Manager):
|
|||||||
qs = qs.annotate(
|
qs = qs.annotate(
|
||||||
date=Case(
|
date=Case(
|
||||||
When(post_type="N", then="public_date"),
|
When(post_type="N", then="public_date"),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
return qs.order_by("-date")
|
return qs.order_by("-date")
|
||||||
|
|
||||||
@@ -136,7 +130,7 @@ class AllEventManager(PublishedManager, models.Manager):
|
|||||||
date=Case(
|
date=Case(
|
||||||
When(post_type="E", then="event_start__date"),
|
When(post_type="E", then="event_start__date"),
|
||||||
When(post_type="F", then="event_start__date"),
|
When(post_type="F", then="event_start__date"),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
return qs.order_by("-date")
|
return qs.order_by("-date")
|
||||||
|
|
||||||
@@ -157,7 +151,7 @@ class EventManager(PublishedManager, models.Manager):
|
|||||||
qs = qs.annotate(
|
qs = qs.annotate(
|
||||||
date=Case(
|
date=Case(
|
||||||
When(post_type="E", then="event_start__date"),
|
When(post_type="E", then="event_start__date"),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
return qs.order_by("-date")
|
return qs.order_by("-date")
|
||||||
|
|
||||||
@@ -182,7 +176,7 @@ class FetMeetingManager(PublishedManager, models.Manager):
|
|||||||
qs = qs.annotate(
|
qs = qs.annotate(
|
||||||
date=Case(
|
date=Case(
|
||||||
When(post_type="F", then="event_start__date"),
|
When(post_type="F", then="event_start__date"),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
return qs.order_by("-date")
|
return qs.order_by("-date")
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,10 @@ class Post(models.Model):
|
|||||||
|
|
||||||
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
|
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
|
||||||
public_date = models.DateField(
|
public_date = models.DateField(
|
||||||
verbose_name="Veröffentlichung", null=True, blank=True, default=timezone.now
|
verbose_name="Veröffentlichung",
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
default=timezone.now,
|
||||||
)
|
)
|
||||||
|
|
||||||
__choices = [("N", _("News")), ("E", _("Event")), ("F", _("FetMeeting"))]
|
__choices = [("N", _("News")), ("E", _("Event")), ("F", _("FetMeeting"))]
|
||||||
@@ -137,9 +140,6 @@ class Post(models.Model):
|
|||||||
self.title,
|
self.title,
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return reverse("posts:post", kwargs={"slug": self.slug})
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
# save the post with some defaults
|
# save the post with some defaults
|
||||||
if not self.public_date:
|
if not self.public_date:
|
||||||
@@ -150,6 +150,9 @@ class Post(models.Model):
|
|||||||
|
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse("posts:post", kwargs={"slug": self.slug})
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def agenda_html(self) -> str | None:
|
def agenda_html(self) -> str | None:
|
||||||
"Agenda HTML from Etherpad Pad"
|
"Agenda HTML from Etherpad Pad"
|
||||||
@@ -188,7 +191,9 @@ class Post(models.Model):
|
|||||||
|
|
||||||
ep_set_html(self.protocol_key, value)
|
ep_set_html(self.protocol_key, value)
|
||||||
request_logger.info(
|
request_logger.info(
|
||||||
"Set protocol etherpad. Post: %s. Key: %s", self.slug, self.protocol_key
|
"Set protocol etherpad. Post: %s. Key: %s",
|
||||||
|
self.slug,
|
||||||
|
self.protocol_key,
|
||||||
)
|
)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@@ -302,10 +307,7 @@ class Post(models.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def published(self):
|
def published(self):
|
||||||
if self.status == self.Status.PUBLIC:
|
return self.status == self.Status.PUBLIC
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class News(Post):
|
class News(Post):
|
||||||
@@ -328,16 +330,16 @@ class Event(Post):
|
|||||||
only_events = EventManager()
|
only_events = EventManager()
|
||||||
all_events = AllEventManager()
|
all_events = AllEventManager()
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.post_type = "E"
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
|
|
||||||
verbose_name = "Event"
|
verbose_name = "Event"
|
||||||
verbose_name_plural = "Events"
|
verbose_name_plural = "Events"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.post_type = "E"
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if not self.post_type:
|
if not self.post_type:
|
||||||
self.post_type = "E"
|
self.post_type = "E"
|
||||||
@@ -355,16 +357,16 @@ class Event(Post):
|
|||||||
class FetMeeting(Event):
|
class FetMeeting(Event):
|
||||||
objects = FetMeetingManager()
|
objects = FetMeetingManager()
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.post_type = "F"
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
|
|
||||||
verbose_name = "Fet Sitzung"
|
verbose_name = "Fet Sitzung"
|
||||||
verbose_name_plural = "Fet Sitzungen"
|
verbose_name_plural = "Fet Sitzungen"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.post_type = "F"
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
self.title = "Fachschaftssitzung"
|
self.title = "Fachschaftssitzung"
|
||||||
if not self.slug:
|
if not self.slug:
|
||||||
@@ -398,8 +400,7 @@ class FetMeeting(Event):
|
|||||||
def __get_slug(self) -> str:
|
def __get_slug(self) -> str:
|
||||||
slug = slugify(self.event_start.date()) + "-" + slugify("Fachschaftssitzung")
|
slug = slugify(self.event_start.date()) + "-" + slugify("Fachschaftssitzung")
|
||||||
|
|
||||||
if Post.objects.filter(slug=slug).exists():
|
if Post.objects.filter(slug=slug).exists() and Post.objects.get(slug=slug).id != self.id:
|
||||||
if Post.objects.get(slug=slug).id != self.id:
|
|
||||||
raise ValidationError("Es existiert bereits eine Sitzung mit demselben Datum.")
|
raise ValidationError("Es existiert bereits eine Sitzung mit demselben Datum.")
|
||||||
|
|
||||||
return slug
|
return slug
|
||||||
|
|||||||
@@ -22,9 +22,7 @@ class PostIndex(indexes.SearchIndex, indexes.Indexable):
|
|||||||
def prepare_date(self, obj):
|
def prepare_date(self, obj):
|
||||||
if obj.post_type == "N":
|
if obj.post_type == "N":
|
||||||
return obj.public_date
|
return obj.public_date
|
||||||
elif obj.post_type == "E":
|
elif obj.post_type == "E" or obj.post_type == "F":
|
||||||
return obj.event_start.date()
|
|
||||||
elif obj.post_type == "F":
|
|
||||||
return obj.event_start.date()
|
return obj.event_start.date()
|
||||||
|
|
||||||
def prepare_agenda(self, obj):
|
def prepare_agenda(self, obj):
|
||||||
|
|||||||
@@ -146,13 +146,13 @@ class PostDetailView(DetailView):
|
|||||||
Helper function for getting previous post
|
Helper function for getting previous post
|
||||||
"""
|
"""
|
||||||
posts = Post.objects.date_sorted_list(self.public_only).filter(
|
posts = Post.objects.date_sorted_list(self.public_only).filter(
|
||||||
post_type=self.object.post_type
|
post_type=self.object.post_type,
|
||||||
)
|
)
|
||||||
qs = posts.filter(
|
qs = posts.filter(
|
||||||
# Get posts which are in the future.
|
# Get posts which are in the future.
|
||||||
Q(date__lt=self.object.date)
|
Q(date__lt=self.object.date)
|
||||||
# Get posts which have the same date but id is lower.
|
# Get posts which have the same date but id is lower.
|
||||||
| (Q(date__lte=self.object.date) & Q(id__lt=self.object.id))
|
| (Q(date__lte=self.object.date) & Q(id__lt=self.object.id)),
|
||||||
)
|
)
|
||||||
if not qs:
|
if not qs:
|
||||||
# If there are any prev posts, then take the latest one.
|
# If there are any prev posts, then take the latest one.
|
||||||
@@ -173,7 +173,7 @@ class PostDetailView(DetailView):
|
|||||||
# Get posts which are in the past.
|
# Get posts which are in the past.
|
||||||
Q(date__gt=self.object.date)
|
Q(date__gt=self.object.date)
|
||||||
# Get posts which have the same date but id is greater.
|
# Get posts which have the same date but id is greater.
|
||||||
| (Q(date__gte=self.object.date) & Q(id__gt=self.object.id))
|
| (Q(date__gte=self.object.date) & Q(id__gt=self.object.id)),
|
||||||
)
|
)
|
||||||
if not qs:
|
if not qs:
|
||||||
# If there are any next posts, then take the first one.
|
# If there are any next posts, then take the first one.
|
||||||
@@ -314,7 +314,7 @@ def show_pdf(request, html, filename):
|
|||||||
|
|
||||||
response = HttpResponse(pdf, content_type="application/pdf")
|
response = HttpResponse(pdf, content_type="application/pdf")
|
||||||
|
|
||||||
content = "inline; filename=%s" % filename
|
content = f"inline; filename={filename}"
|
||||||
response["Content-Disposition"] = content
|
response["Content-Disposition"] = content
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
[pytest]
|
|
||||||
DJANGO_SETTINGS_MODULE = fet2020.settings
|
|
||||||
Binary file not shown.
@@ -22,7 +22,7 @@ class FetUserSearchForm(SearchForm):
|
|||||||
sqs_gallery = self.searchqueryset.models(Album)
|
sqs_gallery = self.searchqueryset.models(Album)
|
||||||
sqs_gallery = sqs_gallery.filter(
|
sqs_gallery = sqs_gallery.filter(
|
||||||
SQ(title__icontains=self.cleaned_data["q"])
|
SQ(title__icontains=self.cleaned_data["q"])
|
||||||
| SQ(description__icontains=self.cleaned_data["q"])
|
| SQ(description__icontains=self.cleaned_data["q"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
sqs_intern = self.searchqueryset.models(Etherpad)
|
sqs_intern = self.searchqueryset.models(Etherpad)
|
||||||
@@ -31,7 +31,7 @@ class FetUserSearchForm(SearchForm):
|
|||||||
sqs_member = self.searchqueryset.models(Member)
|
sqs_member = self.searchqueryset.models(Member)
|
||||||
sqs_member = sqs_member.filter(
|
sqs_member = sqs_member.filter(
|
||||||
SQ(firstname__icontains=self.cleaned_data["q"])
|
SQ(firstname__icontains=self.cleaned_data["q"])
|
||||||
| SQ(surname__icontains=self.cleaned_data["q"])
|
| SQ(surname__icontains=self.cleaned_data["q"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
sqs_post = self.searchqueryset.models(Post)
|
sqs_post = self.searchqueryset.models(Post)
|
||||||
@@ -39,7 +39,7 @@ class FetUserSearchForm(SearchForm):
|
|||||||
SQ(title__icontains=self.cleaned_data["q"])
|
SQ(title__icontains=self.cleaned_data["q"])
|
||||||
| SQ(body__icontains=self.cleaned_data["q"])
|
| SQ(body__icontains=self.cleaned_data["q"])
|
||||||
| SQ(agenda__icontains=self.cleaned_data["q"])
|
| SQ(agenda__icontains=self.cleaned_data["q"])
|
||||||
| SQ(protocol__icontains=self.cleaned_data["q"])
|
| SQ(protocol__icontains=self.cleaned_data["q"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
tmp_results = deque([])
|
tmp_results = deque([])
|
||||||
@@ -82,20 +82,20 @@ class NonUserSearchForm(SearchForm):
|
|||||||
sqs_gallery = self.searchqueryset.models(Album).filter(status="20")
|
sqs_gallery = self.searchqueryset.models(Album).filter(status="20")
|
||||||
sqs_gallery = sqs_gallery.filter(
|
sqs_gallery = sqs_gallery.filter(
|
||||||
SQ(title__icontains=self.cleaned_data["q"])
|
SQ(title__icontains=self.cleaned_data["q"])
|
||||||
| SQ(description__icontains=self.cleaned_data["q"])
|
| SQ(description__icontains=self.cleaned_data["q"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
sqs_member = self.searchqueryset.models(Member)
|
sqs_member = self.searchqueryset.models(Member)
|
||||||
sqs_member = sqs_member.filter(
|
sqs_member = sqs_member.filter(
|
||||||
SQ(firstname__icontains=self.cleaned_data["q"])
|
SQ(firstname__icontains=self.cleaned_data["q"])
|
||||||
| SQ(surname__icontains=self.cleaned_data["q"])
|
| SQ(surname__icontains=self.cleaned_data["q"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
sqs_post = self.searchqueryset.models(Post).filter(status="20")
|
sqs_post = self.searchqueryset.models(Post).filter(status="20")
|
||||||
sqs_post = sqs_post.filter(
|
sqs_post = sqs_post.filter(
|
||||||
SQ(title__icontains=self.cleaned_data["q"])
|
SQ(title__icontains=self.cleaned_data["q"])
|
||||||
| SQ(body__icontains=self.cleaned_data["q"])
|
| SQ(body__icontains=self.cleaned_data["q"])
|
||||||
| SQ(agenda__icontains=self.cleaned_data["q"])
|
| SQ(agenda__icontains=self.cleaned_data["q"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
tmp_results = deque([])
|
tmp_results = deque([])
|
||||||
|
|||||||
@@ -18,4 +18,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
{% extends "admin/base.html" %}
|
{% extends "admin/base.html" %}
|
||||||
{% load i18n admin_urls %}
|
{% load admin_urls i18n static %}
|
||||||
{% load static %}
|
|
||||||
|
|
||||||
{% block extrahead %}
|
{% block extrahead %}
|
||||||
<link rel="shortcut icon" type="image/png" href="{% static 'img/fet_logo_white.png' %}"/>
|
<link rel="shortcut icon" type="image/png" href="{% static 'img/fet_logo_white.png' %}"/>
|
||||||
{% endblock %}
|
{% endblock extrahead %}
|
||||||
|
|
||||||
{% block usertools %}
|
{% block usertools %}
|
||||||
{% if has_permission %}
|
{% if has_permission %}
|
||||||
@@ -12,14 +11,14 @@
|
|||||||
{% block welcome-msg %}
|
{% block welcome-msg %}
|
||||||
{% translate 'Welcome,' %}
|
{% translate 'Welcome,' %}
|
||||||
<strong>{% firstof user.get_short_name user.get_username %}</strong>.
|
<strong>{% firstof user.get_short_name user.get_username %}</strong>.
|
||||||
{% endblock %}
|
{% endblock welcome-msg %}
|
||||||
{% block userlinks %}
|
{% block userlinks %}
|
||||||
{% if site_url %}
|
{% if site_url %}
|
||||||
<a class="button" href="{{ site_url }}">Zurück zur FET Homepage</a>
|
<a class="button" href="{{ site_url }}">Zurück zur FET Homepage</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="button" href="{% url 'authentications:password_change' %}">Passwort ändern</a>
|
<!-- TODO: FIXME: <a class="button" href="{% url 'authentications:password_change' %}">Passwort ändern</a> -->
|
||||||
<a class="button" href="{% url 'admin:logout' %}">{% translate 'Log out' %}</a>
|
<a class="button" href="{% url 'admin:logout' %}">{% translate 'Log out' %}</a>
|
||||||
{% endblock %}
|
{% endblock userlinks %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock usertools %}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{% extends "admin/base_site.html" %}
|
{% extends "admin/base_site.html" %}
|
||||||
|
|
||||||
{% block title %}{{ site_title }}: {{ title }} {% endblock %}
|
{% block title %}{{ site_title }}: {{ title }}{% endblock %}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{% extends "admin/change_form.html" %}
|
{% extends "admin/change_form.html" %}
|
||||||
{% load i18n admin_urls %}
|
{% load admin_urls i18n %}
|
||||||
|
|
||||||
{% block form_top %}
|
{% block form_top %}
|
||||||
{% if help_text %}<p>{{ help_text }}</p>{% endif %}
|
{% if help_text %}<p>{{ help_text }}</p>{% endif %}
|
||||||
{% endblock %}
|
{% endblock form_top %}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{% extends "admin/submit_line.html" %}
|
{% extends "admin/submit_line.html" %}
|
||||||
{% load i18n admin_urls %}
|
{% load admin_urls i18n %}
|
||||||
|
|
||||||
{% block submit-row %}
|
{% block submit-row %}
|
||||||
{% if not show_close %}
|
{% if not show_close %}
|
||||||
@@ -8,4 +8,4 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if generate_pdf %}<input type="submit" value="PDF File generieren" class="default" name="_generate_pdf">{% endif %}
|
{% if generate_pdf %}<input type="submit" value="PDF File generieren" class="default" name="_generate_pdf">{% endif %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
{% endblock %}
|
{% endblock submit-row %}
|
||||||
|
|||||||
@@ -26,4 +26,4 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -12,4 +12,4 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -28,4 +28,4 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
{% load flatpages %}
|
{% load flatpages static version %}
|
||||||
{% load static %}
|
|
||||||
{% load version %}
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="de" x-data="bodyData" x-bind="documentRoot">
|
<html lang="de" x-data="bodyData" x-bind="documentRoot">
|
||||||
@@ -50,9 +48,9 @@
|
|||||||
<link rel="stylesheet" href="{% static 'fonts/Fira_Code-6.2/fira_code.css' %}">
|
<link rel="stylesheet" href="{% static 'fonts/Fira_Code-6.2/fira_code.css' %}">
|
||||||
|
|
||||||
{% block galleryheader %}
|
{% block galleryheader %}
|
||||||
{% endblock %}
|
{% endblock galleryheader %}
|
||||||
{% block extraheader %}
|
{% block extraheader %}
|
||||||
{% endblock %}
|
{% endblock extraheader %}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body x-bind="documentBody">
|
<body x-bind="documentBody">
|
||||||
@@ -203,7 +201,7 @@
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<ul class="icon-list">
|
<ul class="icon-list">
|
||||||
|
|||||||
@@ -75,4 +75,4 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@@ -14,10 +13,10 @@
|
|||||||
<div id="nav"><nav>
|
<div id="nav"><nav>
|
||||||
{% block nav %}
|
{% block nav %}
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<a class="nav" href="/accounts/profile/">{% trans "profile" %}</a>
|
<a class="nav" href="/accounts/profile/">{% translate "profile" %}</a>
|
||||||
<a class="nav" href="/logout">{% trans "logout" %}</a>
|
<a class="nav" href="/logout">{% translate "logout" %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock nav %}
|
||||||
</nav></div>
|
</nav></div>
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
{% block wrapper %}
|
{% block wrapper %}
|
||||||
@@ -31,7 +30,7 @@
|
|||||||
<span class="mark">!</span>{{ message.text }}<span class="mark">!</span>
|
<span class="mark">!</span>{{ message.text }}<span class="mark">!</span>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endblock %}
|
{% endblock messages %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
@@ -42,9 +41,8 @@
|
|||||||
<div id="footer"><footer>
|
<div id="footer"><footer>
|
||||||
</footer></div>
|
</footer></div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock wrapper %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
{% extends "documents/base.html" %}
|
{% extends "documents/base.html" %}
|
||||||
|
|
||||||
|
{% block title %}{{ pad.name }}{% endblock %}
|
||||||
{% block title %}{{pad.name}}{% endblock %}
|
|
||||||
|
|
||||||
{% block wrapper %}
|
{% block wrapper %}
|
||||||
{% if error %}
|
{% if error %}
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<div id="errors">
|
<div id="errors">
|
||||||
<h2>Errors:</h2>
|
<h2>Errors:</h2>
|
||||||
<p>{{error}}</p>
|
<p>{{ error }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<iframe src="{{link}}?userName={{uname}}" style="height: 95%; width: 100%; min-height: 500px; display: block"></iframe>
|
<iframe src="{{ link }}?userName={{ uname }}" style="height: 95%; width: 100%; min-height: 500px; display: block"></iframe>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
/**
|
/**
|
||||||
@@ -31,4 +30,4 @@
|
|||||||
</script>
|
</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock wrapper %}
|
||||||
|
|||||||
@@ -103,4 +103,4 @@
|
|||||||
</section>
|
</section>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<main class="container mx-auto w-full px-4 my-8 flex-1">
|
<main class="container mx-auto w-full px-4 my-8 flex-1">
|
||||||
<section class="w-full text-center">
|
<section class="w-full text-center">
|
||||||
<p class="mt-6 text-gray-900 dark:text-gray-100">Rechnung #{{pk}} wurde erfolgreich eingereicht.</p>
|
<p class="mt-6 text-gray-900 dark:text-gray-100">Rechnung #{{ pk }} wurde erfolgreich eingereicht.</p>
|
||||||
<div class="mt-10 flex items-center justify-center">
|
<div class="mt-10 flex items-center justify-center">
|
||||||
<a href="{% url 'home' %}" type="submit" class="block btn btn-primary">Zur Startseite</a>
|
<a href="{% url 'home' %}" type="submit" class="block btn btn-primary">Zur Startseite</a>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -178,4 +178,4 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -71,4 +71,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -35,4 +35,4 @@
|
|||||||
</section>
|
</section>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -57,4 +57,4 @@
|
|||||||
</a>
|
</a>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
{% for result in object_list %}
|
{% for result in object_list %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-right">{{ result.id }}</td>
|
<td class="text-right">{{ result.id }}</td>
|
||||||
<td class="text-blue-700 dark:text-blue-200 no-underline hover:underline"><a href="{%url 'finance:resolution_detail' result.id %}">{{ result.name }}</a></td>
|
<td class="text-blue-700 dark:text-blue-200 no-underline hover:underline"><a href="{% url 'finance:resolution_detail' result.id %}">{{ result.name }}</a></td>
|
||||||
<!--<td class="text-left">{{ result.voting_text|truncatechars:300 }}</td>-->
|
<!--<td class="text-left">{{ result.voting_text|truncatechars:300 }}</td>-->
|
||||||
<td class="text-left">{{ result.voting }}</td>
|
<td class="text-left">{{ result.voting }}</td>
|
||||||
<td class="text-left">{{ result.get_option_display }}</td>
|
<td class="text-left">{{ result.get_option_display }}</td>
|
||||||
@@ -65,4 +65,4 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -35,4 +35,4 @@
|
|||||||
</section>
|
</section>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -17,4 +17,4 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -77,4 +77,4 @@
|
|||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
{% block galleryheader %}
|
{% block galleryheader %}
|
||||||
<link rel="stylesheet" href="{% static 'Gallery-3.4.0/css/blueimp-gallery.min.css' %}">
|
<link rel="stylesheet" href="{% static 'Gallery-3.4.0/css/blueimp-gallery.min.css' %}">
|
||||||
{% endblock %}
|
{% endblock galleryheader %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
@@ -77,4 +77,4 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -31,4 +31,4 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -121,4 +121,4 @@
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -20,4 +20,4 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -61,4 +61,4 @@
|
|||||||
<a href="{% url 'intern:attachment_update' attachment.topic.topic_group.slug attachment.topic.slug attachment.slug %}" class="btn btn-primary block place-self-end"><i class="fa-solid fa-pen-to-square mr-2"></i>Beschreibung bearbeiten</a>
|
<a href="{% url 'intern:attachment_update' attachment.topic.topic_group.slug attachment.topic.slug attachment.slug %}" class="btn btn-primary block place-self-end"><i class="fa-solid fa-pen-to-square mr-2"></i>Beschreibung bearbeiten</a>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -20,4 +20,4 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -20,4 +20,4 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -20,4 +20,4 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -92,4 +92,4 @@
|
|||||||
<a href="{% url 'admin:intern_topicgroup_add' %}" class="btn btn-primary block w-full"><i class="fa-solid fa-plus-square mr-2"></i>Neuer Themenbereich</a>
|
<a href="{% url 'admin:intern_topicgroup_add' %}" class="btn btn-primary block w-full"><i class="fa-solid fa-plus-square mr-2"></i>Neuer Themenbereich</a>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -20,4 +20,4 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -36,4 +36,4 @@
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -24,4 +24,4 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% load flatpages %}
|
{% load flatpages job_groups softhyphen_tags static %}
|
||||||
{% load job_groups %}
|
|
||||||
{% load softhyphen_tags %}
|
|
||||||
{% load static %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
@@ -106,21 +103,21 @@
|
|||||||
{% if member %}
|
{% if member %}
|
||||||
<!-- show details of a member -->
|
<!-- show details of a member -->
|
||||||
{% block member_content %}
|
{% block member_content %}
|
||||||
{% endblock %}
|
{% endblock member_content %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if members %}
|
{% if members %}
|
||||||
<!-- show all, active or pension members -->
|
<!-- show all, active or pension members -->
|
||||||
{% block members_content %}
|
{% block members_content %}
|
||||||
{% endblock %}
|
{% endblock members_content %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if job_members %}
|
{% if job_members %}
|
||||||
<!-- show job lists in a job group -->
|
<!-- show job lists in a job group -->
|
||||||
{% block jobs_content %}
|
{% block jobs_content %}
|
||||||
{% endblock %}
|
{% endblock jobs_content %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -36,4 +36,4 @@
|
|||||||
</article>
|
</article>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock jobs_content %}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<meta content="{{ member.firstname }}" property="og:profile:first_name">
|
<meta content="{{ member.firstname }}" property="og:profile:first_name">
|
||||||
<meta content="{{ member.surname }}" property="og:profile:last_name">
|
<meta content="{{ member.surname }}" property="og:profile:last_name">
|
||||||
<meta content="profile" property="og:type">
|
<meta content="profile" property="og:type">
|
||||||
{% endblock %}
|
{% endblock extraheader %}
|
||||||
|
|
||||||
{% block member_content %}
|
{% block member_content %}
|
||||||
<section class="flex-grow w-full max-w-prose my-8 sm:my-0 text-gray-800 dark:text-gray-300">
|
<section class="flex-grow w-full max-w-prose my-8 sm:my-0 text-gray-800 dark:text-gray-300">
|
||||||
@@ -70,4 +70,4 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock member_content %}
|
||||||
|
|||||||
@@ -26,4 +26,4 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
{% endblock %}
|
{% endblock members_content %}
|
||||||
|
|||||||
@@ -5,9 +5,11 @@
|
|||||||
{% block title %}{{ post.title }}{% endblock %}
|
{% block title %}{{ post.title }}{% endblock %}
|
||||||
|
|
||||||
{% block prev_text_big %}Vorheriges<br>Event{% endblock %}
|
{% block prev_text_big %}Vorheriges<br>Event{% endblock %}
|
||||||
|
|
||||||
{% block next_text_big %}Nächstes<br>Event{% endblock %}
|
{% block next_text_big %}Nächstes<br>Event{% endblock %}
|
||||||
|
|
||||||
{% block prev_text %}Vorheriges Event{% endblock %}
|
{% block prev_text %}Vorheriges Event{% endblock %}
|
||||||
|
|
||||||
{% block next_text %}Nächstes Event{% endblock %}
|
{% block next_text %}Nächstes Event{% endblock %}
|
||||||
|
|
||||||
{% block update_button_desktop %}
|
{% block update_button_desktop %}
|
||||||
@@ -16,7 +18,7 @@
|
|||||||
<i class="fa-solid fa-pen-to-square mr-1"></i>Event bearbeiten
|
<i class="fa-solid fa-pen-to-square mr-1"></i>Event bearbeiten
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock update_button_desktop %}
|
||||||
|
|
||||||
{% block event_details_desktop %}
|
{% block event_details_desktop %}
|
||||||
<div class="hidden lg:block absolute top-0 right-0 bg-white dark:bg-gray-950 rounded-bl p-2 bg-opacity-60 dark:bg-opacity-60 gap-2 backdrop-blur">
|
<div class="hidden lg:block absolute top-0 right-0 bg-white dark:bg-gray-950 rounded-bl p-2 bg-opacity-60 dark:bg-opacity-60 gap-2 backdrop-blur">
|
||||||
@@ -46,13 +48,13 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock event_details_desktop %}
|
||||||
|
|
||||||
{% block post_body %}
|
{% block post_body %}
|
||||||
{% if post.body %}
|
{% if post.body %}
|
||||||
{{ post.body|safe|tags_to_url }}
|
{{ post.body|safe|tags_to_url }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock post_body %}
|
||||||
|
|
||||||
{% block event_details_mobile %}
|
{% block event_details_mobile %}
|
||||||
<hr class="lg:hidden -mx-4 border-gray-200 dark:border-gray-800 dark:border my-4">
|
<hr class="lg:hidden -mx-4 border-gray-200 dark:border-gray-800 dark:border my-4">
|
||||||
@@ -66,7 +68,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock event_details_mobile %}
|
||||||
|
|
||||||
{% block update_button_mobile %}
|
{% block update_button_mobile %}
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
@@ -74,4 +76,4 @@
|
|||||||
<i class="fa-solid fa-pen-to-square mr-1"></i>Event bearbeiten
|
<i class="fa-solid fa-pen-to-square mr-1"></i>Event bearbeiten
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock update_button_mobile %}
|
||||||
|
|||||||
@@ -27,4 +27,4 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -26,4 +26,4 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -5,9 +5,11 @@
|
|||||||
{% block title %}{{ post.title }} vom {{ post.event_start|date }}{% endblock %}
|
{% block title %}{{ post.title }} vom {{ post.event_start|date }}{% endblock %}
|
||||||
|
|
||||||
{% block prev_text_big %}Vorherige<br>Sitzung{% endblock %}
|
{% block prev_text_big %}Vorherige<br>Sitzung{% endblock %}
|
||||||
|
|
||||||
{% block next_text_big %}Nächste<br>Sitzung{% endblock %}
|
{% block next_text_big %}Nächste<br>Sitzung{% endblock %}
|
||||||
|
|
||||||
{% block prev_text %}Vorherige Sitzung{% endblock %}
|
{% block prev_text %}Vorherige Sitzung{% endblock %}
|
||||||
|
|
||||||
{% block next_text %}Nächste Sitzung{% endblock %}
|
{% block next_text %}Nächste Sitzung{% endblock %}
|
||||||
|
|
||||||
{% block update_button_desktop %}
|
{% block update_button_desktop %}
|
||||||
@@ -16,7 +18,7 @@
|
|||||||
<i class="fa-solid fa-pen-to-square mr-1"></i>FET Sitzung bearbeiten
|
<i class="fa-solid fa-pen-to-square mr-1"></i>FET Sitzung bearbeiten
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock update_button_desktop %}
|
||||||
|
|
||||||
{% block event_details_desktop %}
|
{% block event_details_desktop %}
|
||||||
<div class="hidden lg:block absolute top-0 right-0 bg-white dark:bg-gray-950 rounded-bl p-2 bg-opacity-60 dark:bg-opacity-60 gap-2 backdrop-blur">
|
<div class="hidden lg:block absolute top-0 right-0 bg-white dark:bg-gray-950 rounded-bl p-2 bg-opacity-60 dark:bg-opacity-60 gap-2 backdrop-blur">
|
||||||
@@ -46,7 +48,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock event_details_desktop %}
|
||||||
|
|
||||||
{% block post_body %}
|
{% block post_body %}
|
||||||
{% if post.has_agenda and post.agenda_html %}
|
{% if post.has_agenda and post.agenda_html %}
|
||||||
@@ -59,7 +61,7 @@
|
|||||||
<h2>Protokoll</h2>
|
<h2>Protokoll</h2>
|
||||||
{{ post.protocol_html|safe }}
|
{{ post.protocol_html|safe }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock post_body %}
|
||||||
|
|
||||||
{% block event_details_mobile %}
|
{% block event_details_mobile %}
|
||||||
<hr class="lg:hidden -mx-4 border-gray-200 dark:border-gray-800 dark:border my-4">
|
<hr class="lg:hidden -mx-4 border-gray-200 dark:border-gray-800 dark:border my-4">
|
||||||
@@ -73,7 +75,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock event_details_mobile %}
|
||||||
|
|
||||||
{% block docu_buttons %}
|
{% block docu_buttons %}
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
@@ -176,7 +178,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock docu_buttons %}
|
||||||
|
|
||||||
{% block update_button_mobile %}
|
{% block update_button_mobile %}
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
@@ -184,4 +186,4 @@
|
|||||||
<i class="fa-solid fa-pen-to-square mr-1"></i>FET Sitzung bearbeiten
|
<i class="fa-solid fa-pen-to-square mr-1"></i>FET Sitzung bearbeiten
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock update_button_mobile %}
|
||||||
|
|||||||
@@ -25,4 +25,4 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -116,4 +116,4 @@
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -10,13 +10,13 @@
|
|||||||
<i class="fa-solid fa-pen-to-square mr-1"></i>Artikel bearbeiten
|
<i class="fa-solid fa-pen-to-square mr-1"></i>Artikel bearbeiten
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock update_button_desktop %}
|
||||||
|
|
||||||
{% block post_body %}
|
{% block post_body %}
|
||||||
{% if post.body %}
|
{% if post.body %}
|
||||||
{{ post.body|safe|tags_to_url }}
|
{{ post.body|safe|tags_to_url }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock post_body %}
|
||||||
|
|
||||||
{% block files_buttons %}
|
{% block files_buttons %}
|
||||||
{% if files %}
|
{% if files %}
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock files_buttons %}
|
||||||
|
|
||||||
{% block update_button_mobile %}
|
{% block update_button_mobile %}
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
@@ -59,4 +59,4 @@
|
|||||||
<i class="fa-solid fa-pen-to-square mr-1"></i>Artikel bearbeiten
|
<i class="fa-solid fa-pen-to-square mr-1"></i>Artikel bearbeiten
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock update_button_mobile %}
|
||||||
|
|||||||
@@ -23,4 +23,4 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<meta content="{{ post.title }}" property="og:title">
|
<meta content="{{ post.title }}" property="og:title">
|
||||||
<meta content="article" property="og:type">
|
<meta content="article" property="og:type">
|
||||||
<meta content="" property="og:url">
|
<meta content="" property="og:url">
|
||||||
{% endblock %}
|
{% endblock extraheader %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
@@ -81,12 +81,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% block update_button_desktop %}
|
{% block update_button_desktop %}
|
||||||
{% endblock %}
|
{% endblock update_button_desktop %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative w-full h-44 sm:h-56 md:h-60 lg:h-64 xl:h-96 bg-center bg-no-repeat bg-cover sm:rounded-md mx-auto" style="background-image: url('{{ post.imageurl }}');">
|
<div class="relative w-full h-44 sm:h-56 md:h-60 lg:h-64 xl:h-96 bg-center bg-no-repeat bg-cover sm:rounded-md mx-auto" style="background-image: url('{{ post.imageurl }}');">
|
||||||
{% block event_details_desktop %}
|
{% block event_details_desktop %}
|
||||||
{% endblock %}
|
{% endblock event_details_desktop %}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="mx-4 z-10">
|
<section class="mx-4 z-10">
|
||||||
@@ -94,17 +94,17 @@
|
|||||||
<div class="db-page-content-left">
|
<div class="db-page-content-left">
|
||||||
<!-- Content from DB here: -->
|
<!-- Content from DB here: -->
|
||||||
{% block post_body %}
|
{% block post_body %}
|
||||||
{% endblock %}
|
{% endblock post_body %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% block event_details_mobile %}
|
{% block event_details_mobile %}
|
||||||
{% endblock %}
|
{% endblock event_details_mobile %}
|
||||||
|
|
||||||
{% block docu_buttons %}
|
{% block docu_buttons %}
|
||||||
{% endblock %}
|
{% endblock docu_buttons %}
|
||||||
|
|
||||||
{% block files_buttons %}
|
{% block files_buttons %}
|
||||||
{% endblock %}
|
{% endblock files_buttons %}
|
||||||
|
|
||||||
<hr class="-mx-4 border-gray-200 dark:border-gray-800 dark:border my-4">
|
<hr class="-mx-4 border-gray-200 dark:border-gray-800 dark:border my-4">
|
||||||
<div class="-m-4 flex divide-x divide-gray-200 dark:divide-gray-800 dark:divide-x-2 text-sm sm:text-base">
|
<div class="-m-4 flex divide-x divide-gray-200 dark:divide-gray-800 dark:divide-x-2 text-sm sm:text-base">
|
||||||
@@ -120,7 +120,7 @@
|
|||||||
</article>
|
</article>
|
||||||
|
|
||||||
{% block update_button_mobile %}
|
{% block update_button_mobile %}
|
||||||
{% endblock %}
|
{% endblock update_button_mobile %}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{% if related_posts and related_posts|length > 1 %}
|
{% if related_posts and related_posts|length > 1 %}
|
||||||
@@ -146,4 +146,4 @@
|
|||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -14,4 +14,4 @@
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -45,4 +45,4 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock content %}
|
||||||
|
|||||||
188
lvalist.txt
188
lvalist.txt
@@ -1,188 +0,0 @@
|
|||||||
0***Referenzordner_SE_123.456***
|
|
||||||
0***Referenzordner_SE_123.456_Multimedia_only***
|
|
||||||
3D_Vision_UE_183.130
|
|
||||||
aa_92_Studienplan_nicht_veraendern
|
|
||||||
aaa Richtlinien für Ordnerstruktur~
|
|
||||||
aaa_Richtlinien für Ordnerstruktur~
|
|
||||||
aaa_Richtlinien für Ordnerstruktur3.txt~
|
|
||||||
aaa_Richtlinien für Ordnerstruktur.txt~
|
|
||||||
aa_Bachelor-Vertiefungen
|
|
||||||
aa_Freifach
|
|
||||||
aa_Master-Vertiefungen
|
|
||||||
aa_Skripten_und_Bücher
|
|
||||||
aa_sonstiges
|
|
||||||
Advanced_Wireless_Communications_2_VO_389.172
|
|
||||||
Advanced_Wireless_Communications_2_VO_389 .172_Multimedia_only
|
|
||||||
AKmath_VU_101.748
|
|
||||||
Algorithmen_und_Datenstrukturen_2_VU_186.866
|
|
||||||
Analoge_Integrierte_Schaltungen_VU
|
|
||||||
Anatomy_and_Histology_VO_185.330
|
|
||||||
Aufbau_hochdynamischer_Drehstromantriebe_LU_372.560
|
|
||||||
Automatisierungs_und_Steuerungssysteme_LU
|
|
||||||
Automatisierungs_und_Steuerungssysteme_LU_Multimedia_only
|
|
||||||
Automatisierungs_und_Steuerungssysteme_VO
|
|
||||||
Automatisierungs_und_Steuerungssysteme_VO_Multimedia_only
|
|
||||||
Automatisierung_VU_376.000
|
|
||||||
Automatisierung_VU_376.000_Multimedia_only
|
|
||||||
Betriebssysteme_VO_182.711
|
|
||||||
Biomedical_Sensors_and_Signals_VO_351.029
|
|
||||||
Biophysik_VO_362.111
|
|
||||||
Bitte_lies_mich.txt
|
|
||||||
Codegeneratoren_VO_185.416__Multimedia_only
|
|
||||||
Communication_Networks_1_VO_389.158
|
|
||||||
Communications_Networks_2_VU_389.165
|
|
||||||
Computerunterstütztes_Japanisch_1_VO_057.011
|
|
||||||
Datenkommunikation_VO_389.153
|
|
||||||
Deterministische_Signalverarbeitung_VU
|
|
||||||
Digital_Communications_1_VU_389.163
|
|
||||||
Digital_Communications_2_VU_389.164
|
|
||||||
Digitale_Integrierte_Schaltungen_VU_384.086
|
|
||||||
Digitale_Systeme_UE
|
|
||||||
Echtzeitsysteme_VO_182.713
|
|
||||||
Einführung_in_die_Betriebswirtschaftslehre_VO_(WU)_xxx.xxx
|
|
||||||
Einführung_in_die_Betriebswirtschaftslehre_VO_(WU)_xxx.xxx_Multimedia_only
|
|
||||||
Elektrische_Antriebe_Labor_UE
|
|
||||||
Elektrische_Antriebe_VU_370.027
|
|
||||||
Elektrische_Antriebe_VU_370.027_Mulitmedia
|
|
||||||
Elektrische_Maschinen_VO_372.025
|
|
||||||
Elektrochemische_Energieumwandlung_und_Energiespeicherung_VO_164.288
|
|
||||||
Elektrochemische_Messtechniken_und_Untersuchungsmethoden_VO_164.256
|
|
||||||
Elektrochemische_Messtechniken_und_Untersuchungsmethoden_VO_164.256_Multimedia_only
|
|
||||||
Elektrodynamik_VU_354.077
|
|
||||||
Elektrodynamik_VU_354.077_Multimedia_only
|
|
||||||
Elektronische_Bauelemente_VU_362.072
|
|
||||||
Elektrotechnik_1_UE_351.009
|
|
||||||
Elektrotechnik_1_VO_351.008
|
|
||||||
Elektrotechnik_2_UE_351.012
|
|
||||||
Elektrotechnik_2_VO_351.011
|
|
||||||
Elektrotechnik_2_VO_351.011_Multimedia_only
|
|
||||||
Embedded_Systems_in_FPGAs_VU_384.154
|
|
||||||
EMVgerechter_Schaltungsentwurf_UE_372.015
|
|
||||||
EMVgerechter_Schaltungsentwurf__VO_370.030
|
|
||||||
EMV_und_Netzrückwirkungen_VU
|
|
||||||
Energiemodelle_und_Analysen_VU
|
|
||||||
Energieoekonomie_VU_373.010
|
|
||||||
Energiesysteme_und_Netze_VO_370.021
|
|
||||||
Energieübertragung_und_Hochspannungstechnik_VO_370.028
|
|
||||||
Energieübertragung_und_Hochspannungstechnik_VO_370.028_Multimedia_only
|
|
||||||
Energieversorgung_VU_370.002
|
|
||||||
Entsorgung_und_Recycling_in_der_Elektrotechnik_VO_355.674
|
|
||||||
et-bachelor.zip
|
|
||||||
European_Union_VO_164.287
|
|
||||||
Fachvertiefung_Antriebstechnik_VU_372.750
|
|
||||||
Fachvertiefung_Automatisierung_VU_376.042
|
|
||||||
Fachvertiefung_Biophysik_VU_362.138
|
|
||||||
Fachvertiefung_Energiesysteme_VU_370.007
|
|
||||||
Fachvertiefung_Mathematik_VU_101.440
|
|
||||||
Fachvertiefung_Mikroelektronik_Bauelemente_Labor_VU_362.136
|
|
||||||
Fachvertiefung_Signale_und_Systeme_VU_389.142
|
|
||||||
Fachvertiefung_Softwareentwicklung_3VU_84.141
|
|
||||||
Fachvertiefung_Telekomunikation_VU_389.141
|
|
||||||
Gewerblicher_Rechtschutz_für_Techniker_VO_360.012
|
|
||||||
Grundlagen_der_Betriebs_und_Unternehmensführung_VO_330.001
|
|
||||||
Grundlagen_der_elektrischen_Bahnen_VO_371.816
|
|
||||||
Halbleiterelektronik_VO_362.142
|
|
||||||
Halbleiterphysik_VU_362.069
|
|
||||||
Human_Machine_Interaction_VO_384.160
|
|
||||||
Industrielle_Kommunikationstechnik_VO_384.168
|
|
||||||
Integrierte_Bauelemente_VU
|
|
||||||
Integrierte_Schaltungstechnik_VO
|
|
||||||
Introduction_into_Biophysics_VO
|
|
||||||
IT_Projektplanung_und_Vergaberecht_VO_384.107
|
|
||||||
KFZ-Technik_VO_315.282
|
|
||||||
Kraftwerke_VO_370.026
|
|
||||||
Labor_Energieversorgung_UE_370.024
|
|
||||||
Laser_in_der_Medizintechnik_VU
|
|
||||||
Leistungselektronik_und_Stromrichtertechnik_VU_372.033
|
|
||||||
Leistungselektronik_und_Stromrichtertechnik_VU_372.033_Multimedia_only
|
|
||||||
Machine_Vision_and_Cognitive_Robotics_VU
|
|
||||||
Machine_Vision_and_Cognitive_Robotics_VU_MultimediaOnly
|
|
||||||
Maschinen_und_Antriebe_VU_370.015
|
|
||||||
Mathematik_1_UE
|
|
||||||
Mathematik_1_VO
|
|
||||||
Mathematik_2_UE_101.683
|
|
||||||
Mathematik_2_VO_101.682
|
|
||||||
Mathematik_3_UE_101.686
|
|
||||||
Mathematik_3_VO_101.685
|
|
||||||
Mathematik_3_VU_(-2017)
|
|
||||||
Mathematische_Methoden_der_Modellbildung_und_Simulation_VL_101.555
|
|
||||||
Mechatronische_Systeme_LU
|
|
||||||
Mechatronische_Systeme_VO
|
|
||||||
Mechatronische_Systeme_VO_Multimediaonly
|
|
||||||
Messtechnik_Labor_LU
|
|
||||||
Messtechnik_VU
|
|
||||||
Messtechnik_VU_old stuff
|
|
||||||
Mikrocomputer_Labor_LU_384.996
|
|
||||||
Mikrocomputer_VU_384.173
|
|
||||||
Mikroelektronische_Konzepte_fuer_Biomedizinische_Interfaces_VU
|
|
||||||
Modellbildung_VU
|
|
||||||
Modellierung_Elektronischer_Bauelemente_VU
|
|
||||||
Nachhaltige_Energietraeger_VO_141.217
|
|
||||||
Network_Security_VU_389.159
|
|
||||||
Nutzung_der_Sonnenenergie_VO_372.383
|
|
||||||
Objektorientiertes_Programmieren_VU
|
|
||||||
Optimierung_VU
|
|
||||||
Optimierung_VU_Multimedia
|
|
||||||
Optische_Messtechnik_VU
|
|
||||||
Optische_Nachrichtentechnik_VO
|
|
||||||
Optische_Systeme_VO_387.028
|
|
||||||
Optoelektronische_inegrierte_Schaltungen_VO
|
|
||||||
Parameter_Estimation_Methods_VO_389.119
|
|
||||||
Photonik_1_VO_387.026
|
|
||||||
Photonik_2_VU_387.068
|
|
||||||
Physik_UE_141.A23
|
|
||||||
Physik_VO_141.A19
|
|
||||||
Privates_Wirtschaftsrecht_SoftSkill
|
|
||||||
Programmieren_1_VU
|
|
||||||
Programmieren_2_VU
|
|
||||||
Projektmanagement_VO
|
|
||||||
Prozesschemie_für_Mikro_und_Nanoelektronik_VU_362.149
|
|
||||||
Prozesse_und_Verfahren_VO
|
|
||||||
Quantenelektronik_VO_360.227
|
|
||||||
Regelungssysteme_1_VO
|
|
||||||
Regelungssysteme_1_VO_MultimediaOnly
|
|
||||||
Regelungssysteme_2_VO
|
|
||||||
Regelungssysteme_2_VO_MultimediaOnly
|
|
||||||
Regelungssysteme_Labor
|
|
||||||
Regenerative_Energiesysteme_VO_370_035
|
|
||||||
Ressourceneffizienz_VO_330.262
|
|
||||||
RF_Techniques_VU_354.058
|
|
||||||
Robotik_und_Automatisierung_in_der_KFZ-Elektronik_VO
|
|
||||||
Schaltnetzteile_1_WS
|
|
||||||
Schaltnetzteile_2_SS
|
|
||||||
Schaltungstechnik_VU_354.019
|
|
||||||
Schutztechnik_in_elektrischen_Netzen_VO_370.045
|
|
||||||
Selected_Topics_in_Energy_Economics_and_Environment_370043_VU
|
|
||||||
Sensoren_und_optoelektronische_Bauelemente_VO
|
|
||||||
Sensorik_und_Sensorsysteme_VO
|
|
||||||
Sensorik_und_Sensorsysteme_VO (copy)
|
|
||||||
Sensorik_VU
|
|
||||||
Signale_und_Systeme_1_unsortiert
|
|
||||||
Signale_und_Systeme_1_VU_387.083_neu
|
|
||||||
Signale_und_Systeme_1_VU_alt_351.015
|
|
||||||
Signale_und_Systeme_1_VU_alt_351.015_Multimedia_Only
|
|
||||||
Signale_und_Systeme_2_VU_389.055
|
|
||||||
Signal_Processing_1_VU_389.166
|
|
||||||
Signal_Processing_2_VU_389.170
|
|
||||||
Simulation_elektrischer_Maschinen_und_Antriebe_372.023
|
|
||||||
Smart_Grids_Vertiefung_VU_370.033
|
|
||||||
Smart_Grids_VO_384.146
|
|
||||||
SoC_Architektur_und_Design_VU_384.156
|
|
||||||
Software_and_System_Engineering_VO_384.165
|
|
||||||
Software_Engineering_1_VU
|
|
||||||
Systemtechnik_in_der_Automation_VU
|
|
||||||
Technik_und_Gesellschaft_VO_351.018
|
|
||||||
Technische_Elektronik_LU_362.132
|
|
||||||
Technologie_der_Funktionswerkstoffe_VO
|
|
||||||
telekom
|
|
||||||
Telekommunikation_VU_389.138
|
|
||||||
Telekommunikation_VU_389.138_Multimedia_Only
|
|
||||||
Theoretische_Informatik_und_Logik_für_Elektrotechnik_185.A84_VU
|
|
||||||
Vertiefung_Sozialkompetenz_und_Impulsalgorithmen_VU
|
|
||||||
Videoverarbeitung_VO_188.329
|
|
||||||
Wellenausbreitung_VU_389.064
|
|
||||||
Wellenausbreitung_VU_389.064_Multimedia_Only
|
|
||||||
Werkstoffe_VU
|
|
||||||
Wireless_Communications_1_VU_389.157
|
|
||||||
Wireless_OFDM_systems_VO_389.133
|
|
||||||
Wirtschaft_1_VU
|
|
||||||
Reference in New Issue
Block a user