merge settings.py

This commit is contained in:
root (fetsite21)
2021-01-19 19:31:57 +01:00
61 changed files with 1415 additions and 750 deletions

View File

@@ -0,0 +1,2 @@
from nginx:alpine
copy nginxdev.conf /etc/nginx/conf.d/default.conf

View File

@@ -0,0 +1,3 @@
cp ../nginxdev.conf .
docker build . -t docker.triton2.fet.at/nginxdev-fet2020:latest
docker image push docker.triton2.fet.at/nginxdev-fet2020:latest

View File

@@ -0,0 +1,67 @@
server {
listen 8080;
error_log /var/log/nginx/error.log notice;
rewrite_log on;
resolver 127.0.0.11 valid=30s;
set $theia theia;
set $flaskfetfotos "flaskfetfotos:8080";
set $etherpad "etherpad:9001";
location /fotos {
proxy_ssl_server_name on;
proxy_ssl_verify off;
proxy_pass http://$flaskfetfotos;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
# proxy_set_header REMOTE-USER $http_REMOTE_USER;
# proxy_set_header X-Forwarded-User $http_REMOTE_USER;
# proxy_set_header x-forwarded-user $http_REMOTE_USER;
proxy_set_header Connection "upgrade";
}
location /etherpad {
rewrite /etherpad/(.*) /$1 break;
rewrite ^/etherpad$ /etherpad/ permanent;
proxy_pass http://$etherpad;
proxy_redirect / /etherpad/;
proxy_set_header Host $host;
proxy_buffering off;
}
location /dev {
rewrite_log on;
rewrite /dev/(.*) /$1 break;
rewrite ^/dev$ /dev/ permanent;
proxy_set_header Host $host;
proxy_set_header Proxy "";
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarde-Proto $scheme;
proxy_buffering off;
proxy_pass http://$theia:3000;
}
location / {
proxy_set_header Host $host;
proxy_set_header Proxy "";
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarde-Proto $scheme;
proxy_buffering off;
proxy_pass http://$theia:8000;
}
}

View File

@@ -0,0 +1,5 @@
from theiaide/theia-python:latest
run apt-get update && apt-get -y install libgs-dev
COPY ./requirements.txt .
run pip3 install --upgrade pip && pip3 install -r requirements.txt && pip3 install pytest pylint bandit flake8 black pytest-django six pytest-mock
ENTRYPOINT node /home/theia/src-gen/backend/main.js /home/project/.theia-workspace --hostname=0.0.0.0

View File

@@ -0,0 +1,3 @@
cp ../../fet2020/requirements.txt .
docker build . -t docker.triton2.fet.at/theia-fet2020:latest
docker image push docker.triton2.fet.at/theia-fet2020:latest

View File

@@ -0,0 +1,16 @@
django==3.1.4
django-ckeditor==6.0.0
django-crontab==0.7.1
django-environ==0.4.5
django-filter==2.4.0
django-static-jquery-ui==1.12.1.1
django-softhyphen==1.1.0
django-taggit==1.3.0
djangorestframework==3.12.2
configparser==5.0.1
docutils==0.16
easy-thumbnails==2.7.1
etherpad-lite==0.5
ghostscript==0.6
ldap3==2.8.1
mysqlclient==2.0.1

View File

@@ -1,4 +1,4 @@
version: "3"
version: "2"
services:
flaskfetfotos:
image: flask-fet-fotos
@@ -7,27 +7,16 @@ services:
FLASK_APP: main.py
pages_root: /app/data
volumes:
- /mnt/fotos/www:/app/data
mysql:
image: jbergstroem/mariadb-alpine
environment:
SKIP_INNODB: "yes"
MYSQL_DATABASE: fet2020db
MYSQL_USER: user
MYSQL_PASSWORD: hgu
MYSQL_COLLATION: utf8_general_ci
MYSQL_CHARSET: utf8
volumes:
- mysql-volume:/var/lib/mysql
- foto-data:/app/data
etherpadsql:
image: jbergstroem/mariadb-alpine
image: mariadb
environment:
SKIP_INNODB: "no"
MYSQL_DATABASE: etherpaddb
MYSQL_USER: user
MYSQL_PASSWORD: hgu
MYSQL_COLLATION: utf8_general_ci
MYSQL_CHARSET: utf8
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
volumes:
- ep-mysql-volume:/var/lib/mysql
etherpad:
@@ -40,38 +29,14 @@ services:
DB_USER: user
DB_PASS: hgu
DB_CHARSET: utf8
#ADMIN_PASSWORD: "AndiS"
#REQUIRE_AUTHENTICATION: "false"
TRUST_PROXY: "true"
REQUIRE_SESSION: "true"
#LOGLEVEL: "DEBUG"
REQUIRE_SESSION: "true"
depends_on:
- etherpadsql
volumes:
- ./etherpad/APIKEY.txt:/opt/etherpad-lite/APIKEY.txt
# restart: always
# fet2020:
# image: fet2020django
# build: .
# environment:
# HOST_NAME: andis.2020.fet.at
# DEBUG: "False"
# SECRET_KEY: arguiq3ebhnjo
# MYSQL_USER: user
# MYSQL_PASSWORD: hgu
# MYSQL_PORT: 3306
# depends_on:
# - mysql
# ports:
# - "8106:8080"
# volumes:
# - ./fet2020:/app
# - ./assets:/app/assets
# - ./etherpad:/app/etherpad
# - ./deployment/nginx.conf:/etc/nginx/conf.d/fet2020.conf
# restart: always
theia:
image: theiaide/theia-python:latest
image: docker.triton2.fet.at/theia-fet2020:latest
volumes:
- .:/home/project
- ./etherpad/APIKEY.txt:/srv/etherpad/APIKEY.txt
@@ -83,4 +48,6 @@ services:
- "8106:8080"
volumes:
ep-mysql-volume:
mysql-volume:
driver: local
foto-data:
driver: local

53
docker-compose.dev.yml Normal file
View File

@@ -0,0 +1,53 @@
version: "2"
services:
flaskfetfotos:
image: docker.triton2.fet.at/flask-fet-fotos:latest
environment:
FLASK_DEBUG: 0
FLASK_APP: main.py
pages_root: /app/data
etherpadsql:
image: mariadb
environment:
MYSQL_DATABASE: etherpaddb
MYSQL_USER: user
MYSQL_PASSWORD: hgu
MYSQL_COLLATION: utf8_general_ci
MYSQL_CHARSET: utf8
volumes:
- ep-mysql:/var/lib/mysql
etherpad:
image: etherpad/etherpad
environment:
DB_TYPE: mysql
DB_HOST: etherpadsql
DB_PORT: 3306
DB_NAME: etherpaddb
DB_USER: user
DB_PASS: hgu
DB_CHARSET: utf8
TRUST_PROXY: "true"
REQUIRE_SESSION: "true"
depends_on:
- etherpadsql
volumes:
- /srv/etherpad/APIKEY.txt:/opt/etherpad-lite/APIKEY.txt
theia:
image: docker.triton2.fet.at/theia-fet2020:latest
volumes:
- dev_data:/home/project
- /srv/etherpad/APIKEY.txt:/srv/etherpad/APIKEY.txt
- theia_usr:/usr/local
environment:
HOST_NAME: andis.triton2.fet.at
nginx:
image: docker.triton2.fet.at/nginxdev-fet2020:latest
ports:
- "8106:8080"
volumes:
ep-mysql:
driver: local
theia_usr:
driver: local
dev_data:
driver: local

View File

@@ -2,4 +2,4 @@ from django.apps import AppConfig
class AuthenticationsConfig(AppConfig):
name = 'authentications'
name = "authentications"

View File

@@ -11,25 +11,25 @@ def authentication(username, password):
return None
# username format
new_username = 'uid={username},ou=user,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at'
new_username = "uid={username},ou=user,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at"
userdn = new_username.format(username=username)
server_uri = 'ldap://gagarin.fet.htu.tuwien.ac.at'
server_uri = "ldap://gagarin.fet.htu.tuwien.ac.at"
server = ldap3.Server(server_uri, port=389, use_ssl=True)
has_user = False
try:
conn = ldap3.Connection(server, user=userdn, password=password, auto_bind=True)
conn.search('dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at', '(objectclass=person)')
conn.search("dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at", "(objectclass=person)")
for user in sorted(conn.entries):
if ("DN: uid=" + str(username.lower())) in str(user):
has_user = True
except LDAPBindError as e:
logger.info('Username does not exist. Error: {}'.format(e))
logger.info("Username does not exist. Error: {}".format(e))
username = None
except Exception as e:
logger.info('Connection to server lost. Error: {}'.format(e))
logger.info("Connection to server lost. Error: {}".format(e))
username = None
if not has_user:

View File

@@ -4,7 +4,7 @@ from django.shortcuts import redirect
def unauthenticated_user(view_func):
def wrapper_func(request, *args, **kwargs):
if request.user.is_authenticated:
return redirect('home')
return redirect("home")
else:
return view_func(request, *args, **kwargs)
@@ -16,7 +16,7 @@ def authenticated_user(view_func):
if request.user.is_authenticated:
return view_func(request, *args, **kwargs)
else:
return redirect('login')
return redirect("login")
return wrapper_func

View File

@@ -3,4 +3,4 @@ from django import forms
class LoginForm(forms.Form):
username = forms.CharField()
password = forms.CharField(label='Passwort', widget=forms.PasswordInput())
password = forms.CharField(label="Passwort", widget=forms.PasswordInput())

View File

@@ -3,6 +3,6 @@ from . import views
urlpatterns = [
path('login/', views.loginPage, name="login"),
path('logout/', views.logoutUser, name="logout"),
path("login/", views.loginPage, name="login"),
path("logout/", views.logoutUser, name="logout"),
]

View File

@@ -12,9 +12,9 @@ from .forms import LoginForm
@unauthenticated_user
def loginPage(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if request.method == "POST":
username = request.POST.get("username")
password = request.POST.get("password")
auth_user = authentication(username, password)
@@ -25,23 +25,27 @@ def loginPage(request):
user = User.objects.create_user(auth_user.lower())
login(request, user)
return redirect('home')
try:
return redirect(request.GET.get("next"))
except:
return redirect("home")
else:
messages.info(request, 'username or password is incorrect')
messages.info(request, "username or password is incorrect")
form = LoginForm()
context = {
"form": form,
}
return render(request, 'authentications/login.html', context)
return render(request, "authentications/login.html", context)
@authenticated_user
def logoutUser(request):
logout(request)
response = redirect('home')
response = redirect("home")
response = del_ep_cookie(request, response)
return response

View File

@@ -8,7 +8,7 @@ class JobPostingAdmin(admin.ModelAdmin):
form = JobPostingForm
model = JobPosting
list_display = ['companyName', 'jobName', 'salary', 'publishDate']
list_display = ["companyName", "jobName", "salary", "publishDate"]
admin.site.register(JobPosting, JobPostingAdmin)

View File

@@ -2,4 +2,4 @@ from django.apps import AppConfig
class BlackboardConfig(AppConfig):
name = 'blackboard'
name = "blackboard"

View File

@@ -7,19 +7,17 @@ from .models import JobPosting
class JobPostingForm(forms.ModelForm):
class Meta:
model = JobPosting
fields = ['companyName', 'jobName', 'salary', 'pdfLocation', 'publishDate']
fields = ["companyName", "jobName", "salary", "pdfLocation", "publishDate"]
labels = {
'companyName': _("Firmenname"),
'jobName': _("Berufsbezeichnung"),
'salary': _("Gehalt"),
'pdfLocation': _("Stellenausschreibung"),
'publishDate': _("Veröffentlichung"),
"companyName": _("Firmenname"),
"jobName": _("Berufsbezeichnung"),
"salary": _("Gehalt"),
"pdfLocation": _("Stellenausschreibung"),
"publishDate": _("Veröffentlichung"),
}
help_texts = {
'pdfLocation': _(
"Verwendbare Formate: PDF"
),
'salary': _("in Euro angeben"),
"pdfLocation": _("Verwendbare Formate: PDF"),
"salary": _("in Euro angeben"),
}

View File

@@ -11,16 +11,23 @@ from os.path import splitext, basename
import ghostscript
import logging
logger = logging.getLogger('blackboard')
logger = logging.getLogger("blackboard")
class JobPosting(models.Model):
companyName = models.CharField(verbose_name="Firmenname", max_length=128)
jobName = models.CharField(verbose_name="Berufsbezeichnung", max_length=128)
salary = models.PositiveSmallIntegerField(verbose_name="Gehalt", )
pdfLocation = models.FileField(verbose_name="Stellenausschreibung", upload_to='uploads/blackboard/pdf/')
salary = models.PositiveSmallIntegerField(
verbose_name="Gehalt",
)
pdfLocation = models.FileField(
verbose_name="Stellenausschreibung", upload_to="uploads/blackboard/pdf/"
)
pdf_thumb_location = models.CharField(max_length=128)
publishDate = models.DateField(verbose_name="Veröffentlichung", default=timezone.now)
publishDate = models.DateField(
verbose_name="Veröffentlichung", default=timezone.now
)
# Managers
all_jobPosting = models.Manager()
@@ -40,7 +47,7 @@ class JobPosting(models.Model):
"-dDEVICEWIDTHPOINTS=600",
"-dDEVICEHEIGHTPOINTS=800",
"-sOutputFile=" + jpeg_output_path,
pdf_input_path
pdf_input_path,
]
encoding = locale.getpreferredencoding()
@@ -54,14 +61,18 @@ class JobPosting(models.Model):
if not os.path.exists(settings.MEDIA_ROOT + "uploads/blackboard/thumb/"):
os.makedirs(settings.MEDIA_ROOT + "uploads/blackboard/thumb/")
pdf_thumb_location_full = settings.MEDIA_ROOT \
+ "uploads/blackboard/thumb/" \
+ splitext(basename(self.pdfLocation.name))[0] \
pdf_thumb_location_full = (
settings.MEDIA_ROOT
+ "uploads/blackboard/thumb/"
+ splitext(basename(self.pdfLocation.name))[0]
+ ".jpg"
)
self.pdf_thumb_location = "/files/uploads/blackboard/thumb/" \
+ splitext(basename(self.pdfLocation.name))[0] \
self.pdf_thumb_location = (
"/files/uploads/blackboard/thumb/"
+ splitext(basename(self.pdfLocation.name))[0]
+ ".jpg"
)
self.pdf2jpeg(self.pdfLocation.path, pdf_thumb_location_full)
logger.info("SavenThumbAs: " + self.pdf_thumb_location)
@@ -70,7 +81,7 @@ class JobPosting(models.Model):
def clean(self):
count = 0
for i in self.pdfLocation.name:
if i == '.':
if i == ".":
count = count + 1
if count > 1: # if more than one dot in filename
raise ValidationError(_('Keine Dateien mit >1 Punkten im Namen erlaubt.'))
raise ValidationError(_("Keine Dateien mit >1 Punkten im Namen erlaubt."))

View File

@@ -4,5 +4,5 @@ from . import views
urlpatterns = [
path('', views.index, name='blackboard'),
path("", views.index, name="blackboard"),
]

View File

@@ -10,8 +10,8 @@ from posts.models import Post
def index(request):
job_postings_cutoff = timezone.now().date() - timedelta(30) # 30days from now
job_postings = JobPosting.all_jobPosting.filter(publishDate__gt=job_postings_cutoff)
bb_info = Post.objects.filter(slug='blackboard').first()
bb_empty = Post.objects.filter(slug='blackboard-empty').first()
bb_info = Post.objects.filter(slug="blackboard").first()
bb_empty = Post.objects.filter(slug="blackboard-empty").first()
context = {
"job_postings": job_postings,
@@ -19,4 +19,4 @@ def index(request):
"bb_empty": bb_empty,
}
return render(request, 'blackboard/index.html', context)
return render(request, "blackboard/index.html", context)

View File

@@ -6,6 +6,7 @@ import urllib.parse
from etherpad_lite import EtherpadLiteClient, EtherpadException
import logging
logger = logging.getLogger(__name__)
SERVER_URL = settings.ETHERPAD_CLIENT["exturl"]
@@ -20,9 +21,11 @@ def get_ep_client():
apikey = f.read()
apikey = apikey.rstrip()
epc = EtherpadLiteClient(
base_params={'apikey': apikey, },
base_params={
"apikey": apikey,
},
base_url=urllib.parse.urljoin(settings.ETHERPAD_CLIENT["url"], "api"),
api_version='1.2.14',
api_version="1.2.14",
)
group = epc.createGroupIfNotExistsFor(groupMapper="fet")
except Exception as e:
@@ -62,7 +65,9 @@ def createPadifNotExists(padID):
# Pad doesn't exist
if not __checkPadExists(padID=padID):
try:
epc.createGroupPad(groupID=group["groupID"], padName=padID, text="helloworld")
epc.createGroupPad(
groupID=group["groupID"], padName=padID, text="helloworld"
)
except EtherpadException as e:
logger.info("Can't create Pad '{}'. EtherpadException: {}".format(padID, e))
return None
@@ -81,7 +86,9 @@ def getPadHTML(padID):
try:
text = epc.getHTML(padID=group["groupID"] + "$" + padID)["html"]
except EtherpadException as e:
logger.info("Can't get HTML from padID '{}'. EtherpadException: {}".format(padID, e))
logger.info(
"Can't get HTML from padID '{}'. EtherpadException: {}".format(padID, e)
)
return None
except Exception as e:
raise e
@@ -107,4 +114,6 @@ def get_pad_link(padID):
if not epc or not group:
return "#"
return urllib.parse.urljoin(settings.ETHERPAD_CLIENT["exturl"], 'p/' + group["groupID"] + '$' + str(padID))
return urllib.parse.urljoin(
settings.ETHERPAD_CLIENT["exturl"], "p/" + group["groupID"] + "$" + str(padID)
)

View File

@@ -1,6 +1,7 @@
from django.apps import AppConfig
# from django.contrib.admin.apps import AdminConfig
class DocumentsConfig(AppConfig):
name = 'documents'
name = "documents"

View File

@@ -11,22 +11,21 @@ def __get_ep_sessionid(request):
return None, None
author = epc.createAuthorIfNotExistsFor(
name=str(request.user),
authorMapper=str(request.user)
)['authorID']
name=str(request.user), authorMapper=str(request.user)
)["authorID"]
expires = datetime.utcnow() + timedelta(hours=3)
try:
result = epc.createSession(
groupID=str(group['groupID']),
groupID=str(group["groupID"]),
authorID=str(author),
validUntil=str(int(expires.timestamp()))
validUntil=str(int(expires.timestamp())),
)
except Exception as e:
raise e
return None, None
return result['sessionID'], expires
return result["sessionID"], expires
def add_ep_cookie(request, response):
@@ -34,32 +33,19 @@ def add_ep_cookie(request, response):
if ep_sessid:
response.set_cookie(
"sessionID",
ep_sessid,
expires=expires,
domain=".2020.fet.at",
path="/"
)
response.set_cookie(
"sessionID",
ep_sessid,
expires=expires,
path="/etherpad"
"sessionID", ep_sessid, expires=expires, domain=".2020.fet.at", path="/"
)
response.set_cookie("sessionID", ep_sessid, expires=expires, path="/etherpad")
return response
def del_ep_cookie(request, response):
if 'sessionID' in request.COOKIES:
ep_sessionID = request.COOKIES['sessionID']
if "sessionID" in request.COOKIES:
ep_sessionID = request.COOKIES["sessionID"]
epc, group = get_ep_client()
epc.deleteSession(sessionID=ep_sessionID)
response.delete_cookie(
"sessionID",
domain=".2020.fet.at",
path="/"
)
response.delete_cookie("sessionID", domain=".2020.fet.at", path="/")
return response

View File

@@ -2,6 +2,6 @@ import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'fet2020.settings')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "fet2020.settings")
application = get_asgi_application()

View File

@@ -11,23 +11,23 @@ env = environ.Env(
MYSQL_USER=(str),
MYSQL_PASSWORD=(str),
HOST_NAME=(str, "localhost"),
ETHERPAD_PORT=(str,"9001"),
ETHERPAD_HOST=(str,"etherpad2.2020.fet.at")
ETHERPAD_PORT=(str, "9001"),
ETHERPAD_HOST=(str, "etherpad2.2020.fet.at"),
)
# Prints and logs are written to console
# TODO: Change before release
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"console": {
"class": "logging.StreamHandler",
},
},
'root': {
'handlers': ['console'],
'level': 'DEBUG',
"root": {
"handlers": ["console"],
"level": "DEBUG",
},
}
@@ -41,108 +41,107 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env('DEBUG')
DEBUG = env("DEBUG")
if DEBUG:
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'r37-i7l)vrduzz2-gira+z#u!p!di9#f+%s*5-bb($hg)55@ns'
SECRET_KEY = "r37-i7l)vrduzz2-gira+z#u!p!di9#f+%s*5-bb($hg)55@ns"
else:
SECRET_KEY = env('SECRET_KEY')
SECRET_KEY = env("SECRET_KEY")
ALLOWED_HOSTS = ["127.0.0.1", env('HOST_NAME'), "2020.fet.at"]
HOST_NAME = env('HOST_NAME')
HOST_NAME = env("HOST_NAME")
DATA_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 1024
# Application definition
CKEDITOR_UPLOAD_PATH = 'upload'
CKEDITOR_UPLOAD_PATH = "upload"
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.admindocs',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'taggit',
'ckeditor',
'ckeditor_uploader',
'easy_thumbnails',
'rest_framework',
'softhyphen',
'django_crontab',
'django_filters',
'django_static_jquery_ui',
'posts.apps.PostsConfig',
'members.apps.MembersConfig',
'documents.apps.DocumentsConfig',
'blackboard.apps.BlackboardConfig',
'tasks.apps.TasksConfig',
"django.contrib.admin",
"django.contrib.admindocs",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"taggit",
"ckeditor",
"ckeditor_uploader",
"easy_thumbnails",
"rest_framework",
"softhyphen",
"django_crontab",
"django_filters",
"django_static_jquery_ui",
"posts.apps.PostsConfig",
"members.apps.MembersConfig",
"documents.apps.DocumentsConfig",
"blackboard.apps.BlackboardConfig",
"tasks.apps.TasksConfig",
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'fet2020.middleware.FETHeaderMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.locale.LocaleMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"fet2020.middleware.FETHeaderMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = 'fet2020.urls'
ROOT_URLCONF = "fet2020.urls"
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, 'templates'),
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [
os.path.join(BASE_DIR, "templates"),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'django.template.context_processors.i18n',
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
"django.template.context_processors.i18n",
],
},
},
]
WSGI_APPLICATION = 'fet2020.wsgi.application'
WSGI_APPLICATION = "fet2020.wsgi.application"
# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
if DEBUG:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
}
}
else:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': env('MYSQL_DATABASE'),
'USER': env('MYSQL_USER'),
'PASSWORD': env('MYSQL_PASSWORD'),
'HOST': env('MYSQL_HOST'),
'PORT': env('MYSQL_PORT')
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": env("MYSQL_DATABASE"),
"USER": env("MYSQL_USER"),
"PASSWORD": env("MYSQL_PASSWORD"),
"HOST": env("MYSQL_HOST"),
"PORT": env("MYSQL_PORT"),
}
}
AUTHENTICATION_BACKENDS = [
# 'django.contrib.auth.backends.RemoteUserBackend',
'django.contrib.auth.backends.ModelBackend',
"django.contrib.auth.backends.ModelBackend",
]
# Password validation
@@ -150,16 +149,16 @@ AUTHENTICATION_BACKENDS = [
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
@@ -167,9 +166,9 @@ AUTH_PASSWORD_VALIDATORS = [
# Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/
LANGUAGE_CODE = 'de-at'
LANGUAGE_CODE = "de-at"
TIME_ZONE = 'CET'
TIME_ZONE = "CET"
USE_I18N = True
@@ -177,32 +176,32 @@ USE_L10N = True
USE_TZ = True
LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale')]
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATIC_URL = '/assets/'
STATIC_URL = "/assets/"
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static")
]
STATIC_ROOT = 'assets/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'files/')
MEDIA_URL = '/files/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")]
STATIC_ROOT = "assets/"
MEDIA_ROOT = os.path.join(BASE_DIR, "files/")
MEDIA_URL = "/files/"
TAGGIT_FORCE_LOWERCASE = True
CKEDITOR_CONFIGS = {
'default': {
'stylesSet': [
"default": {
"stylesSet": [
{
"name": 'Überschrift 2',
"element": 'h2',
"name": "Überschrift 2",
"element": "h2",
"attributes": {},
},
{
"name": 'Code',
"element": 'code',
"name": "Code",
"element": "code",
"attributes": {"class": "code-block"},
},
],
@@ -211,41 +210,41 @@ CKEDITOR_CONFIGS = {
# THUMBNAIL
THUMBNAIL_ALIASES = {
'': {
'avatar': {'size': (50, 50), 'crop': True},
'thumb': {'size': (150, 150), 'crop': True},
"": {
"avatar": {"size": (50, 50), "crop": True},
"thumb": {"size": (150, 150), "crop": True},
},
}
# ETHERPAD CLIENT
if DEBUG:
ETHERPAD_CLIENT = {
'url': "http://etherpad:"+env('ETHERPAD_PORT'),
'exturl': env('ETHERPAD_HOST'),
'apikey': "/srv/etherpad/APIKEY.txt"
"url": "http://etherpad:" + env("ETHERPAD_PORT"),
"exturl": env("ETHERPAD_HOST"),
"apikey": "/srv/etherpad/APIKEY.txt",
}
else:
ETHERPAD_CLIENT = {
'url': "http://etherpad:"+env('ETHERPAD_PORT'),
'exturl': urljoin('https://' + env('HOST_NAME'),"etherpad/"),
'apikey': "/app/etherpad/APIKEY.txt"
"url": "http://etherpad:" + env("ETHERPAD_PORT"),
"exturl": urljoin("https://" + env("HOST_NAME"), "etherpad/"),
"apikey": "/app/etherpad/APIKEY.txt",
}
# REST FRAMEWORK
REST_FRAMEWORK={
'DEFAULT_PERMISSION_CLASSES_CLASSES':[
'rest_framework.permissions.AllowAny',
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES_CLASSES": [
"rest_framework.permissions.AllowAny",
],
'DEFAULT_AUTHENTICATION_CLASSES':()
"DEFAULT_AUTHENTICATION_CLASSES": (),
}
# DJANGO MAIL
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'buran.htu.tuwien.ac.at'
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "buran.htu.tuwien.ac.at"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
# CRON JOBS
CRONJOBS = [
('0 16 * * *', 'posts.cronjobs.check_to_send_agenda_mail'),
("0 16 * * *", "posts.cronjobs.check_to_send_agenda_mail"),
]

View File

@@ -4,31 +4,36 @@ from django.conf.urls.static import static
from django.conf import settings
from django.views.generic import RedirectView
from . import views
from posts.views import PostViewSet
from posts.viewsets import PostViewSet
from members.urls import member_urlpatterns, jobs_urlpatterns
from members.views import MemberViewSet, JobViewSet, JobGroupViewSet, JobMemberViewSet
from members.viewsets import (
MemberViewSet,
JobViewSet,
JobGroupViewSet,
JobMemberViewSet,
)
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'posts', PostViewSet)
router.register(r'members', MemberViewSet)
router.register(r'jobgroups', JobGroupViewSet)
router.register(r'jobs', JobViewSet)
router.register(r'jobmembers', JobMemberViewSet)
router.register(r"posts", PostViewSet)
router.register(r"members", MemberViewSet)
router.register(r"jobgroups", JobGroupViewSet)
router.register(r"jobs", JobViewSet)
router.register(r"jobmembers", JobMemberViewSet)
urlpatterns = [
path('posts/', include('posts.urls')),
path('admin/doc/', include('django.contrib.admindocs.urls')),
path('admin/login/', RedirectView.as_view(pattern_name='login')),
path('admin/', admin.site.urls),
path('auth/', include('authentications.urls')),
path('', views.index, name='home'),
path('index.html', views.index, name='home'),
path('ckeditor/', include('ckeditor_uploader.urls')),
path('api/', include(router.urls)),
path('members/', include('members.urls'), name='members'),
path('jobs/', include(jobs_urlpatterns), name='jobs'),
path('member/', include(member_urlpatterns), name='member'),
path('blackboard/', include('blackboard.urls'), name='blackboard'),
path('tasks/', include('tasks.urls'), name='tasks'),
path("posts/", include("posts.urls")),
path("admin/doc/", include("django.contrib.admindocs.urls")),
path("admin/login/", RedirectView.as_view(pattern_name="login")),
path("admin/", admin.site.urls),
path("auth/", include("authentications.urls")),
path("", views.index, name="home"),
path("index.html", views.index, name="home"),
path("ckeditor/", include("ckeditor_uploader.urls")),
path("api/", include(router.urls)),
path("members/", include("members.urls"), name="members"),
path("jobs/", include(jobs_urlpatterns), name="jobs"),
path("member/", include(member_urlpatterns), name="member"),
path("blackboard/", include("blackboard.urls"), name="blackboard"),
path("tasks/", include("tasks.urls"), name="tasks"),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@@ -1,4 +1,5 @@
from django.shortcuts import render
# from django.http import HttpResponse
from collections import deque
from posts.models import Post, FetMeeting, Event
@@ -34,12 +35,12 @@ def index(request):
featured_meeting = FetMeeting.objects.get_meetings()
context = {
'posts': deque(list(posts)[:5]),
'events': Event.all_events.get_five_events(),
'featured_post': featured_post,
'featured_event': featured_event,
'featured_meeting': featured_meeting,
'tags_list': " ".join(t)
"posts": deque(list(posts)[:5]),
"events": Event.all_events.get_five_events(),
"featured_post": featured_post,
"featured_event": featured_event,
"featured_meeting": featured_meeting,
"tags_list": " ".join(t),
}
return render(request, 'home.html', context)
return render(request, "home.html", context)

View File

@@ -2,6 +2,6 @@ import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'fet2020.settings')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "fet2020.settings")
application = get_wsgi_application()

View File

@@ -1,3 +1,3 @@
#!/bin/sh
python manage.py makemigrations && python manage.py makemigrations posts members blackboard\
&& python manage.py migrate
python3 manage.py makemigrations && python3 manage.py makemigrations posts members blackboard\
&& python3 manage.py migrate

Binary file not shown.

View File

@@ -0,0 +1,346 @@
# Translation for Austrian German 'de_AT'. You have to use 'de' because Django doesn't support 'de_AT'.
msgid ""
msgstr ""
"Project-Id-Version: fet2020\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-17 12:57+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FET\n"
"Language-Team: German\n"
"Language: de_AT\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: blackboard/forms.py:13
msgid "Firmenname"
msgstr ""
#: blackboard/forms.py:14
msgid "Berufsbezeichnung"
msgstr ""
#: blackboard/forms.py:15
msgid "Gehalt"
msgstr ""
#: blackboard/forms.py:16
msgid "Stellenausschreibung"
msgstr ""
#: blackboard/forms.py:17 posts/forms.py:55 posts/forms.py:109
msgid "Veröffentlichung"
msgstr ""
#: blackboard/forms.py:21
msgid "Verwendbare Formate: PDF"
msgstr ""
#: blackboard/forms.py:22
msgid "in Euro angeben"
msgstr ""
#: blackboard/models.py:87
msgid "Keine Dateien mit >1 Punkten im Namen erlaubt."
msgstr ""
#: members/admin.py:9
msgid "Rolle"
msgstr ""
#: members/admin.py:14
msgid "Aktiv"
msgstr ""
#: members/admin.py:15 members/models.py:107
msgid "Pension"
msgstr ""
#: members/forms.py:29
msgid "Beschreibung zu der Person"
msgstr ""
#: members/forms.py:30
msgid "Porträt"
msgstr ""
#: members/forms.py:31
msgid "Geburtstag"
msgstr ""
#: members/forms.py:32
msgid "Telefonnummer"
msgstr ""
#: members/forms.py:33
msgid "Wohnadresse"
msgstr ""
#: members/forms.py:37
msgid "Mindestgröße: 150*150 px, Verwendbare Formate: ..."
msgstr ""
#: members/forms.py:38
msgid "Die Mailadresse mit '@fet.at' angeben."
msgstr ""
#: members/forms.py:53
msgid "Kürzel der Tätigkeit"
msgstr ""
#: members/forms.py:54
msgid "Tätigkeitsbereich"
msgstr ""
#: members/forms.py:72
msgid "Kürzel des Tätigkeitsbereichs"
msgstr ""
#: members/forms.py:73
msgid "Beschreibung des Tätigkeitsbereichs"
msgstr ""
#: members/forms.py:75
msgid ""
"Dieser Tätigkeitsbereich soll im Fachschaftsbereich angeheftet werden, damit "
"es sofort ersichtlich ist."
msgstr ""
#: members/models.py:106
msgid "Active"
msgstr ""
#: members/models.py:144
msgid "Es fehlt das Profilbild."
msgstr ""
#: members/models.py:148
msgid "Das Bild ist zu klein. (Höhe: {}, Breite: {})"
msgstr ""
#: members/models.py:154
msgid "In der Mailadresse fehlt die Domäne."
msgstr ""
#: members/models.py:234
msgid "VorsitzendeR"
msgstr ""
#: members/models.py:235
msgid "stv VorsitzendeR"
msgstr ""
#: members/models.py:236
msgid "2. stv VorsitzendeR"
msgstr ""
#: members/models.py:237
msgid "VerantwortlicheR"
msgstr ""
#: members/models.py:238
msgid "Mitglied"
msgstr ""
#: members/models.py:239
msgid "Ersatzmitglied"
msgstr ""
#: posts/admin.py:27
#, python-format
msgid "Das Agenda konnte nicht erstellt werden. Error: %s"
msgstr ""
#: posts/admin.py:37
#, python-format
msgid "Das Protokoll konnte nicht erstellt werden. Error: %s"
msgstr ""
#: posts/admin.py:52
#, python-format
msgid "Das Event %s wurde erfolgreich in eine FET Sitzung konvertiert."
msgstr ""
#: posts/forms.py:50 posts/forms.py:101 tasks/forms.py:19
msgid "Titel"
msgstr ""
#: posts/forms.py:51 posts/forms.py:102
msgid "Untertitel"
msgstr ""
#: posts/forms.py:52 posts/forms.py:103
msgid "Hintergrundbild"
msgstr ""
#: posts/forms.py:53 posts/forms.py:104
msgid "Text"
msgstr ""
#: posts/forms.py:54 posts/forms.py:108
msgid "Autor"
msgstr ""
#: posts/forms.py:56
msgid "Post anheften"
msgstr ""
#: posts/forms.py:57
msgid "Post verstecken"
msgstr ""
#: posts/forms.py:62 posts/forms.py:115 posts/forms.py:153
msgid ""
"Die Hashtags ohne '#' eintragen, und mit Komma kann man mehrere Tags anfügen."
msgstr ""
#: posts/forms.py:64
msgid "Verwendbare Formate: ..."
msgstr ""
#: posts/forms.py:66
msgid "Dieser Post soll an die Startseite als erster Post angeheftet werden."
msgstr ""
#: posts/forms.py:69
msgid "Dieser Post soll im News Feed nicht auftauchen, z.B. Impressum."
msgstr ""
#: posts/forms.py:105
msgid "Start des Events"
msgstr ""
#: posts/forms.py:106
msgid "Ende des Events"
msgstr ""
#: posts/forms.py:107
msgid "Ort des Events"
msgstr ""
#: posts/forms.py:110
msgid "Event anheften"
msgstr ""
#: posts/forms.py:117
msgid "Verwendbare Formate: "
msgstr ""
#: posts/forms.py:119
msgid "Dieses Event soll an die Startseite als erster Post angeheftet werden."
msgstr ""
#: posts/forms.py:145
msgid "Start der Sitzung"
msgstr ""
#: posts/forms.py:146
msgid "Ende der Sitzung"
msgstr ""
#: posts/models.py:86
msgid "News"
msgstr ""
#: posts/models.py:86
msgid "Event"
msgstr ""
#: posts/models.py:86
msgid "FetMeeting"
msgstr ""
#: posts/models.py:258
msgid "Das Ende des Events liegt vor dem Beginn."
msgstr ""
#: posts/models.py:318
msgid "Das Datum des Events fehlt."
msgstr ""
#: posts/models.py:349
msgid "Es existiert bereits eine Sitzung mit demselben Datum."
msgstr ""
#: tasks/forms.py:20
msgid "Aufgabenbereich"
msgstr ""
#: tasks/forms.py:21
msgid "Fälligkeit"
msgstr ""
#: tasks/forms.py:22
msgid "Abgeschlossen"
msgstr ""
#: tasks/forms.py:23
msgid "Datum der Fertigstellung"
msgstr ""
#: tasks/forms.py:24 tasks/forms.py:52
msgid "Zuweisen an"
msgstr ""
#: tasks/forms.py:25
msgid "Notizen"
msgstr ""
#: tasks/forms.py:26
msgid "Priorität"
msgstr ""
#: tasks/forms.py:49
msgid "Titel des Tasks"
msgstr ""
#: tasks/forms.py:50
msgid "Task-Gruppe"
msgstr ""
#: tasks/forms.py:51
msgid "Fälligkeitsdatum"
msgstr ""
#: templates/admin/base.html:8
msgid "Welcome,"
msgstr ""
#: templates/admin/base.html:18
msgid "Documentation"
msgstr ""
#: templates/admin/base.html:22
msgid "Change password"
msgstr ""
#: templates/admin/base.html:24
msgid "Log out"
msgstr ""
#: templates/admin/submit_line.html:5
msgid "Close"
msgstr ""
#: templates/documents/base.html:17
msgid "profile"
msgstr ""
#: templates/documents/base.html:18
msgid "logout"
msgstr ""
msgid "January"
msgstr "Jänner"
msgid "jan"
msgstr "Jän"
msgctxt "abbrev. month"
msgid "Jan."
msgstr "Jän."
msgctxt "alt. month"
msgid "January"
msgstr "Jänner"

View File

@@ -6,13 +6,13 @@ from .forms import MemberForm, JobForm, JobGroupForm
class MemberRoleFilter(admin.SimpleListFilter):
title = _('Rolle')
parameter_name = 'role'
title = _("Rolle")
parameter_name = "role"
def lookups(self, request, model_admin):
return (
('A', _('Aktiv')),
('P', _('Pension')),
("A", _("Aktiv")),
("P", _("Pension")),
)
def queryset(self, request, queryset):
@@ -55,40 +55,53 @@ class MemberAdmin(admin.ModelAdmin):
form = MemberForm
model = Member
fieldsets = (
(None, {
'fields': (
('firstname', 'surname',),
'nickname',
'username',
'mailaccount',
'role',
'description',
'image',
'birthday',
'phone',
'address',
)
}),
(
None,
{
"fields": (
(
"firstname",
"surname",
),
"nickname",
"username",
"mailaccount",
"role",
"description",
"image",
"birthday",
"phone",
"address",
)
},
),
)
inlines = (JobOverviewInline,)
list_display = ['nickname', 'firstname', 'surname', 'mailaccount', 'role']
ordering = ['firstname', ]
search_fields = ['firstname', 'surname', 'nickname', 'mailaccount']
list_display = ["nickname", "firstname", "surname", "mailaccount", "role"]
ordering = [
"firstname",
]
search_fields = ["firstname", "surname", "nickname", "mailaccount"]
list_filter = [MemberRoleFilter]
def add_view(self, request, form_url='', extra_context=None):
def add_view(self, request, form_url="", extra_context=None):
extra_context = extra_context or {}
extra_context['help_text'] = "Fette Schriften sind Pflichtfelder."
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."
return super().add_view(
request, form_url, extra_context=extra_context,
request,
form_url,
extra_context=extra_context,
)
def change_view(self, request, object_id, form_url='', extra_context=None):
def change_view(self, request, object_id, form_url="", extra_context=None):
extra_context = extra_context or {}
extra_context['help_text'] = "Fette Schriften sind Pflichtfelder."
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."
return super().change_view(
request, object_id, form_url, extra_context=extra_context,
request,
object_id,
form_url,
extra_context=extra_context,
)
def save_model(self, request, obj, form, change):
@@ -101,22 +114,33 @@ class JobAdmin(admin.ModelAdmin):
model = Job
inlines = (ActiveMemberInline, InactiveMemberInline)
list_display = ['name', ]
ordering = ['name', ]
search_fields = ['name', ]
list_display = [
"name",
]
ordering = [
"name",
]
search_fields = [
"name",
]
def add_view(self, request, form_url='', extra_context=None):
def add_view(self, request, form_url="", extra_context=None):
extra_context = extra_context or {}
extra_context['help_text'] = "Fette Schriften sind Pflichtfelder."
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."
return super().add_view(
request, form_url, extra_context=extra_context,
request,
form_url,
extra_context=extra_context,
)
def change_view(self, request, object_id, form_url='', extra_context=None):
def change_view(self, request, object_id, form_url="", extra_context=None):
extra_context = extra_context or {}
extra_context['help_text'] = "Fette Schriften sind Pflichfelder."
extra_context["help_text"] = "Fette Schriften sind Pflichfelder."
return super().change_view(
request, object_id, form_url, extra_context=extra_context,
request,
object_id,
form_url,
extra_context=extra_context,
)
def save_model(self, request, obj, form, change):
@@ -127,24 +151,36 @@ class JobAdmin(admin.ModelAdmin):
class JobGroupAdmin(admin.ModelAdmin):
form = JobGroupForm
model = JobGroup
inlines = (JobInline, )
inlines = (JobInline,)
list_display = ['name', 'is_pinned', ]
ordering = ['name', ]
search_fields = ['name', ]
list_display = [
"name",
"is_pinned",
]
ordering = [
"name",
]
search_fields = [
"name",
]
def add_view(self, request, form_url='', extra_context=None):
def add_view(self, request, form_url="", extra_context=None):
extra_context = extra_context or {}
extra_context['help_text'] = "Fette Schriften sind Pflichtfelder."
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."
return super().add_view(
request, form_url, extra_context=extra_context,
request,
form_url,
extra_context=extra_context,
)
def change_view(self, request, object_id, form_url='', extra_context=None):
def change_view(self, request, object_id, form_url="", extra_context=None):
extra_context = extra_context or {}
extra_context['help_text'] = "Fette Schriften sind Pflichfelder."
extra_context["help_text"] = "Fette Schriften sind Pflichfelder."
return super().change_view(
request, object_id, form_url, extra_context=extra_context,
request,
object_id,
form_url,
extra_context=extra_context,
)
def save_model(self, request, obj, form, change):

View File

@@ -2,4 +2,4 @@ from django.apps import AppConfig
class MembersConfig(AppConfig):
name = 'members'
name = "members"

View File

@@ -10,56 +10,68 @@ class MemberForm(forms.ModelForm):
class Meta:
model = Member
fields = [
'firstname', 'surname', 'nickname', 'username', 'mailaccount', 'role', 'description',
'image', 'birthday', 'phone', 'address',
"firstname",
"surname",
"nickname",
"username",
"mailaccount",
"role",
"description",
"image",
"birthday",
"phone",
"address",
]
widgets = {
'description': CKEditorUploadingWidget(config_name='default')
}
widgets = {"description": CKEditorUploadingWidget(config_name="default")}
labels = {
'description': _("Beschreibung zu der Person"),
'image': _("Porträt"),
'birthday': _("Geburtstag"),
'phone': _("Telefonnummer"),
'address': _("Wohnadresse"),
"description": _("Beschreibung zu der Person"),
"image": _("Porträt"),
"birthday": _("Geburtstag"),
"phone": _("Telefonnummer"),
"address": _("Wohnadresse"),
}
help_texts = {
'image': _(
"Mindestgröße: 150*150 px, Verwendbare Formate: ..."
),
'mailaccount': _(
"Die Mailadresse mit '@fet.at' angeben."
),
"image": _("Mindestgröße: 150*150 px, Verwendbare Formate: ..."),
"mailaccount": _("Die Mailadresse mit '@fet.at' angeben."),
}
class JobForm(forms.ModelForm):
class Meta:
model = Job
fields = ['name', 'shortterm', 'slug', 'job_group',]
fields = [
"name",
"shortterm",
"slug",
"job_group",
]
labels = {
'shortterm': _("Kürzel der Tätigkeit"),
'job_group': _("Tätigkeitsbereich"),
"shortterm": _("Kürzel der Tätigkeit"),
"job_group": _("Tätigkeitsbereich"),
}
class JobGroupForm(forms.ModelForm):
class Meta:
model = JobGroup
fields = ['name', 'shortterm', 'slug', 'description', 'is_pinned',]
fields = [
"name",
"shortterm",
"slug",
"description",
"is_pinned",
]
widgets = {
'description': CKEditorUploadingWidget(config_name='default')
}
widgets = {"description": CKEditorUploadingWidget(config_name="default")}
labels = {
'shortterm': _("Kürzel des Tätigkeitsbereichs"),
'description': _("Beschreibung des Tätigkeitsbereichs"),
'is_pinned': _(
"shortterm": _("Kürzel des Tätigkeitsbereichs"),
"description": _("Beschreibung des Tätigkeitsbereichs"),
"is_pinned": _(
"Dieser Tätigkeitsbereich soll im Fachschaftsbereich angeheftet werden, damit es sofort ersichtlich ist."
),
}

View File

@@ -10,15 +10,20 @@ from easy_thumbnails.fields import ThumbnailerImageField
class ActiveJobMemberManager(models.Manager):
'''
"""
return a list of active member, and members who are still working
'''
"""
def get_all_by_slug(self, slug):
return self.get_queryset().filter(job__job_group__slug=slug).order_by('job__slug', 'job_role', 'member__firstname')
return (
self.get_queryset()
.filter(job__job_group__slug=slug)
.order_by("job__slug", "job_role", "member__firstname")
)
def get_queryset(self):
date_today = timezone.now().date()
qs = super().get_queryset().order_by('member__firstname')
qs = super().get_queryset().order_by("member__firstname")
return qs.filter(
Q(member__role=Member.MemberRole.ACTIVE)
@@ -27,15 +32,20 @@ class ActiveJobMemberManager(models.Manager):
class InactiveJobMemberManager(models.Manager):
'''
"""
return a list of inactive member
'''
"""
def get_all_by_slug(self, slug):
return self.get_queryset().filter(job__job_group__slug=slug).order_by('job__slug', 'job_role', 'member__firstname')
return (
self.get_queryset()
.filter(job__job_group__slug=slug)
.order_by("job__slug", "job_role", "member__firstname")
)
def get_queryset(self):
date_today = timezone.now().date()
qs = super().get_queryset().order_by('member__firstname')
qs = super().get_queryset().order_by("member__firstname")
return qs.filter(
Q(member__role=Member.MemberRole.PENSION)
@@ -45,7 +55,7 @@ class InactiveJobMemberManager(models.Manager):
class JobMemberManager(models.Manager):
def get_members(self, role):
qs = self.get_queryset().order_by('member__firstname')
qs = self.get_queryset().order_by("member__firstname")
return qs.filter(Q(member__role=role))
@@ -82,19 +92,19 @@ class JobGroupManager(models.Manager):
class MemberManager(models.Manager):
def get_queryset(self):
return super().get_queryset().order_by('firstname')
return super().get_queryset().order_by("firstname")
class Member(models.Model):
firstname = models.CharField("Vorname", max_length=128)
surname = models.CharField("Nachname", max_length=128)
nickname = models.CharField("Spitzname", max_length=128)
nickname = models.CharField("Spitzname", max_length=128)
username = models.CharField("Benutzername", blank=True, max_length=128)
mailaccount = models.CharField("Mailadresse", unique=True, max_length=128)
class MemberRole(models.TextChoices):
ACTIVE = 'A', _('Active')
PENSION = 'P', _('Pension')
ACTIVE = "A", _("Active")
PENSION = "P", _("Pension")
role = models.CharField(
"Rolle",
@@ -104,14 +114,16 @@ class Member(models.Model):
)
description = models.TextField(null=True, blank=True)
image = ThumbnailerImageField(upload_to='uploads/members/image/')
image = ThumbnailerImageField(upload_to="uploads/members/image/")
birthday = models.DateField(null=True, blank=True)
phone_error_msg = _((
"Phone number must be entered in the format: +999999999'. Up to 15 digits allowed."
))
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message=phone_error_msg)
phone_error_msg = _(
(
"Phone number must be entered in the format: +999999999'. Up to 15 digits allowed."
)
)
phone_regex = RegexValidator(regex=r"^\+?1?\d{9,15}$", message=phone_error_msg)
phone = models.CharField(validators=[phone_regex], max_length=17, blank=True)
address = models.TextField(null=True, blank=True)
@@ -134,15 +146,12 @@ class Member(models.Model):
if self.image.height < 150 or self.image.width < 150:
raise ValidationError(
_("Das Bild ist zu klein. (Höhe: {}, Breite: {})").format(
self.image.height,
self.image.width
self.image.height, self.image.width
)
)
if not "@fet.at" in self.mailaccount:
raise ValidationError(
_("In der Mailadresse fehlt die Domäne.")
)
raise ValidationError(_("In der Mailadresse fehlt die Domäne."))
def __str__(self):
return self.firstname + " " + self.surname
@@ -198,7 +207,7 @@ class Job(models.Model):
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.shortterm)
#if type(self.job_group) = str:
# if type(self.job_group) = str:
# self.job_group=JobGroup.objects.filter(slug=self.job)
super().save(*args, **kwargs)
@@ -218,18 +227,20 @@ class JobMember(models.Model):
verbose_name="Tätigkeit",
)
job_start = models.DateField('Job Start')
job_end = models.DateField('Job Ende', null=True, blank=True)
job_start = models.DateField("Job Start")
job_end = models.DateField("Job Ende", null=True, blank=True)
class JobRole(models.TextChoices):
PRESIDENT = ('10', _('VorsitzendeR'))
VICE_PRESIDENT = ('20', _('stv VorsitzendeR'))
SECOND_VICE_PRESIDENT = ('30', _('2. stv VorsitzendeR'))
PERSON_RESPONSIBLE = ('40', _('VerantwortlicheR'))
MEMBER = ('50', _('Mitglied'))
SUBSTITUTE_MEMBER = ('60', _('Ersatzmitglied'))
PRESIDENT = ("10", _("VorsitzendeR"))
VICE_PRESIDENT = ("20", _("stv VorsitzendeR"))
SECOND_VICE_PRESIDENT = ("30", _("2. stv VorsitzendeR"))
PERSON_RESPONSIBLE = ("40", _("VerantwortlicheR"))
MEMBER = ("50", _("Mitglied"))
SUBSTITUTE_MEMBER = ("60", _("Ersatzmitglied"))
job_role = models.CharField(max_length=2, choices=JobRole.choices, default=JobRole.MEMBER)
job_role = models.CharField(
max_length=2, choices=JobRole.choices, default=JobRole.MEMBER
)
objects = models.Manager()
members = JobMemberManager()

View File

@@ -4,65 +4,50 @@ from rest_framework import serializers
class MemberSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Member
fields = ['id',
'firstname',
'surname',
'nickname',
'mailaccount',
'role',
'description',
'image',
'birthday',
fields = [
"id",
"firstname",
"surname",
"nickname",
"mailaccount",
"role",
"description",
"image",
"birthday",
]
class JobGroupSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = JobGroup
fields = [
'id',
'name',
'shortterm',
'slug'
]
fields = ["id", "name", "shortterm", "slug"]
class JobSerializer(serializers.HyperlinkedModelSerializer):
#job_group = JobGroupSerializer()
# job_group = JobGroupSerializer()
job_group = serializers.SlugRelatedField(
slug_field='slug',queryset = JobGroup.objects
)
slug_field="slug", queryset=JobGroup.objects
)
class Meta:
model = Job
fields = [
'id',
'name',
'shortterm',
'job_group',
'slug'
]
fields = ["id", "name", "shortterm", "job_group", "slug"]
class JobMemberSerializer(serializers.HyperlinkedModelSerializer):
#member = MemberSerializer()
#job = JobSerializer()
job = serializers.SlugRelatedField(
slug_field='slug',queryset = Job.objects
)
member= serializers.SlugRelatedField(
slug_field='mailaccount',queryset = Member.objects
)
# member = MemberSerializer()
# job = JobSerializer()
job = serializers.SlugRelatedField(slug_field="slug", queryset=Job.objects)
member = serializers.SlugRelatedField(
slug_field="mailaccount", queryset=Member.objects
)
class Meta:
model = JobMember
fields = [
'id',
'job_start',
'job_end',
'member',
'job',
'job_role'
]
fields = ["id", "job_start", "job_end", "member", "job", "job_role"]
# def create(self, validated_data):
# member_data = validated_data.pop('member')

View File

@@ -4,14 +4,14 @@ from . import views
urlpatterns = [
path('', views.index, name='members'),
path('<str:filter>', views.members_view),
path("", views.index, name="members"),
path("<str:filter>", views.members_view),
]
member_urlpatterns = [
path('<str:member_id>', views.profile_view, name='member'),
path("<str:member_id>", views.profile_view, name="member"),
]
jobs_urlpatterns = [
path('<str:slug>', views.jobs_view, name='jobs'),
path("<str:slug>", views.jobs_view, name="jobs"),
]

View File

@@ -2,15 +2,12 @@ from django.http import Http404
from django.shortcuts import render
from collections import deque
from django_filters.rest_framework import DjangoFilterBackend
from .models import Member, JobMember, JobGroup, Job
from .serializers import MemberSerializer, JobSerializer, JobGroupSerializer, JobMemberSerializer
from rest_framework import viewsets
#from rest_framework import permissions
from posts.models import Post
import logging
logger = logging.getLogger(__name__)
@@ -45,20 +42,25 @@ def index(request):
pinned_job_groups, unpinned_job_groups = __get_job_groups()
members = deque(Member.all_members.all())
fs_info = Post.objects.filter(slug="fachschaft-info").first()
context = {
"pinned_job_groups": pinned_job_groups,
"unpinned_job_groups": unpinned_job_groups,
"members": members,
"fs_info": fs_info,
}
return render(request, 'members/index.html', context)
return render(request, "members/index.html", context)
def jobs_view(request, slug=None):
pinned_job_groups, unpinned_job_groups = __get_job_groups()
try:
description = JobGroup.all_jobgroups.filter(slug=slug).values().first()['description']
description = (
JobGroup.all_jobgroups.filter(slug=slug).values().first()["description"]
)
except Exception as e:
logger.info("Wrong job '{}'".format(slug))
raise Http404("wrong job")
@@ -72,7 +74,7 @@ def jobs_view(request, slug=None):
"job_members": job_members,
}
return render(request, 'members/index.html', context)
return render(request, "members/index.html", context)
def members_view(request, filter=None):
@@ -84,13 +86,16 @@ def members_view(request, filter=None):
logger.info("Wrong member role '{}'".format(filter))
raise Http404("no member role")
fs_info = Post.objects.filter(slug="fachschaft-info").first()
context = {
"pinned_job_groups": pinned_job_groups,
"unpinned_job_groups": unpinned_job_groups,
"members": members,
"fs_info": fs_info,
}
return render(request, 'members/index.html', context)
return render(request, "members/index.html", context)
def profile_view(request, member_id=None):
@@ -110,53 +115,4 @@ def profile_view(request, member_id=None):
"jobs": jobs,
}
return render(request, 'members/member.html', context)
class MemberViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = Member.all_members.order_by('nickname')
serializer_class = MemberSerializer
#permission_classes = [permissions.IsAuthenticated]
filter_backends = [DjangoFilterBackend]
filterset_fields = ['nickname','mailaccount']
# lookup_field = 'name'
def pre_save(self, obj):
obj.image = self.request.FILES.get('image')
class JobGroupViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = JobGroup.all_jobgroups.all()
serializer_class = JobGroupSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['name','slug']
#lookup_field = 'name'
# lookup_field = 'name'
class JobViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = Job.objects.all()
serializer_class = JobSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['name','slug']
#lookup_field = 'slug'
class JobMemberViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = JobMember.objects.all()
serializer_class = JobMemberSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['member','job','job_role','job_start']
#lookup_field = 'nickname'
return render(request, "members/member.html", context)

View File

@@ -0,0 +1,64 @@
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets
from .models import Member, JobMember, JobGroup, Job
from .serializers import (
MemberSerializer,
JobSerializer,
JobGroupSerializer,
JobMemberSerializer,
)
class MemberViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = Member.all_members.order_by("nickname")
serializer_class = MemberSerializer
# permission_classes = [permissions.IsAuthenticated]
filter_backends = [DjangoFilterBackend]
filterset_fields = ["nickname", "mailaccount"]
# lookup_field = 'name'
def pre_save(self, obj):
obj.image = self.request.FILES.get("image")
class JobGroupViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = JobGroup.all_jobgroups.all()
serializer_class = JobGroupSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ["name", "slug"]
# lookup_field = 'name'
class JobViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = Job.objects.all()
serializer_class = JobSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ["name", "slug"]
# lookup_field = 'slug'
class JobMemberViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = JobMember.objects.all()
serializer_class = JobMemberSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ["member", "job", "job_role", "job_start"]
# lookup_field = 'nickname'

View File

@@ -13,7 +13,7 @@ admin.site.unregister(taggit.models.Tag)
def make_fetmeeting(self, request, queryset):
qs = self.get_queryset(request).filter(id=request.POST['_selected_action']).first()
qs = self.get_queryset(request).filter(id=request.POST["_selected_action"]).first()
failed = False
agenda_key = None
@@ -24,7 +24,7 @@ def make_fetmeeting(self, request, queryset):
except Exception as e:
self.message_user(
request,
_('Das Agenda konnte nicht erstellt werden. Error: %s') % str(e),
_("Das Agenda konnte nicht erstellt werden. Error: %s") % str(e),
messages.ERROR,
)
failed = True
@@ -34,14 +34,14 @@ def make_fetmeeting(self, request, queryset):
except Exception as e:
self.message_user(
request,
_('Das Protokoll konnte nicht erstellt werden. Error: %s') % str(e),
_("Das Protokoll konnte nicht erstellt werden. Error: %s") % str(e),
messages.ERROR,
)
failed = True
if not failed:
queryset.update(
post_type='F',
post_type="F",
has_agenda=True,
has_protocol=True,
agenda_key=agenda_key,
@@ -49,7 +49,8 @@ def make_fetmeeting(self, request, queryset):
)
self.message_user(
request,
_('Das Event %s wurde erfolgreich in eine FET Sitzung konvertiert.') % (qs.title),
_("Das Event %s wurde erfolgreich in eine FET Sitzung konvertiert.")
% (qs.title),
messages.SUCCESS,
)
@@ -60,22 +61,27 @@ make_fetmeeting.short_description = "In eine Fachschaftssitzung konvertieren"
class PostAdmin(admin.ModelAdmin):
form = PostForm
model = Post
list_filter = ['is_pinned', 'is_hidden']
list_display = ['title', 'slug', 'public_date', 'is_pinned', 'is_hidden']
ordering = ['is_hidden', '-public_date']
list_filter = ["is_pinned", "is_hidden"]
list_display = ["title", "slug", "public_date", "is_pinned", "is_hidden"]
ordering = ["is_hidden", "-public_date"]
def add_view(self, request, form_url='', extra_context=None):
def add_view(self, request, form_url="", extra_context=None):
extra_context = extra_context or {}
extra_context['help_text'] = "Fette Schriften sind Pflichtfelder."
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."
return super().add_view(
request, form_url, extra_context=extra_context,
request,
form_url,
extra_context=extra_context,
)
def change_view(self, request, object_id, form_url='', extra_context=None):
def change_view(self, request, object_id, form_url="", extra_context=None):
extra_context = extra_context or {}
extra_context['help_text'] = "Fette Schriften sind Pflichtfelder."
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."
return super().change_view(
request, object_id, form_url, extra_context=extra_context,
request,
object_id,
form_url,
extra_context=extra_context,
)
def save_model(self, request, obj, form, change):
@@ -98,9 +104,9 @@ class PostAdmin(admin.ModelAdmin):
class EventAdmin(PostAdmin):
form = EventForm
model = Event
list_filter = ['is_pinned']
list_display = ['title', 'slug', 'event_start', 'public_date', 'is_pinned']
ordering = ['-event_start']
list_filter = ["is_pinned"]
list_display = ["title", "slug", "event_start", "public_date", "is_pinned"]
ordering = ["-event_start"]
actions = [make_fetmeeting]
@@ -113,7 +119,7 @@ class FetMeetingAdmin(EventAdmin):
form = FetMeetingForm
model = FetMeeting
list_filter = []
list_display = ['title', 'slug', 'event_start', 'public_date']
list_display = ["title", "slug", "event_start", "public_date"]
actions = []

View File

@@ -2,4 +2,4 @@ from django.apps import AppConfig
class PostsConfig(AppConfig):
name = 'posts'
name = "posts"

View File

@@ -10,14 +10,23 @@ from .models import Post, Event, News, FetMeeting
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'subtitle', 'tags', 'image', 'body', 'slug', 'author', 'public_date']
fields = [
"title",
"subtitle",
"tags",
"image",
"body",
"slug",
"author",
"public_date",
]
widgets = {'body': CKEditorUploadingWidget(config_name='default')}
widgets = {"body": CKEditorUploadingWidget(config_name="default")}
class Media:
js = (
'js/auto_slug.js', # automatic slag completion via ajax
'js/tag_completion.js', # to get a list for tag autocompletion via ajax
"js/auto_slug.js", # automatic slag completion via ajax
"js/tag_completion.js", # to get a list for tag autocompletion via ajax
)
@@ -25,106 +34,124 @@ class NewsForm(PostForm):
class Meta:
model = News
fields = [
'title', 'subtitle', 'tags', 'image', 'body', 'slug', 'author', 'public_date',
'is_pinned', 'is_hidden',
"title",
"subtitle",
"tags",
"image",
"body",
"slug",
"author",
"public_date",
"is_pinned",
"is_hidden",
]
labels = {
'title': _("Titel"),
'subtitle': _("Untertitel"),
'image': _("Hintergrundbild"),
'body': _("Text"),
'author': _("Autor"),
'public_date': _("Veröffentlichung"),
'is_pinned': _("Post anheften"),
'is_hidden': _("Post verstecken"),
"title": _("Titel"),
"subtitle": _("Untertitel"),
"image": _("Hintergrundbild"),
"body": _("Text"),
"author": _("Autor"),
"public_date": _("Veröffentlichung"),
"is_pinned": _("Post anheften"),
"is_hidden": _("Post verstecken"),
}
help_texts = {
'tags': _(
"tags": _(
"Die Hashtags ohne '#' eintragen, und mit Komma kann man mehrere Tags anfügen."
),
'image': _(
"Verwendbare Formate: ..."
),
'is_pinned': _(
"image": _("Verwendbare Formate: ..."),
"is_pinned": _(
"Dieser Post soll an die Startseite als erster Post angeheftet werden."
),
'is_hidden': _(
"is_hidden": _(
"Dieser Post soll im News Feed nicht auftauchen, z.B. Impressum."
),
}
widgets = {'body': CKEditorUploadingWidget(config_name='default')}
widgets = {"body": CKEditorUploadingWidget(config_name="default")}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # to get the self.fields set
self.fields['author'].queryset = self.fields['author'].queryset.order_by('username')
self.fields["author"].queryset = self.fields["author"].queryset.order_by(
"username"
)
class EventForm(PostForm):
class Meta:
model = Event
fields = [
'title', 'subtitle', 'tags', 'image', 'body', 'event_start', 'event_end',
'event_place', 'slug', 'author', 'public_date', 'is_pinned',
"title",
"subtitle",
"tags",
"image",
"body",
"event_start",
"event_end",
"event_place",
"slug",
"author",
"public_date",
"is_pinned",
]
labels = {
'title': _("Titel"),
'subtitle': _("Untertitel"),
'image': _("Hintergrundbild"),
'body': _("Text"),
'event_start': _("Start des Events"),
'event_end': _("Ende des Events"),
'event_place': _("Ort des Events"),
'author': _("Autor"),
'public_date': _("Veröffentlichung"),
'is_pinned': _("Event anheften"),
"title": _("Titel"),
"subtitle": _("Untertitel"),
"image": _("Hintergrundbild"),
"body": _("Text"),
"event_start": _("Start des Events"),
"event_end": _("Ende des Events"),
"event_place": _("Ort des Events"),
"author": _("Autor"),
"public_date": _("Veröffentlichung"),
"is_pinned": _("Event anheften"),
}
help_texts = {
'tags': _(
"tags": _(
"Die Hashtags ohne '#' eintragen, und mit Komma kann man mehrere Tags anfügen."
),
'image': _(
"Verwendbare Formate: "
),
'is_pinned': _(
"image": _("Verwendbare Formate: "),
"is_pinned": _(
"Dieses Event soll an die Startseite als erster Post angeheftet werden."
),
}
widgets = {'body': CKEditorUploadingWidget(config_name='default')}
widgets = {"body": CKEditorUploadingWidget(config_name="default")}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # to get the self.fields set
self.fields['author'].queryset = self.fields['author'].queryset.order_by('username')
self.fields["author"].queryset = self.fields["author"].queryset.order_by(
"username"
)
self.fields['event_start'].required = True
self.fields['event_end'].required = False
self.fields["event_start"].required = True
self.fields["event_end"].required = False
if 'event_place' in self.fields:
self.fields['event_place'].required = True
if "event_place" in self.fields:
self.fields["event_place"].required = True
class FetMeetingForm(PostForm):
# agenda_html = forms.CharField(widget = forms.TextInput())
class Meta:
model = FetMeeting
fields = ['event_start', 'event_end', 'tags']#, 'has_agenda', 'has_protocol']
fields = ["event_start", "event_end", "tags"] # , 'has_agenda', 'has_protocol']
labels = {
'event_start': _("Start der Sitzung"),
'event_end': _("Ende der Sitzung")#,
# 'has_agenda': _("Agenda"),
# 'has_protocol': _("Protokoll"),
"event_start": _("Start der Sitzung"),
"event_end": _("Ende der Sitzung") # ,
# 'has_agenda': _("Agenda"),
# 'has_protocol': _("Protokoll"),
}
help_texts = {
'tags': _(
"tags": _(
"Die Hashtags ohne '#' eintragen, und mit Komma kann man mehrere Tags anfügen."
)#,
) # ,
#'has_agenda': _("Agenda zur Sitzung hinzufügen."),
#'has_protocol': _("Protokoll zur Sitzung hinzufügen."),
}
@@ -132,13 +159,13 @@ class FetMeetingForm(PostForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # to get the self.fields set
self.fields['event_start'].required = True
self.fields['event_end'].required = False
self.fields["event_start"].required = True
self.fields["event_end"].required = False
#self.fields['has_agenda'].initial = True
#self.fields['has_protocol'].initial = True
# self.fields['has_agenda'].initial = True
# self.fields['has_protocol'].initial = True
tags = []
tags.append(Tag())
tags[0].name = "fachschaft"
self.fields['tags'].initial = tags
self.fields["tags"].initial = tags

View File

@@ -3,15 +3,22 @@ from django.core.mail import send_mail
def send_agenda_mail(date, time, slug):
msg = "Liebe Alle,\n\n" \
"wir haben am " + str(date) + " um " + str(time) + " wieder Sitzung.\n" \
"du hast noch bis morgen Zeit, weitere Themen auf die Agenda zu schreiben: " \
+ settings.HOST_NAME + '/posts/' + str(slug) + ".\n\n" \
msg = (
"Liebe Alle,\n\n"
"wir haben am " + str(date) + " um " + str(time) + " wieder Sitzung.\n"
"du hast noch bis morgen Zeit, weitere Themen auf die Agenda zu schreiben: "
+ settings.HOST_NAME
+ "/posts/"
+ str(slug)
+ ".\n\n"
"LG deine FET"
)
send_mail(
subject = 'Test - Agenda der FET Sitzung von ' + str(date),
message = msg,
from_email = 'patrick@fet.at',
recipient_list = ['all@fet.at', ],
subject="Test - Agenda der FET Sitzung von " + str(date),
message=msg,
from_email="patrick@fet.at",
recipient_list=[
"all@fet.at",
],
)

View File

@@ -5,23 +5,32 @@ from django.utils import timezone
class PostManager(models.Manager):
def get_queryset(self):
return super().get_queryset().order_by('-public_date')
return super().get_queryset().order_by("-public_date")
def get_visible_articles(self):
return self.get_queryset().filter(is_hidden=False)
def all_post_with_date(self):
return self.get_queryset().filter(Q(event_start__isnull=False) & Q(event_end__isnull=False)).order_by('-event_start')
return (
self.get_queryset()
.filter(Q(event_start__isnull=False) & Q(event_end__isnull=False))
.order_by("-event_start")
)
class ArticleManager(models.Manager):
"""
Provide a query set only for "Article"
regular fet meetings should not be contained in the news stream
"""
def get_queryset(self):
return super().get_queryset().filter(
Q(post_type='E')
| Q(post_type='N')
).order_by('-public_date')
return (
super()
.get_queryset()
.filter(Q(post_type="E") | Q(post_type="N"))
.order_by("-public_date")
)
def get_visible_articles(self):
return self.get_queryset().filter(is_hidden=False)
@@ -34,8 +43,9 @@ class NewsManager(models.Manager):
"""
Provide a query set only for "News"
"""
def get_queryset(self):
return super().get_queryset().filter(post_type='N').order_by('-public_date')
return super().get_queryset().filter(post_type="N").order_by("-public_date")
def get_visible_articles(self):
return self.get_queryset().filter(is_hidden=False)
@@ -45,12 +55,17 @@ class AllEventManager(models.Manager):
"""
Provide a query set for all events ("Event" and "Fet Meeting")
"""
def get_queryset(self):
return super().get_queryset().filter(Q(post_type='E') | Q(post_type='F'))
return super().get_queryset().filter(Q(post_type="E") | Q(post_type="F"))
def get_five_events(self):
date_today = timezone.now()
return self.get_queryset().filter(event_start__gt=date_today).order_by('event_start')[:5]
return (
self.get_queryset()
.filter(event_start__gt=date_today)
.order_by("event_start")[:5]
)
class EventManager(models.Manager):
@@ -58,32 +73,50 @@ class EventManager(models.Manager):
Provide a query set only for "Events"
regular fet meetings should not be contained in the news stream
"""
def get_queryset(self):
return super().get_queryset().filter(post_type='E')
return super().get_queryset().filter(post_type="E")
def get_future_events(self):
date_today = timezone.now()
return self.get_queryset().filter(event_start__gt=date_today).order_by('event_start')
return (
self.get_queryset()
.filter(event_start__gt=date_today)
.order_by("event_start")
)
def get_past_events(self):
date_today = timezone.now()
return self.get_queryset().filter(event_start__lt=date_today).order_by('-event_start')
return (
self.get_queryset()
.filter(event_start__lt=date_today)
.order_by("-event_start")
)
class FetMeetingManager(models.Manager):
"""
Provide a query set only for "Fet Meeting"
"""
def get_queryset(self):
return super().get_queryset().filter(post_type='F')
return super().get_queryset().filter(post_type="F")
def _get_future_events(self):
date_today = timezone.now()
return self.get_queryset().filter(event_start__gt=date_today).order_by('event_start')
return (
self.get_queryset()
.filter(event_start__gt=date_today)
.order_by("event_start")
)
def _get_past_events(self):
date_today = timezone.now()
return self.get_queryset().filter(event_start__lt=date_today).order_by('-event_start')
return (
self.get_queryset()
.filter(event_start__lt=date_today)
.order_by("-event_start")
)
def get_meetings(self):
meetings = []

View File

@@ -133,6 +133,7 @@ class Post(models.Model):
)
html = None
return html
@property
def protocol_html(self):
"Protocol HTML from Etherpad Pad"
@@ -196,7 +197,7 @@ class Post(models.Model):
def get_agenda_key(self):
"""Create a Etherpad Id for the Pad associated to this post.
Creates the pad if it doesn't exist"""
Creates the pad if it doesn't exist"""
if not self.slug:
return None
return create_pad_for_post(self.slug, typ="agenda")
@@ -255,9 +256,10 @@ class Post(models.Model):
def clean(self):
if self.event_end and self.event_end < self.event_start:
raise ValidationError(_("Das Ende des Events liegt vor dem Beginn."))
if self.event_start and self.post_type not in ['E','F']:
if self.event_start and self.post_type not in ["E", "F"]:
raise ValidationError("Für diesen Post Typ ist kein Event Start zulässig")
super().clean()
def save(self, *args, **kwargs):
# save the post with some defaults
if not self.public_date:
@@ -307,9 +309,9 @@ class Event(Post):
verbose_name = "Event"
verbose_name_plural = "Events"
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.post_type='E'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.post_type = "E"
def clean(self):
if not self.event_start:
@@ -334,10 +336,10 @@ class FetMeeting(Event):
verbose_name = "Fet Sitzung"
verbose_name_plural = "Fet Sitzungen"
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.post_type='F'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.post_type = "F"
def __get_slug(self):
slug = slugify(self.event_start.date()) + "-" + slugify("Fachschaftssitzung")
@@ -349,48 +351,47 @@ class FetMeeting(Event):
return slug
# def __get_agenda_key(self):
# if not self.slug:
# return None##
#
# try:
# agenda_key = createPadifNotExists(self.slug + "-agenda")
# except Exception as error:
# raise ValidationError(
# _("Die Agenda konnte nicht erstellt werden. Error: %(error)s"),
# params={"error": str(error)},
# ) from error
#
# return agenda_key
# def __get_agenda_key(self):
# if not self.slug:
# return None##
#
# try:
# agenda_key = createPadifNotExists(self.slug + "-agenda")
# except Exception as error:
# raise ValidationError(
# _("Die Agenda konnte nicht erstellt werden. Error: %(error)s"),
# params={"error": str(error)},
# ) from error
#
# return agenda_key
# def __get_protocol_key(self):
# if not self.slug:
# return None#
#
# try:
# protocol_key = createPadifNotExists(self.slug + "-protocol")
# except URLError as error:
# raise ValidationError(
# _("Das Protokoll konnte nicht erstellt werden. Error: %(error)s"),
# params={"error": str(error)},
# ) from error#
#
# return protocol_key
# def __get_protocol_key(self):
# if not self.slug:
# return None#
#
# try:
# protocol_key = createPadifNotExists(self.slug + "-protocol")
# except URLError as error:
# raise ValidationError(
# _("Das Protokoll konnte nicht erstellt werden. Error: %(error)s"),
# params={"error": str(error)},
# ) from error#
#
# return protocol_key
def clean(self):
super().clean()
if not self.slug:
self.slug = self.__get_slug()
def save(self, *args, **kwargs):
self.title = "Fachschaftssitzung"
if not self.slug:
self.slug = self.__get_slug()
self.has_agenda = True
self.has_protocol = True
self.agenda_key = self.get_agenda_key()
self.protocol_key = self.get_protocol_key()

View File

@@ -5,33 +5,34 @@ from rest_framework import serializers
class PostSerializer(serializers.HyperlinkedModelSerializer):
agenda_html = serializers.CharField(required=False)
tag_string = serializers.CharField(required=False,read_only=True)
imageurl = serializers.CharField(required=False,read_only=True)
tag_string = serializers.CharField(required=False, read_only=True)
imageurl = serializers.CharField(required=False, read_only=True)
class Meta:
model = Post
fields = [
'slug',
'title',
'subtitle',
'body',
'url',
'post_type',
'public_date',
'legacy_id',
'image',
'event_start',
'event_end',
'is_hidden',
'agenda_html',
'protocol_html',
'has_agenda',
'has_protocol',
'tag_string',
'imageurl'
"slug",
"title",
"subtitle",
"body",
"url",
"post_type",
"public_date",
"legacy_id",
"image",
"event_start",
"event_end",
"is_hidden",
"agenda_html",
"protocol_html",
"has_agenda",
"has_protocol",
"tag_string",
"imageurl"
# 'author',
]
extra_kwargs={
'agenda_html': {"required": False},
'protocol_html': {"required": False}
extra_kwargs = {
"agenda_html": {"required": False},
"protocol_html": {"required": False},
}

View File

@@ -3,10 +3,10 @@ from . import views
urlpatterns = [
path('func/tag_complete', views.tag_complete),
path('func/slug_calc', views.slug_calc),
path('t/<str:tag>', views.tags, name='posts.tags'),
path('', views.index, name='posts.index'),
path('fet_calendar.ics', views.calendar, name='posts.calendar'),
path('<str:id>', views.show, name='posts.show'),
path("func/tag_complete", views.tag_complete),
path("func/slug_calc", views.slug_calc),
path("t/<str:tag>", views.tags, name="posts.tags"),
path("", views.index, name="posts.index"),
path("fet_calendar.ics", views.calendar, name="posts.calendar"),
path("<str:id>", views.show, name="posts.show"),
]

View File

@@ -1,13 +1,11 @@
from collections import deque
import logging
from rest_framework import viewsets
from taggit.models import Tag
from django.shortcuts import render
from django.http import HttpResponse, JsonResponse, HttpResponseServerError
from django.utils.text import slugify
from django.utils import timezone
from django_filters.rest_framework import DjangoFilterBackend
from documents.api import get_pad_link
from documents.etherpadlib import add_ep_cookie
@@ -15,7 +13,8 @@ from members.models import Member, JobMember
from .models import Post, FetMeeting
from .serializers import PostSerializer
logger = logging.getLogger(__name__)
@@ -28,7 +27,7 @@ def index(request):
"Index von aktuellen Posts"
posts = deque(Post.objects.get_visible_articles().order_by("-public_date"))
taglist = map(lambda post : post.tags, posts)
taglist = map(lambda post: post.tags, posts)
return render(request, "posts/index.html", {"posts": posts, "tags_list": taglist})
@@ -36,7 +35,7 @@ def index(request):
def calendar(request):
"Kalender Ansicht ICS zur Verknüpfung mit Outlook"
events = deque(Post.objects.all_post_with_date().all())
return render(
request,
"posts/fet_calendar.ics",
@@ -49,7 +48,6 @@ def tags(request, tag=""):
posts = deque(Post.objects.get_visible_articles().filter(tags__name=tag))
featured_post = Post.objects.get_visible_articles().filter(slug=tag).first()
job_members = JobMember.active_member.get_all_by_slug(slug=tag)
author_image = None
@@ -87,25 +85,21 @@ def show(request, id=None):
ep_agenda_link = "#"
ep_protocol_link = "#"
if p.has_agenda:# and p.agenda_key:
if p.has_agenda: # and p.agenda_key:
try:
ep_agenda_link = get_pad_link(p.agenda_key)
except Exception as e:
logger.error(
"Can't get the agenda link from '%s'. Error: %s",
p.agenda_key,
e
"Can't get the agenda link from '%s'. Error: %s", p.agenda_key, e
)
ep_agenda_link = "#"
if p.has_protocol:# and p.protocol_key:
if p.has_protocol: # and p.protocol_key:
try:
ep_protocol_link = get_pad_link(p.protocol_key)
except Exception as e:
logger.error(
"Can't get the protocol link from '%s. Error: %s",
p.protocol_key, e
"Can't get the protocol link from '%s. Error: %s", p.protocol_key, e
)
ep_protocol_link = "#"
@@ -126,8 +120,7 @@ def show(request, id=None):
try:
response = add_ep_cookie(request, response)
except Exception as e:
logger.info(
"Etherpad Server doesn't work. Error: %s", e)
logger.info("Etherpad Server doesn't work. Error: %s", e)
return response
@@ -196,19 +189,3 @@ def get_next_dict(post=None):
break
return d
class PostViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = Post.objects.all().order_by("-public_date")
serializer_class = PostSerializer
# permission_classes = [permissions.IsAuthenticated]
filter_backends = [DjangoFilterBackend]
filterset_fields = ["legacy_id", "slug", "legacy_rubrik_id"]
lookup_field = "slug"
def pre_save(self, obj):
obj.image = self.request.FILES.get("image")

21
fet2020/posts/viewsets.py Normal file
View File

@@ -0,0 +1,21 @@
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets
from .models import Post, FetMeeting
from .serializers import PostSerializer
class PostViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = Post.objects.all().order_by("-public_date")
serializer_class = PostSerializer
# permission_classes = [permissions.IsAuthenticated]
filter_backends = [DjangoFilterBackend]
filterset_fields = ["legacy_id", "slug", "legacy_rubrik_id"]
lookup_field = "slug"
def pre_save(self, obj):
obj.image = self.request.FILES.get("image")

View File

@@ -1,4 +1,4 @@
django==3.1.4
django==3.1.5
django-ckeditor==6.0.0
django-crontab==0.7.1
django-environ==0.4.5

18
fet2020/static/fet.css Normal file
View File

@@ -0,0 +1,18 @@
body {
min-height:100%;
position:relative;
}
img.logo {
height:40px;
width:40px;
}
div.footer {
height: 100%;
}
.footer a {
color: white;
}
.footer a i {
font-size:30px;
}

View File

@@ -8,23 +8,33 @@ class TaskAdmin(admin.ModelAdmin):
form = TaskAdminForm
model = Task
fieldsets = (
(None, {
'fields': (
'title',
'task_list',
'assigned_to',
'due_date',
'completed',
'completed_date',
'note',
'priority',
)
}),
(
None,
{
"fields": (
"title",
"task_list",
"assigned_to",
"due_date",
"completed",
"completed_date",
"note",
"priority",
)
},
),
)
list_display = ['title', 'task_list', 'assigned_to', 'due_date', 'completed', 'priority']
list_filter = ('task_list', )
search_fields = ('title', )
list_display = [
"title",
"task_list",
"assigned_to",
"due_date",
"completed",
"priority",
]
list_filter = ("task_list",)
search_fields = ("title",)
def save_model(self, request, obj, form, change):
obj.created_by = request.user

View File

@@ -2,4 +2,4 @@ from django.apps import AppConfig
class TasksConfig(AppConfig):
name = 'tasks'
name = "tasks"

View File

@@ -7,29 +7,31 @@ from .models import Task, TaskList
class DateInput(forms.DateInput):
input_type = 'date'
input_type = "date"
class TaskAdminForm(forms.ModelForm):
class Meta:
model = Task
fields = '__all__'
fields = "__all__"
labels = {
'title': _('Titel'),
'task_list': _('Aufgabenbereich'),
'due_date': _('Fälligkeit'),
'completed': _('Abgeschlossen'),
'completed_date': _('Datum der Fertigstellung'),
'assigned_to': _('Zuweisen an'),
'note': _('Notizen'),
'priority': _('Priorität'),
"title": _("Titel"),
"task_list": _("Aufgabenbereich"),
"due_date": _("Fälligkeit"),
"completed": _("Abgeschlossen"),
"completed_date": _("Datum der Fertigstellung"),
"assigned_to": _("Zuweisen an"),
"note": _("Notizen"),
"priority": _("Priorität"),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # to get the self.fields set
self.fields['assigned_to'].empty_label = "Alle"
self.fields['assigned_to'].queryset = self.fields['assigned_to'].queryset.order_by('username')
self.fields["assigned_to"].empty_label = "Alle"
self.fields["assigned_to"].queryset = self.fields[
"assigned_to"
].queryset.order_by("username")
class TaskForm(forms.ModelForm):
@@ -37,39 +39,41 @@ class TaskForm(forms.ModelForm):
model = Task
fields = [
'title',
'task_list',
'due_date',
'assigned_to',
"title",
"task_list",
"due_date",
"assigned_to",
]
labels = {
'title': _('Titel des Tasks'),
'task_list': _('Task-Gruppe'),
'due_date': _('Fälligkeitsdatum'),
'assigned_to': _('Zuweisen an'),
"title": _("Titel des Tasks"),
"task_list": _("Task-Gruppe"),
"due_date": _("Fälligkeitsdatum"),
"assigned_to": _("Zuweisen an"),
}
widgets = {
'due_date': DateInput(
format=('%d-%m-%Y'),
"due_date": DateInput(
format=("%d-%m-%Y"),
)
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # to get the self.fields set
self.fields['assigned_to'].empty_label = "Alle"
self.fields['assigned_to'].queryset = self.fields['assigned_to'].queryset.order_by('username')
self.fields["assigned_to"].empty_label = "Alle"
self.fields["assigned_to"].queryset = self.fields[
"assigned_to"
].queryset.order_by("username")
class TaskListForm(forms.ModelForm):
users = forms.ModelMultipleChoiceField(
label="Benutzer",
help_text="Es können nur die Benutzer ausgewählt werden, die sich auf der Homepage angemeldet haben.",
queryset=User.objects.all().order_by('username'),
widget=FilteredSelectMultiple("User", is_stacked=False)
queryset=User.objects.all().order_by("username"),
widget=FilteredSelectMultiple("User", is_stacked=False),
)
class Meta:
model = TaskList
fields = '__all__'
fields = "__all__"

View File

@@ -7,7 +7,7 @@ from django.utils import timezone
class TaskQuerySet(models.QuerySet):
def get_ordered(self):
return self.order_by('task_list')
return self.order_by("task_list")
class TaskManager(models.Manager):
@@ -16,7 +16,11 @@ class TaskManager(models.Manager):
qs = self.get_queryset().get_ordered().filter(assigned_to__id=user)
if all_tasks:
qs_tmp = self.get_queryset().get_ordered().filter(Q(assigned_to=None) & Q(task_list__users__id__exact=user))
qs_tmp = (
self.get_queryset()
.get_ordered()
.filter(Q(assigned_to=None) & Q(task_list__users__id__exact=user))
)
qs = (qs | qs_tmp).distinct()
if not completed:
@@ -24,7 +28,7 @@ class TaskManager(models.Manager):
if task_list:
qs = qs.filter(task_list=task_list)
return qs
def get_queryset(self):
@@ -49,7 +53,9 @@ class TaskList(models.Model):
class Task(models.Model):
title = models.CharField(verbose_name="Titel", max_length=140)
task_list = models.ForeignKey(TaskList, verbose_name="Aufgabenbereich", on_delete=models.CASCADE, null=True)
task_list = models.ForeignKey(
TaskList, verbose_name="Aufgabenbereich", on_delete=models.CASCADE, null=True
)
created_date = models.DateTimeField(auto_now_add=True)
due_date = models.DateField(verbose_name="Fälligkeit", blank=True, null=True)
@@ -72,7 +78,9 @@ class Task(models.Model):
)
note = models.TextField(verbose_name="Notizen", blank=True, null=True)
priority = models.PositiveIntegerField(verbose_name="Priorität", blank=True, null=True)
priority = models.PositiveIntegerField(
verbose_name="Priorität", blank=True, null=True
)
objects = models.Manager()
taskmanager = TaskManager()

View File

@@ -4,5 +4,5 @@ from . import views
urlpatterns = [
path('', views.index, name='tasks'),
path("", views.index, name="tasks"),
]

View File

@@ -17,8 +17,8 @@ def index(request):
show_tasklist = None
show_all_tasks = True
if request.method == 'POST':
if 'btn_input' in request.POST:
if request.method == "POST":
if "btn_input" in request.POST:
form = TaskForm(request.POST)
if form.is_valid():
@@ -29,13 +29,18 @@ def index(request):
task.created_by = request.user
task.save()
else:
messages.info(request, "User '{}' ist nicht in der Liste von Task-Gruppe '{}'.".format(task.assigned_to, task.task_list.name))
messages.info(
request,
"User '{}' ist nicht in der Liste von Task-Gruppe '{}'.".format(
task.assigned_to, task.task_list.name
),
)
else:
task.created_by = request.user
task.save()
elif 'btn_checkbox' in request.POST:
for task_id in request.POST.getlist('checkbox'):
elif "btn_checkbox" in request.POST:
for task_id in request.POST.getlist("checkbox"):
task = Task.objects.get(id=task_id)
if not task.completed:
@@ -43,27 +48,31 @@ def index(request):
task.completed_date = timezone.now().date()
task.save()
elif 'btn_user' in request.POST:
if request.POST['action'] == 'show_incompleted':
elif "btn_user" in request.POST:
if request.POST["action"] == "show_incompleted":
current_action = False
else:
current_action = True
if request.POST['tasklist'] != 'all':
show_tasklist = TaskList.objects.filter(id=request.POST['tasklist']).first()
if request.POST["tasklist"] != "all":
show_tasklist = TaskList.objects.filter(
id=request.POST["tasklist"]
).first()
if request.POST['tasks'] == 'all':
if request.POST["tasks"] == "all":
show_all_tasks = True
else:
show_all_tasks = False
form = TaskForm()
tasks = deque(Task.taskmanager.get_tasks(
user=current_user,
completed=current_action,
task_list=show_tasklist,
all_tasks=show_all_tasks
))
tasks = deque(
Task.taskmanager.get_tasks(
user=current_user,
completed=current_action,
task_list=show_tasklist,
all_tasks=show_all_tasks,
)
)
tasklists = deque(TaskList.objects.all())
context = {
@@ -74,4 +83,4 @@ def index(request):
"current_action": current_action,
}
return render(request, 'tasks/index.html', context)
return render(request, "tasks/index.html", context)

View File

@@ -58,7 +58,7 @@
{% for post in events %}
{% include 'posts/partials/_date_box.html' %}
{% endfor %}
<a href="{% url 'posts.calendar' %}">ICS Kalendar</a>
FET-Ka­len­der abonnieren: <a href="{% url 'posts.calendar' %}">ICS Ka­len­der</a>
</div>
</div>

View File

@@ -8,11 +8,12 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FET</title>
<link rel="stylesheet" href="{% static 'app.css' %}">
<link rel="stylesheet" href="{% static 'fet.css' %}">
<script src="https://kit.fontawesome.com/dbe3c89a9d.js" crossorigin="anonymous"></script>
{% block extraheader %}
{% endblock %}
</head>
<body style="min-height:100%; position:relative">
<body style="">
<style>
footer {
@@ -28,7 +29,7 @@ footer {
<div class="title-bar-left">
<a href="{% url 'home' %}">
<img style="height:40px; width:40px" src="/assets/img/logo2014_64.png"/>
<img class="logo" src="/assets/img/logo2014_64.png"/>
</a>
</div>
@@ -43,26 +44,26 @@ footer {
<div class="top-bar-left show-for-large">
<a href="{% url 'home' %}">
<img style="height:40px; width:40px" src="/assets/img/logo2014_64.png"/>
<img class="logo" src="/assets/img/logo2014_64.png"/>
</a>
</div>
<div class="top-bar-right">
<ul class="dropdown vertical medium-horizontal menu" data-responsive-menu="drilldown medium-dropdown" data-animate-heigt="true">
{% if request.user.is_authenticated %}
<li><a role="menuitem" style="color: black; background: lightgrey">Hallo {{request.user.username}}</a></li>
<li><a role="menuitem" style="color: black; background: lightgrey">Hallo {{ request.user.username }}</a></li>
<li><a href="/admin" style="background: lightgrey;">Admin</a></li>
<li><a href="{%url 'tasks'%}" style="background: lightgrey;">Tasks</a></li>
<li><a href="{% url 'tasks' %}" style="background: lightgrey;">Tasks</a></li>
{% endif %}
<li><a href="{% url 'posts.index' %}">Aktuelles</a></li>
<!-- show active members first -->
<li><a href="{% url 'members'%}A">Fachschaft</a></li>
<li><a href="{% url 'members' %}A">Fachschaft</a></li>
<li><a href="/fotos/">Fotos</a></li>
<li><a href="/blackboard">Blackboard</a></li>
{% if request.user.is_authenticated %}
<li><a href="{%url 'logout'%}">Logout</a></li>
<li><a href="{% url 'logout' %}">Logout</a></li>
{% else %}
<li><a href="{%url 'login'%}">Login</a></li>
<li><a href="{% url 'login' %}?next={{ request.path }}">Login</a></li>
{% endif %}
</ul>
</div>
@@ -76,17 +77,17 @@ footer {
<div class="grid-container">
<div class="grid-x padding-top-1 padding-bottom-1">
<div class="cell medium-6 large-9">
<div class="grid-y" style="height: 100%;">
<div class="grid-y" style="height: 100%;" class="footer">
<div class="cell small-6 medium-9 large-9">
<a href="{% url 'posts.show' 'impressum' %}" style="color: white">Impressum</a>
<a href="{% url 'posts.show' 'impressum' %}" >Impressum</a>
</div>
<div class="cell small-6 medium-3 large-3 padding-bottom-2">
<a href="https://www.facebook.com/FachschaftET"><i class="fab fa-facebook-square" style="font-size:30px; color: white;"></i></a>
<a href="https://www.instagram.com/fet_tuwien/"><i class="fab fa-instagram-square" style="font-size:30px; color: white;"></i></a>
<a href="https://discord.gg/7qRuuMA"><i class="fab fa-discord" style="font-size:30px; color: white;"></i></a>
<a href="https://t.me/FETInfo"><i class="fab fa-telegram" style="font-size:30px; color: white;"></i></a>
<a href="https://www.facebook.com/FachschaftET"><i class="fab fa-facebook-square" style="font-size:30px; color: white;"></i>Facebook</a>
<a href="https://www.instagram.com/fet_tuwien/"><i class="fab fa-instagram-square" style="font-size:30px; color: white;"></i>Instagram</a>
<a href="https://discord.gg/7qRuuMA"><i class="fab fa-discord" style="font-size:30px; color: white;"></i>Discord</a>
<a href="https://t.me/FETInfo"><i class="fab fa-telegram" style="font-size:30px; color: white;"></i>Telegram</a>
</div>
</div>
</div>

View File

@@ -62,7 +62,9 @@
{% if members %}
<div class="grid-x">
<div class="cell padding-top-1 padding-left-1 padding-right-1" style="background-color: white; text-align: justify;">
Die Fachschaft Elektrotechnik (kurz: FET), ist die offizielle Vertretung aller Studierenden auf der Fakultät für Elektrotechnik und Informationstechnik. Ehrenamtliche engagierte Studierende unterstützen dich in Anliegen und Fragen zum und rund ums Studium. Wir vertreten eure Interessen in den offiziellen Gremien der Universität und arbeiten an Studienplänen mit. Außerdem bieten wir ein Rahmenprogramm zum Studium in Form von Veranstaltungen und Festln. Wir freuen uns über Feedback und Anregungen, insbesondere von jenen, die gleich Nägel mit Köpfen machen und unser Team verstärken wollen oder ihre Themen und Meinungen in eine unserer Sitzungen einbringen möchten.
{% if fs_info %}
{{ fs_info.body|safe }}
{% endif %}
</div>
</div>

View File

@@ -25,7 +25,7 @@ UID:{{event.id}}
ORGANIZER;CN="Fachschaft Elektrotechnik":MAILTO:service@fet.at
LOCATION:Vienna
SUMMARY:{{ event.title }}
DESCRIPTION: {{ request.scheme }}://{{ request.get_host }}{{ event.url }} {{ even.title }}</a>
DESCRIPTION:{{ request.scheme }}://{{ request.get_host }}{{ event.url }} {{ even.title }}
CLASS:PUBLIC
DTSTART;TZID=Europe/Vienna:{{ event.event_start|date:'Ymd' }}T{{ event.event_start|time:'His' }}
DTEND;TZID=Europe/Vienna:{{ event.event_end|date:'Ymd' }}T{{ event.event_end|time:'His' }}