Merge branch 'master' of https://git.fet.at/bofh/fet2020
This commit is contained in:
14
Readme.md
14
Readme.md
@@ -59,3 +59,17 @@ python3 fet2020/manage.py collectstatic
|
||||
<code>
|
||||
python3 fet2020/manage.py check --deploy
|
||||
</code>
|
||||
|
||||
Code Formatting (zuerst isort und danach black anwenden):
|
||||
```bash
|
||||
isort **/*.py
|
||||
```
|
||||
```bash
|
||||
black **/*.py
|
||||
```
|
||||
```bash
|
||||
isort **/templatetags/*.py
|
||||
```
|
||||
```bash
|
||||
black **/templatetags/*.py
|
||||
```
|
||||
|
||||
1
assets/css/flowbite@1.5.5.css
Normal file
1
assets/css/flowbite@1.5.5.css
Normal file
File diff suppressed because one or more lines are too long
2
assets/js/flowbite@1.5.5.js
Normal file
2
assets/js/flowbite@1.5.5.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1,9 +1,12 @@
|
||||
import logging
|
||||
|
||||
import ldap3
|
||||
from ldap3 import HASHED_SALTED_SHA, MODIFY_REPLACE, Connection, Server
|
||||
from ldap3.core.exceptions import LDAPBindError
|
||||
from ldap3.utils.hashed import hashed
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
host = "ldap://juri.fet.htu.tuwien.ac.at"
|
||||
port = 389
|
||||
|
||||
|
||||
def authentication(username, password):
|
||||
@@ -11,20 +14,38 @@ def authentication(username, password):
|
||||
if password is None or password.strip() == "":
|
||||
return None
|
||||
|
||||
server_uri = "ldap://juri.fet.htu.tuwien.ac.at"
|
||||
server = ldap3.Server(server_uri, port=389, 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"
|
||||
|
||||
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)")
|
||||
for user in sorted(conn.entries):
|
||||
if f"DN: uid={username}" in str(user):
|
||||
return username
|
||||
c = Connection(server, user=userdn, password=password, auto_bind=True)
|
||||
if c.extend.standard.who_am_i():
|
||||
return username
|
||||
|
||||
except LDAPBindError as e:
|
||||
logger.info(f"Username does not exist. Error: {e}")
|
||||
logger.info(f"LDAP Bind error. Error: {e}")
|
||||
except Exception as e:
|
||||
logger.info(f"Connection to server lost. Error: {e}")
|
||||
logger.info(f"Auth exception. Error: {e}")
|
||||
|
||||
logger.info(f"This username has been typed: '{username}'")
|
||||
return None
|
||||
|
||||
|
||||
def change_password(username, old_password, new_password):
|
||||
server = Server(host, port=port, use_ssl=True)
|
||||
userdn = f"uid={username},ou=user,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at"
|
||||
|
||||
try:
|
||||
c = Connection(server, user=userdn, password=old_password, auto_bind=True)
|
||||
|
||||
hashed_password = hashed(HASHED_SALTED_SHA, new_password)
|
||||
c.modify(userdn, {"userPassword": [(MODIFY_REPLACE, [hashed_password])]})
|
||||
|
||||
return username
|
||||
|
||||
except LDAPBindError as e:
|
||||
logger.info(f"LDAP Bind error. Error: {e}")
|
||||
except Exception as e:
|
||||
logger.info(f"Auth change-password exception. Error: {e}")
|
||||
|
||||
return None
|
||||
|
||||
@@ -1,6 +1,49 @@
|
||||
from django import forms
|
||||
from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.validators import ValidationError
|
||||
|
||||
from .authentications import authentication, change_password
|
||||
|
||||
|
||||
class LoginForm(forms.Form):
|
||||
username = forms.CharField()
|
||||
password = forms.CharField(label="Passwort", widget=forms.PasswordInput())
|
||||
class LoginForm(AuthenticationForm):
|
||||
def clean(self):
|
||||
username = self.cleaned_data.get("username")
|
||||
password = self.cleaned_data.get("password")
|
||||
|
||||
if username is not None and password:
|
||||
auth_user = authentication(username, password)
|
||||
|
||||
if auth_user:
|
||||
try:
|
||||
self.user_cache = User.objects.get(username=auth_user.lower())
|
||||
except User.DoesNotExist:
|
||||
self.user_cache = User.objects.create_user(auth_user.lower())
|
||||
else:
|
||||
raise self.get_invalid_login_error()
|
||||
|
||||
if self.user_cache is None:
|
||||
raise self.get_invalid_login_error()
|
||||
else:
|
||||
self.confirm_login_allowed(self.user_cache)
|
||||
|
||||
return self.cleaned_data
|
||||
|
||||
|
||||
class LdapPasswordChangeForm(PasswordChangeForm):
|
||||
def clean_old_password(self):
|
||||
old_password = self.cleaned_data["old_password"]
|
||||
if not authentication(self.user.username, old_password):
|
||||
raise ValidationError(
|
||||
self.error_messages["password_incorrect"],
|
||||
code="password_incorrect",
|
||||
)
|
||||
return old_password
|
||||
|
||||
def clean(self):
|
||||
old_password = self.cleaned_data["old_password"]
|
||||
new_password = self.cleaned_data["new_password1"]
|
||||
if not change_password(self.user, old_password, new_password):
|
||||
raise ValidationError("Passwort im LDAP ändern funktioniert nicht.")
|
||||
|
||||
def save(self):
|
||||
return self.user
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import apps
|
||||
from . import views
|
||||
from . import apps, views
|
||||
|
||||
app_name = apps.AuthenticationsConfig.name
|
||||
|
||||
urlpatterns = [
|
||||
path("login/", views.loginPage, name="login"),
|
||||
path("login/", views.AuthLoginView.as_view(), name="login"),
|
||||
path("logout/", views.logoutUser, name="logout"),
|
||||
path(
|
||||
"change-password/",
|
||||
views.LdapPasswordChangeView.as_view(),
|
||||
name="password_change",
|
||||
),
|
||||
path(
|
||||
"change-password/done/",
|
||||
views.LdapPasswordChangeDoneView.as_view(),
|
||||
name="password_change_done",
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,46 +1,22 @@
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import login, logout
|
||||
from django.contrib.auth.models import User
|
||||
from django.shortcuts import render, redirect
|
||||
from django.contrib.auth import logout
|
||||
from django.contrib.auth.views import (
|
||||
LoginView,
|
||||
PasswordChangeDoneView,
|
||||
PasswordChangeView,
|
||||
)
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse, reverse_lazy
|
||||
|
||||
from documents.etherpadlib import del_ep_cookie
|
||||
from .authentications import authentication
|
||||
from .decorators import unauthenticated_user, authenticated_user
|
||||
from .forms import LoginForm
|
||||
|
||||
from .decorators import authenticated_user
|
||||
from .forms import LdapPasswordChangeForm, LoginForm
|
||||
|
||||
|
||||
@unauthenticated_user
|
||||
def loginPage(request):
|
||||
if request.method == "POST":
|
||||
username = request.POST.get("username").lower()
|
||||
password = request.POST.get("password")
|
||||
|
||||
auth_user = authentication(username, password)
|
||||
|
||||
if auth_user:
|
||||
try:
|
||||
user = User.objects.get(username=auth_user.lower())
|
||||
except User.DoesNotExist:
|
||||
user = User.objects.create_user(auth_user.lower())
|
||||
|
||||
login(request, user)
|
||||
|
||||
try:
|
||||
return redirect(request.GET.get("next"))
|
||||
except:
|
||||
return redirect("home")
|
||||
else:
|
||||
messages.error(
|
||||
request,
|
||||
"Anmeldung nicht erfolgreich. Bitte überprüfe Benutzername und Passwort.",
|
||||
)
|
||||
|
||||
form = LoginForm()
|
||||
|
||||
context = {
|
||||
"form": form,
|
||||
}
|
||||
return render(request, "authentications/login.html", context)
|
||||
class AuthLoginView(LoginView):
|
||||
authentication_form = LoginForm
|
||||
redirect_authenticated_user = True
|
||||
template_name = "authentications/login.html"
|
||||
|
||||
|
||||
@authenticated_user
|
||||
@@ -55,3 +31,13 @@ def logoutUser(request):
|
||||
response = del_ep_cookie(request, response)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
class LdapPasswordChangeView(PasswordChangeView):
|
||||
form_class = LdapPasswordChangeForm
|
||||
success_url = reverse_lazy("authentications:password_change_done")
|
||||
template_name = "authentications/change_password.html"
|
||||
|
||||
|
||||
class LdapPasswordChangeDoneView(PasswordChangeDoneView):
|
||||
template_name = "authentications/change_password_done.html"
|
||||
|
||||
@@ -5,7 +5,6 @@ from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
logger = logging.getLogger("blackboard")
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import apps
|
||||
from . import views
|
||||
from . import apps, views
|
||||
|
||||
app_name = apps.BlackboardConfig.name
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import taggit.admin
|
||||
|
||||
from django.contrib import admin, auth
|
||||
from django.contrib.flatpages.admin import FlatPageAdmin
|
||||
from django.contrib.flatpages.models import FlatPage
|
||||
@@ -37,13 +36,13 @@ class CustomFlatPageAdmin(FlatPageAdmin):
|
||||
|
||||
|
||||
# Set the ordering of models in Admin Dashboard
|
||||
def get_app_list(self, request):
|
||||
def get_app_list(self, request, app_label=None):
|
||||
"""
|
||||
Return a sorted list of all the installed apps that have been
|
||||
registered in this site.
|
||||
"""
|
||||
# Retrieve the original list
|
||||
app_dict = self._build_app_dict(request)
|
||||
app_dict = self._build_app_dict(request, app_label)
|
||||
app_list = sorted(app_dict.values(), key=lambda x: x["name"].lower())
|
||||
|
||||
# Sort the models customably within each app.
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from ckeditor_uploader.widgets import CKEditorUploadingWidget
|
||||
|
||||
from django import forms
|
||||
|
||||
from .models import CustomFlatPage
|
||||
|
||||
0
fet2020/core/templatetags/__init__.py
Normal file
0
fet2020/core/templatetags/__init__.py
Normal file
10
fet2020/core/templatetags/version.py
Normal file
10
fet2020/core/templatetags/version.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from django import template
|
||||
|
||||
from fet2020 import __version__, build
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def version():
|
||||
return f"Version {__version__} Build {build}"
|
||||
@@ -3,10 +3,8 @@ import os
|
||||
from contextlib import contextmanager
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from etherpad_lite import EtherpadLiteClient, EtherpadException
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from etherpad_lite import EtherpadException, EtherpadLiteClient
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ from django.conf import settings
|
||||
from django.utils import timezone
|
||||
|
||||
from authentications.decorators import ep_authenticated_user
|
||||
|
||||
from .api import get_ep_client
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
from django.utils.version import get_version
|
||||
|
||||
VERSION = (1, 0, 0, "final", 0)
|
||||
BUILD = 0
|
||||
|
||||
__version__ = get_version(VERSION)
|
||||
build = BUILD
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import environ
|
||||
import os
|
||||
from urllib.parse import urljoin
|
||||
|
||||
import environ
|
||||
|
||||
env = environ.Env(
|
||||
# set casting, default value
|
||||
DEBUG=(bool, True),
|
||||
@@ -66,6 +67,7 @@ AUTHENTICATION_BACKENDS = [
|
||||
"django.contrib.auth.backends.ModelBackend",
|
||||
]
|
||||
|
||||
LOGIN_REDIRECT_URL = "home"
|
||||
LOGIN_URL = "/auth/login"
|
||||
|
||||
|
||||
@@ -102,7 +104,7 @@ MEDIA_ROOT = os.path.join(BASE_DIR, "files/")
|
||||
MEDIA_URL = "files/"
|
||||
|
||||
|
||||
# GLOBALIZATION
|
||||
# GLOBALIZATION
|
||||
LANGUAGE_CODE = "de-at"
|
||||
LOCALE_PATHS = [os.path.join(BASE_DIR, "locale")]
|
||||
TIME_ZONE = "CET"
|
||||
@@ -316,7 +318,7 @@ CKEDITOR_CONFIGS = {
|
||||
"-",
|
||||
"Blockquote",
|
||||
"CreateDiv",
|
||||
],
|
||||
],
|
||||
},
|
||||
"/",
|
||||
{
|
||||
@@ -326,15 +328,9 @@ CKEDITOR_CONFIGS = {
|
||||
"JustifyCenter",
|
||||
"JustifyRight",
|
||||
"JustifyBlock",
|
||||
],
|
||||
},
|
||||
{ "name": "links",
|
||||
"items": [
|
||||
"Link",
|
||||
"Unlink",
|
||||
"Anchor"
|
||||
]
|
||||
],
|
||||
},
|
||||
{"name": "links", "items": ["Link", "Unlink", "Anchor"]},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
from rest_framework import routers
|
||||
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from django.contrib import admin, flatpages
|
||||
from django.contrib.sitemaps.views import sitemap
|
||||
from django.urls import include, path, re_path
|
||||
from django.views.generic import RedirectView
|
||||
from rest_framework import routers
|
||||
|
||||
from posts.viewsets import PostViewSet
|
||||
from members.viewsets import (
|
||||
MemberViewSet,
|
||||
JobViewSet,
|
||||
JobGroupViewSet,
|
||||
JobMemberViewSet,
|
||||
JobViewSet,
|
||||
MemberViewSet,
|
||||
)
|
||||
from posts.viewsets import PostViewSet
|
||||
|
||||
from . import views
|
||||
from .sitemaps import sitemaps
|
||||
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
router.register(r"posts", PostViewSet)
|
||||
router.register(r"members", MemberViewSet)
|
||||
|
||||
@@ -25,5 +25,6 @@ def add_log_action(request, form, app_label, model, add=True):
|
||||
change_message=change_message,
|
||||
)
|
||||
|
||||
|
||||
def create_random_id():
|
||||
return str(uuid.uuid4())[:8]
|
||||
|
||||
@@ -2,19 +2,11 @@ from collections import deque
|
||||
|
||||
from django.shortcuts import render
|
||||
|
||||
from posts.models import Post, FetMeeting, Event
|
||||
from posts.models import Event, FetMeeting, Post
|
||||
|
||||
|
||||
def index(request):
|
||||
posts = deque(Post.articles.date_sorted_list())
|
||||
posts_for_tags = deque(Post.objects.get_last_months_posts())
|
||||
|
||||
def get_tags(lst):
|
||||
for p in lst:
|
||||
for t in list(p.tags.names()):
|
||||
yield "#" + t
|
||||
|
||||
t = set(t for t in get_tags(posts_for_tags))
|
||||
|
||||
# set the pinned post
|
||||
pinned_post = Post.articles.pinned()
|
||||
@@ -37,7 +29,6 @@ def index(request):
|
||||
"featured_post": pinned_post,
|
||||
"featured_event": featured_event,
|
||||
"featured_meeting": featured_meeting,
|
||||
"tags_list": " ".join(t),
|
||||
}
|
||||
|
||||
return render(request, "home.html", context)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from haystack import indexes
|
||||
|
||||
from django.utils import timezone
|
||||
from haystack import indexes
|
||||
|
||||
from .models import Album
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import apps
|
||||
from . import views
|
||||
from . import apps, views
|
||||
|
||||
app_name = apps.GalleryConfig.name
|
||||
|
||||
urlpatterns = [
|
||||
path("", views.index, name="index"),
|
||||
path("<slug:slug>/", views.show_album, name="album"),
|
||||
path("draft/<slug:slug>/", views.show_draft_album, name="draft-album"),
|
||||
path("draft/<slug:slug>/", views.show_draft_album, name="album_draft"),
|
||||
]
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import logging
|
||||
import os
|
||||
from PIL import Image, ExifTags, ImageOps
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from PIL import ExifTags, Image, ImageOps
|
||||
|
||||
gallery_path = settings.GALLERY["path"]
|
||||
gallery_thumb_path = settings.GALLERY["thumb_path"]
|
||||
|
||||
@@ -7,6 +7,7 @@ from django.shortcuts import render
|
||||
from django.utils.text import slugify
|
||||
|
||||
from authentications.decorators import authenticated_user
|
||||
|
||||
from .models import Album
|
||||
from .utils import create_thumbs, get_folder_list
|
||||
|
||||
|
||||
@@ -2,17 +2,17 @@ from django.contrib import admin
|
||||
from django.db.models import F
|
||||
|
||||
from .forms import (
|
||||
TopicGroupAdminForm,
|
||||
TopicAdminForm,
|
||||
AttachmentAdminForm,
|
||||
EtherpadAdminForm,
|
||||
FileUploadAdminForm,
|
||||
TopicInlineForm,
|
||||
AttachmentInlineForm,
|
||||
EtherpadAdminForm,
|
||||
EtherpadInlineForm,
|
||||
FileUploadAdminForm,
|
||||
FileUploadInlineForm,
|
||||
TopicAdminForm,
|
||||
TopicGroupAdminForm,
|
||||
TopicInlineForm,
|
||||
)
|
||||
from .models import TopicGroup, Topic, Attachment, Etherpad, FileUpload
|
||||
from .models import Attachment, Etherpad, FileUpload, Topic, TopicGroup
|
||||
|
||||
|
||||
class TopicInline(admin.TabularInline):
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from ckeditor.widgets import CKEditorWidget
|
||||
from ckeditor_uploader.widgets import CKEditorUploadingWidget
|
||||
|
||||
from django import forms
|
||||
from django.forms.widgets import HiddenInput
|
||||
|
||||
from tasks.models import Task, TaskList
|
||||
from .models import TopicGroup, Topic, Attachment, Etherpad, FileUpload
|
||||
|
||||
from .models import Attachment, Etherpad, FileUpload, Topic, TopicGroup
|
||||
|
||||
|
||||
class DateInput(forms.DateInput):
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from django.urls import include, path
|
||||
|
||||
from . import apps
|
||||
from . import views
|
||||
from . import apps, views
|
||||
from .views import (
|
||||
AttachmentCreateView,
|
||||
AttachmentDetailView,
|
||||
@@ -14,7 +13,6 @@ from .views import (
|
||||
TopicUpdateView,
|
||||
)
|
||||
|
||||
|
||||
app_name = apps.InternConfig.name
|
||||
|
||||
attachment_urlpatterns = [
|
||||
@@ -26,17 +24,17 @@ attachment_urlpatterns = [
|
||||
path(
|
||||
"update/",
|
||||
AttachmentUpdateView.as_view(),
|
||||
name="attachment-update",
|
||||
name="attachment_update",
|
||||
),
|
||||
path(
|
||||
"etherpad-create/",
|
||||
"create-etherpad/",
|
||||
EtherpadCreateView.as_view(),
|
||||
name="etherpad-create",
|
||||
name="etherpad_create",
|
||||
),
|
||||
path(
|
||||
"file-create/",
|
||||
"create-file/",
|
||||
FileUploadCreateView.as_view(),
|
||||
name="file-create",
|
||||
name="file_create",
|
||||
),
|
||||
]
|
||||
|
||||
@@ -45,22 +43,22 @@ topic_urlpatterns = [
|
||||
path(
|
||||
"update/",
|
||||
TopicUpdateView.as_view(),
|
||||
name="topic-update",
|
||||
name="topic_update",
|
||||
),
|
||||
path(
|
||||
"attachment-create/",
|
||||
"create-attachment/",
|
||||
AttachmentCreateView.as_view(),
|
||||
name="attachment-create",
|
||||
name="attachment_create",
|
||||
),
|
||||
path("create/", TaskCreateView.as_view(), name="task-create"),
|
||||
path("create-task/", TaskCreateView.as_view(), name="task_create"),
|
||||
]
|
||||
|
||||
urlpatterns = [
|
||||
path("", views.index, name="index"),
|
||||
path(
|
||||
"<slug:topic_group_slug>/topic-create/",
|
||||
"<slug:topic_group_slug>/create-topic/",
|
||||
TopicCreateView.as_view(),
|
||||
name="topic-create",
|
||||
name="topic_create",
|
||||
),
|
||||
path("<slug:topic_group_slug>/<slug:slug>/", include(topic_urlpatterns)),
|
||||
path(
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import logging
|
||||
|
||||
from collections import deque
|
||||
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.db.models import F, Q
|
||||
from django.shortcuts import render
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.views.generic.detail import DetailView
|
||||
from django.views.generic.edit import CreateView, UpdateView
|
||||
from django.urls import reverse_lazy, reverse
|
||||
|
||||
from authentications.decorators import authenticated_user
|
||||
from documents.etherpadlib import add_ep_cookie
|
||||
from fet2020.utils import add_log_action
|
||||
from tasks.forms import InternTaskCreateForm
|
||||
from tasks.models import Task
|
||||
|
||||
from .forms import (
|
||||
AttachmentCreateForm,
|
||||
AttachmentUpdateForm,
|
||||
@@ -21,7 +22,7 @@ from .forms import (
|
||||
TopicCreateForm,
|
||||
TopicUpdateForm,
|
||||
)
|
||||
from .models import TopicGroup, Topic, Attachment, Etherpad, FileUpload
|
||||
from .models import Attachment, Etherpad, FileUpload, Topic, TopicGroup
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .forms import JobForm, JobGroupForm, JobInlineForm, MemberForm
|
||||
from .forms import (
|
||||
ActiveMemberForm,
|
||||
InactiveMemberForm,
|
||||
JobForm,
|
||||
JobGroupForm,
|
||||
JobInlineForm,
|
||||
MemberForm,
|
||||
)
|
||||
from .models import Job, JobGroup, JobMember, Member
|
||||
|
||||
|
||||
@@ -28,8 +35,12 @@ class JobOverviewInline(JobMemberInline):
|
||||
verbose_name = "Tätigkeit"
|
||||
verbose_name_plural = "Tätigkeitsübersicht"
|
||||
|
||||
def get_queryset(self, request):
|
||||
return JobMember.members.get_all_jobs_sorted()
|
||||
|
||||
|
||||
class ActiveMemberInline(JobMemberInline):
|
||||
form = ActiveMemberForm
|
||||
verbose_name = "Mitglied"
|
||||
verbose_name_plural = "Aktive Mitglieder Liste"
|
||||
|
||||
@@ -38,6 +49,7 @@ class ActiveMemberInline(JobMemberInline):
|
||||
|
||||
|
||||
class InactiveMemberInline(JobMemberInline):
|
||||
form = InactiveMemberForm
|
||||
verbose_name = "Mitglied"
|
||||
verbose_name_plural = "Inaktive Mitglieder Liste"
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from ckeditor_uploader.widgets import CKEditorUploadingWidget
|
||||
|
||||
from django import forms
|
||||
|
||||
from .models import Member, Job, JobGroup
|
||||
from .models import Job, JobGroup, JobMember, Member
|
||||
|
||||
|
||||
class JobInlineForm(forms.ModelForm):
|
||||
@@ -11,6 +10,31 @@ class JobInlineForm(forms.ModelForm):
|
||||
fields = ["name"]
|
||||
|
||||
|
||||
class ActiveMemberForm(forms.ModelForm):
|
||||
class Meta:
|
||||
fields = "__all__"
|
||||
model = JobMember
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["member"].queryset = (
|
||||
self.fields["member"]
|
||||
.queryset.filter(role="A")
|
||||
.order_by("firstname", "surname")
|
||||
)
|
||||
|
||||
|
||||
class InactiveMemberForm(forms.ModelForm):
|
||||
class Meta:
|
||||
fields = "__all__"
|
||||
model = JobMember
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
member_qs = self.fields["member"].queryset.order_by("firstname", "surname")
|
||||
self.fields["member"].queryset = member_qs
|
||||
|
||||
|
||||
class MemberForm(forms.ModelForm):
|
||||
class Meta:
|
||||
fields = "__all__"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from datetime import timedelta
|
||||
|
||||
from django.db import models
|
||||
from django.db.models import Q
|
||||
from django.db.models import F, Q
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
@@ -12,12 +12,16 @@ class ActiveJobMemberManager(models.Manager):
|
||||
return (
|
||||
self.get_queryset()
|
||||
.filter(job__job_group__slug=slug)
|
||||
.order_by("job__slug", "job_role", "member__firstname")
|
||||
.order_by("job__slug", "job_role", "member__firstname", "member__surname")
|
||||
)
|
||||
|
||||
def get_queryset(self):
|
||||
date_today = timezone.now().date()
|
||||
qs = super().get_queryset().order_by("member__firstname")
|
||||
qs = (
|
||||
super()
|
||||
.get_queryset()
|
||||
.order_by("job_role", "member__firstname", "member__surname", "job_start")
|
||||
)
|
||||
|
||||
return qs.filter(
|
||||
Q(member__role="A") & (Q(job_end__gt=date_today) | Q(job_end__isnull=True))
|
||||
@@ -36,7 +40,11 @@ class InactiveJobMemberManager(models.Manager):
|
||||
|
||||
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", "member__surname", "-job_start")
|
||||
)
|
||||
|
||||
return qs.filter(
|
||||
Q(member__role="P")
|
||||
@@ -50,15 +58,29 @@ class JobMemberManager(models.Manager):
|
||||
|
||||
return qs.filter(Q(member__role=role))
|
||||
|
||||
def get_all_jobs_sorted(self):
|
||||
qs = self.get_queryset().order_by(
|
||||
F("job_end").desc(nulls_first=True), "-job_start", "job__name"
|
||||
)
|
||||
return qs
|
||||
|
||||
def get_active_jobs(self, member_id):
|
||||
date_today = timezone.now().date()
|
||||
qs = self.get_queryset().filter(member__id=member_id).order_by("-job_start")
|
||||
qs = (
|
||||
self.get_queryset()
|
||||
.filter(member__id=member_id)
|
||||
.order_by("-job_start", "job__name")
|
||||
)
|
||||
|
||||
return qs.filter(Q(job_end__gt=date_today) | Q(job_end__isnull=True))
|
||||
|
||||
def get_inactive_jobs(self, member_id):
|
||||
date_today = timezone.now().date()
|
||||
qs = self.get_queryset().filter(member__id=member_id).order_by("-job_start")
|
||||
qs = (
|
||||
self.get_queryset()
|
||||
.filter(member__id=member_id)
|
||||
.order_by("-job_start", "job__name")
|
||||
)
|
||||
|
||||
return qs.filter(
|
||||
Q(job_end__lt=date_today + timedelta(days=1)) & Q(job_end__isnull=False)
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import logging
|
||||
|
||||
from easy_thumbnails.fields import ThumbnailerImageField
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.validators import ValidationError, validate_email
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.utils.text import slugify
|
||||
from easy_thumbnails.fields import ThumbnailerImageField
|
||||
|
||||
from .managers import (
|
||||
ActiveJobMemberManager,
|
||||
@@ -157,7 +156,11 @@ class Job(models.Model):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse("members:jobs", kwargs={"slug": self.job_group.slug}) + "#" + self.slug
|
||||
return (
|
||||
reverse("members:jobs", kwargs={"slug": self.job_group.slug})
|
||||
+ "#"
|
||||
+ self.slug
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
if not self.shortterm:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from .models import Member, Job, JobGroup, JobMember
|
||||
from .models import Job, JobGroup, JobMember, Member
|
||||
|
||||
|
||||
class MemberSerializer(serializers.HyperlinkedModelSerializer):
|
||||
|
||||
0
fet2020/members/templatetags/__init__.py
Normal file
0
fet2020/members/templatetags/__init__.py
Normal file
29
fet2020/members/templatetags/job_groups.py
Normal file
29
fet2020/members/templatetags/job_groups.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from collections import deque
|
||||
|
||||
from django import template
|
||||
|
||||
from members.models import JobMember, JobGroup
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.inclusion_tag("members/partials/jobs_sidebar.html")
|
||||
def get_jobs_sidebar(slug):
|
||||
job_groups = deque(JobGroup.objects.all())
|
||||
|
||||
# remove job group if there is no longer an active member
|
||||
for elem in job_groups.copy():
|
||||
job_members = JobMember.active_member.get_all(slug=elem.slug)
|
||||
if not job_members:
|
||||
job_groups.remove(elem)
|
||||
|
||||
job_members = JobMember.active_member.get_all(slug=slug)
|
||||
active_job_group = JobGroup.objects.filter(slug=slug).first()
|
||||
|
||||
context = {
|
||||
"job_groups": job_groups,
|
||||
"job_members": job_members,
|
||||
"active_job_group": active_job_group,
|
||||
}
|
||||
|
||||
return context
|
||||
@@ -6,7 +6,6 @@ from django.test import TestCase
|
||||
from .forms import JobForm, JobGroupForm, MemberForm
|
||||
from .models import Job, JobGroup, Member
|
||||
|
||||
|
||||
image_path = os.path.join(os.path.dirname(__file__), "tests/files/peter.jpg")
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import apps
|
||||
from . import views
|
||||
from . import apps, views
|
||||
|
||||
app_name = apps.MembersConfig.name
|
||||
|
||||
|
||||
@@ -1,32 +1,17 @@
|
||||
from collections import deque
|
||||
import logging
|
||||
|
||||
from django.http import Http404
|
||||
from django.shortcuts import render
|
||||
|
||||
from .models import Member, JobMember, JobGroup
|
||||
from .models import JobGroup, JobMember, Member
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def __get_job_groups():
|
||||
job_groups = deque(JobGroup.objects.all())
|
||||
|
||||
# remove job group if there is no longer an active member
|
||||
for elem in job_groups.copy():
|
||||
job_members = JobMember.active_member.get_all(slug=elem.slug)
|
||||
if not job_members:
|
||||
job_groups.remove(elem)
|
||||
|
||||
return job_groups
|
||||
|
||||
|
||||
def index(request):
|
||||
job_groups = __get_job_groups()
|
||||
members = Member.all_members.filter(role=Member.MemberRole.ACTIVE)
|
||||
|
||||
context = {
|
||||
"job_groups": job_groups,
|
||||
"members": members,
|
||||
}
|
||||
|
||||
@@ -34,8 +19,6 @@ def index(request):
|
||||
|
||||
|
||||
def jobs(request, slug=None):
|
||||
job_groups = __get_job_groups()
|
||||
|
||||
try:
|
||||
description = JobGroup.objects.filter(slug=slug).values().first()["description"]
|
||||
except Exception:
|
||||
@@ -46,7 +29,6 @@ def jobs(request, slug=None):
|
||||
active_job_group = JobGroup.objects.filter(slug=slug).first()
|
||||
|
||||
context = {
|
||||
"job_groups": job_groups,
|
||||
"description": description,
|
||||
"job_members": job_members,
|
||||
"active_job_group": active_job_group,
|
||||
@@ -56,8 +38,6 @@ def jobs(request, slug=None):
|
||||
|
||||
|
||||
def members(request, role=None):
|
||||
job_groups = __get_job_groups()
|
||||
|
||||
for elem in Member.MemberRole:
|
||||
if role == elem.label.lower():
|
||||
members = Member.all_members.filter(role=elem.value)
|
||||
@@ -66,7 +46,6 @@ def members(request, role=None):
|
||||
members = Member.all_members.all()
|
||||
|
||||
context = {
|
||||
"job_groups": job_groups,
|
||||
"members": members,
|
||||
}
|
||||
|
||||
@@ -74,8 +53,6 @@ def members(request, role=None):
|
||||
|
||||
|
||||
def profile(request, member_id=None):
|
||||
job_groups = __get_job_groups()
|
||||
|
||||
member = Member.all_members.filter(id=member_id).first()
|
||||
if not member:
|
||||
logger.info("Wrong member id '{}'".format(member_id))
|
||||
@@ -85,7 +62,6 @@ def profile(request, member_id=None):
|
||||
inactive_jobs = JobMember.members.get_inactive_jobs(member_id)
|
||||
|
||||
context = {
|
||||
"job_groups": job_groups,
|
||||
"member": member,
|
||||
"active_jobs": active_jobs,
|
||||
"inactive_jobs": inactive_jobs,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework import viewsets
|
||||
|
||||
from .models import Member, JobMember, JobGroup, Job
|
||||
from .models import Job, JobGroup, JobMember, Member
|
||||
from .serializers import (
|
||||
MemberSerializer,
|
||||
JobSerializer,
|
||||
JobGroupSerializer,
|
||||
JobMemberSerializer,
|
||||
JobSerializer,
|
||||
MemberSerializer,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from django.contrib import admin, messages
|
||||
|
||||
from documents.api import create_pad
|
||||
|
||||
from .forms import EventForm, FetMeetingForm, NewsForm, PostForm
|
||||
from .models import Event, FetMeeting, FileUpload, News, Post
|
||||
|
||||
|
||||
@@ -7,9 +7,8 @@ from datetime import timedelta
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
from .models import FetMeeting
|
||||
from .mails import send_agenda_mail
|
||||
|
||||
from .models import FetMeeting
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
from ckeditor_uploader.widgets import CKEditorUploadingWidget
|
||||
from taggit.models import Tag
|
||||
|
||||
from django import forms
|
||||
from django.forms.widgets import CheckboxInput
|
||||
from django.forms.widgets import CheckboxInput, DateTimeInput
|
||||
from django.utils import timezone
|
||||
from django.utils.dates import MONTHS
|
||||
from taggit.models import Tag
|
||||
|
||||
from .models import Post, Event, News, FetMeeting
|
||||
from .models import Event, FetMeeting, News, Post
|
||||
|
||||
|
||||
class PostForm(forms.ModelForm):
|
||||
@@ -167,3 +166,94 @@ class PostSearchForm(forms.Form):
|
||||
self.fields["year"].choices = year_choices
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
class NewsUpdateForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = News
|
||||
|
||||
fields = [
|
||||
"title",
|
||||
"status",
|
||||
"body",
|
||||
]
|
||||
|
||||
labels = {
|
||||
"title": "Titel",
|
||||
"image": "Hintergrundbild",
|
||||
"body": "Text",
|
||||
}
|
||||
|
||||
widgets = {"body": CKEditorUploadingWidget(config_name="default")}
|
||||
|
||||
|
||||
class EventUpdateForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = [
|
||||
"title",
|
||||
"status",
|
||||
"event_start",
|
||||
"event_end",
|
||||
"event_place",
|
||||
]
|
||||
|
||||
labels = {
|
||||
"title": "Titel",
|
||||
"event_start": "Start des Events",
|
||||
"event_end": "Ende des Events",
|
||||
"event_place": "Ort des Events",
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if "event_place" in self.fields:
|
||||
self.fields["event_place"].required = True
|
||||
|
||||
|
||||
class FetMeetingCreateForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = FetMeeting
|
||||
fields = ["event_start", "event_end", "event_place", "tags"]
|
||||
|
||||
labels = {
|
||||
"event_start": "Start der Sitzung",
|
||||
"event_end": "Ende der Sitzung",
|
||||
"event_place": "Ort der Sitzung",
|
||||
}
|
||||
|
||||
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_place"].initial = "FET"
|
||||
|
||||
tags = []
|
||||
tags.append(Tag())
|
||||
tags[0].name = "fachschaft"
|
||||
self.fields["tags"].initial = tags
|
||||
|
||||
|
||||
class FetMeetingUpdateForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = FetMeeting
|
||||
fields = ["event_start", "event_end", "event_place"]
|
||||
|
||||
labels = {
|
||||
"event_start": "Start der Sitzung",
|
||||
"event_end": "Ende der Sitzung",
|
||||
"event_place": "Ort der Sitzung",
|
||||
}
|
||||
|
||||
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_place"].initial = "FET"
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import logging
|
||||
|
||||
from html2text import html2text
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.mail import send_mail
|
||||
|
||||
from html2text import html2text
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from django.db import models
|
||||
from django.db.models import Case, Manager, Q, When
|
||||
from django.utils import timezone
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from django.db import models
|
||||
from django.db.models import Case, Q, When
|
||||
from django.utils import timezone
|
||||
|
||||
class PublishedManager(Manager):
|
||||
|
||||
class PublishedManager(models.Manager):
|
||||
def published(self, public=True):
|
||||
"""
|
||||
publish all posts with status 'PUBLIC'
|
||||
@@ -27,7 +27,7 @@ class PublishedManager(Manager):
|
||||
return qs
|
||||
|
||||
|
||||
class PostManager(PublishedManager, Manager):
|
||||
class PostManager(PublishedManager, models.Manager):
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
qs = qs.annotate(
|
||||
@@ -61,15 +61,8 @@ class PostManager(PublishedManager, Manager):
|
||||
|
||||
return self.published(public).filter(qs_filter)
|
||||
|
||||
# use for finding tags at homepage - TODO: delete when new design published
|
||||
def get_last_months_posts(self, public=True):
|
||||
date_today = timezone.now().date()
|
||||
return self.published(public).filter(
|
||||
public_date__gt=date_today - timedelta(days=365)
|
||||
)
|
||||
|
||||
|
||||
class ArticleManager(PublishedManager, Manager):
|
||||
class ArticleManager(PublishedManager, models.Manager):
|
||||
"""
|
||||
Provide a query set only for "Article"
|
||||
regular fet meetings should not be contained in the news stream
|
||||
@@ -92,7 +85,7 @@ class ArticleManager(PublishedManager, Manager):
|
||||
return self.published(public).filter(is_pinned=True).first()
|
||||
|
||||
|
||||
class NewsManager(PublishedManager, Manager):
|
||||
class NewsManager(PublishedManager, models.Manager):
|
||||
"""
|
||||
Provide a query set only for "News"
|
||||
"""
|
||||
@@ -107,7 +100,7 @@ class NewsManager(PublishedManager, Manager):
|
||||
return qs.order_by("-date")
|
||||
|
||||
|
||||
class AllEventManager(PublishedManager, Manager):
|
||||
class AllEventManager(PublishedManager, models.Manager):
|
||||
"""
|
||||
Provide a query set for all events ("Event" and "Fet Meeting")
|
||||
"""
|
||||
@@ -128,7 +121,7 @@ class AllEventManager(PublishedManager, Manager):
|
||||
return qs.reverse()
|
||||
|
||||
|
||||
class EventManager(PublishedManager, Manager):
|
||||
class EventManager(PublishedManager, models.Manager):
|
||||
"""
|
||||
Provide a query set only for "Events"
|
||||
regular fet meetings should not be contained in the news stream
|
||||
@@ -154,7 +147,7 @@ class EventManager(PublishedManager, Manager):
|
||||
return qs
|
||||
|
||||
|
||||
class FetMeetingManager(PublishedManager, Manager):
|
||||
class FetMeetingManager(PublishedManager, models.Manager):
|
||||
"""
|
||||
Provide a query set only for "Fet Meeting"
|
||||
"""
|
||||
|
||||
@@ -2,8 +2,6 @@ import logging
|
||||
import re
|
||||
from datetime import timedelta
|
||||
|
||||
from taggit.managers import TaggableManager
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.validators import ValidationError
|
||||
@@ -13,17 +11,19 @@ from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.text import slugify
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from taggit.managers import TaggableManager
|
||||
|
||||
from core.models import CustomFlatPage
|
||||
from documents import create_pad, get_pad_html, set_pad_html
|
||||
from documents.api import get_pad_link
|
||||
|
||||
from .managers import (
|
||||
PostManager,
|
||||
ArticleManager,
|
||||
NewsManager,
|
||||
AllEventManager,
|
||||
ArticleManager,
|
||||
EventManager,
|
||||
FetMeetingManager,
|
||||
NewsManager,
|
||||
PostManager,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -138,7 +138,7 @@ class Post(models.Model):
|
||||
)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse("posts:show", kwargs={"id": self.slug})
|
||||
return reverse("posts:post", kwargs={"slug": self.slug})
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# save the post with some defaults
|
||||
@@ -150,13 +150,6 @@ class Post(models.Model):
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
self.tags.set(
|
||||
[
|
||||
*re.findall(r"\#([\d\w-]+)", str(self.subtitle)),
|
||||
*re.findall(r"\#([\d\w-]+)", str(self.title)),
|
||||
]
|
||||
)
|
||||
|
||||
@property
|
||||
def agenda_html(self):
|
||||
"Agenda HTML from Etherpad Pad"
|
||||
@@ -256,6 +249,10 @@ class Post(models.Model):
|
||||
def three_tag_names(self):
|
||||
return self.tags.names()[:3]
|
||||
|
||||
@property
|
||||
def tag_names(self):
|
||||
return [t for t in self.tags.names()]
|
||||
|
||||
@property
|
||||
def imageurl(self):
|
||||
"""
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from django.urls import path, re_path
|
||||
|
||||
from . import apps
|
||||
from . import views
|
||||
from . import apps, views
|
||||
from .utils import slug_calc, tag_complete
|
||||
|
||||
app_name = apps.PostsConfig.name
|
||||
@@ -10,7 +9,13 @@ urlpatterns = [
|
||||
path("", views.index, name="index"),
|
||||
# fet calendar (path have to be ahead show)
|
||||
path("fet_calendar.ics", views.calendar, name="calendar"),
|
||||
path("<str:id>", views.show, name="show"),
|
||||
path(
|
||||
"create-fetmeeting/",
|
||||
views.FetMeetingCreateView.as_view(),
|
||||
name="fetmeeting_create",
|
||||
),
|
||||
path("<slug:slug>/", views.PostDetailView.as_view(), name="post"),
|
||||
path("<slug:slug>/update/", views.PostUpdateView.as_view(), name="post_update"),
|
||||
re_path(
|
||||
r"^(?P<id>[-\w]+)/agenda.pdf$",
|
||||
views.show_pdf_agenda,
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
from io import BytesIO
|
||||
|
||||
from taggit.models import Tag
|
||||
from xhtml2pdf import pisa
|
||||
|
||||
from django.http import HttpResponse, JsonResponse, HttpResponseServerError
|
||||
from django.http import HttpResponse, HttpResponseServerError, JsonResponse
|
||||
from django.utils import timezone
|
||||
from django.utils.text import slugify
|
||||
from taggit.models import Tag
|
||||
from xhtml2pdf import pisa
|
||||
|
||||
|
||||
def render_to_pdf(html):
|
||||
|
||||
@@ -1,18 +1,28 @@
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponse, Http404
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.shortcuts import render
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils import timezone
|
||||
from django.views.generic.detail import DetailView
|
||||
from django.views.generic.edit import CreateView, UpdateView
|
||||
|
||||
from authentications.decorators import authenticated_user
|
||||
from documents.etherpadlib import add_ep_cookie
|
||||
from fet2020.utils import add_log_action
|
||||
from members.models import Member
|
||||
from .forms import PostSearchForm
|
||||
from .models import Event, FileUpload, Post
|
||||
from .utils import render_to_pdf
|
||||
|
||||
from .forms import (
|
||||
EventUpdateForm,
|
||||
FetMeetingCreateForm,
|
||||
FetMeetingUpdateForm,
|
||||
NewsUpdateForm,
|
||||
PostSearchForm,
|
||||
)
|
||||
from .models import Event, FetMeeting, FileUpload, Post
|
||||
from .utils import render_to_pdf
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -88,62 +98,197 @@ def tags(request, tag=""):
|
||||
return render(request, "posts/tag.html", context)
|
||||
|
||||
|
||||
def __get_post_object(id=None, public=True):
|
||||
post = None
|
||||
class PostDetailView(DetailView):
|
||||
model = Post
|
||||
|
||||
try:
|
||||
if id.isdigit() or id is int:
|
||||
post = Post.objects.published(public).get(id=int(id))
|
||||
elif id != "" and id is not None:
|
||||
post = Post.objects.published(public).get(slug=id)
|
||||
except Exception:
|
||||
logger.info("Wrong id '{}'".format(id))
|
||||
raise Http404("wrong post id")
|
||||
def get(self, request, *args, **kwargs):
|
||||
response = super().get(request, *args, **kwargs)
|
||||
|
||||
return post
|
||||
# check if etherpad server works
|
||||
if self.object.agenda_link or self.object.protocol_link:
|
||||
try:
|
||||
response = add_ep_cookie(request, response)
|
||||
except Exception as e:
|
||||
logger.info("Etherpad Server doesn't work. Error: %s", e)
|
||||
|
||||
return response
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
# files
|
||||
files = FileUpload.objects.filter(post=self.object)
|
||||
|
||||
# author
|
||||
author = None
|
||||
author_image = None
|
||||
post_author = Member.all_members.filter(username=self.object.author).first()
|
||||
if post_author:
|
||||
author = post_author
|
||||
author_image = post_author.image["avatar"].url
|
||||
|
||||
related_posts = self.object.tags.similar_objects()
|
||||
# list of non 'is_hidden' posts for related_posts
|
||||
for obj in related_posts:
|
||||
if not obj.published:
|
||||
related_posts.remove(obj)
|
||||
|
||||
context = {
|
||||
"post": self.object,
|
||||
"files": files,
|
||||
"author": author,
|
||||
"author_image": author_image,
|
||||
"next": self.post_next(),
|
||||
"previous": self.post_previous(),
|
||||
"related_posts": related_posts[:4],
|
||||
}
|
||||
|
||||
return context
|
||||
|
||||
def get_queryset(self):
|
||||
self.public_only = not self.request.user.is_authenticated
|
||||
return Post.objects.published(self.public_only)
|
||||
|
||||
def get_template_names(self):
|
||||
template_name = "posts/news/detail.html"
|
||||
if self.object.post_type == "E":
|
||||
template_name = "posts/event/detail.html"
|
||||
elif self.object.post_type == "F":
|
||||
template_name = "posts/fetmeeting/detail.html"
|
||||
|
||||
return template_name
|
||||
|
||||
def post_next(self):
|
||||
"""
|
||||
Helper function for getting next post
|
||||
"""
|
||||
posts = Post.objects.date_sorted_list(self.public_only).filter(
|
||||
post_type=self.object.post_type
|
||||
)
|
||||
qs = posts.filter(date__lt=self.object.date)
|
||||
if not qs:
|
||||
qs = posts[:1]
|
||||
|
||||
return qs.first().slug
|
||||
|
||||
def post_previous(self):
|
||||
"""
|
||||
Helper function for getting previous post
|
||||
"""
|
||||
posts = (
|
||||
Post.objects.date_sorted_list(self.public_only)
|
||||
.filter(post_type=self.object.post_type)
|
||||
.reverse()
|
||||
)
|
||||
qs = posts.filter(date__gt=self.object.date)
|
||||
if not qs:
|
||||
qs = posts[:1]
|
||||
|
||||
return qs.first().slug
|
||||
|
||||
|
||||
def show(request, id=None):
|
||||
public_only = not request.user.is_authenticated
|
||||
post = __get_post_object(id, public_only)
|
||||
class PostUpdateView(LoginRequiredMixin, UpdateView):
|
||||
model = Post
|
||||
|
||||
# files
|
||||
files = FileUpload.objects.filter(post=post)
|
||||
def form_valid(self, form):
|
||||
model = "news"
|
||||
if self.object.post_type == "E":
|
||||
model = "event"
|
||||
elif self.object.post_type == "F":
|
||||
model = "fetmeeting"
|
||||
add_log_action(self.request, form, "posts", model, False)
|
||||
|
||||
# author
|
||||
author = None
|
||||
author_image = None
|
||||
post_author = Member.all_members.filter(username=post.author).first()
|
||||
if post_author:
|
||||
author = post_author
|
||||
author_image = post_author.image["avatar"].url
|
||||
return super().form_valid(form)
|
||||
|
||||
related_posts = post.tags.similar_objects()
|
||||
# list of non 'is_hidden' posts for related_posts
|
||||
for obj in related_posts:
|
||||
if not obj.published:
|
||||
related_posts.remove(obj)
|
||||
def get_form_class(self):
|
||||
form_class = NewsUpdateForm
|
||||
if self.object.post_type == "E":
|
||||
form_class = EventUpdateForm
|
||||
elif self.object.post_type == "F":
|
||||
form_class = FetMeetingUpdateForm
|
||||
|
||||
context = {
|
||||
"post": post,
|
||||
"files": files,
|
||||
"author": author,
|
||||
"author_image": author_image,
|
||||
"next": __next(post, public_only),
|
||||
"previous": __previous(post, public_only),
|
||||
"related_posts": related_posts[:4],
|
||||
}
|
||||
return form_class
|
||||
|
||||
response = render(request, "posts/show.html", context)
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
|
||||
# check if etherpad server works
|
||||
if post.agenda_link or post.protocol_link:
|
||||
try:
|
||||
response = add_ep_cookie(request, response)
|
||||
except Exception as e:
|
||||
logger.info("Etherpad Server doesn't work. Error: %s", e)
|
||||
q = kwargs["data"]
|
||||
|
||||
return response
|
||||
_mutable = q._mutable
|
||||
q._mutable = True
|
||||
|
||||
event_start_0 = q.pop("event_start_0")[0]
|
||||
event_start_1 = q.pop("event_start_1")[0]
|
||||
q.update({"event_start": f"{event_start_0} {event_start_1}"})
|
||||
|
||||
event_end_0 = q.pop("event_end_0")[0]
|
||||
event_end_1 = q.pop("event_end_1")[0]
|
||||
q.update({"event_end": f"{event_end_0} {event_end_1}"})
|
||||
|
||||
q._mutable = _mutable
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
return kwargs
|
||||
|
||||
def get_template_names(self):
|
||||
template_name = "posts/news/update.html"
|
||||
if self.object.post_type == "E":
|
||||
template_name = "posts/event/update.html"
|
||||
elif self.object.post_type == "F":
|
||||
template_name = "posts/fetmeeting/update.html"
|
||||
|
||||
return template_name
|
||||
|
||||
|
||||
class FetMeetingCreateView(LoginRequiredMixin, CreateView):
|
||||
model = FetMeeting
|
||||
template_name = "posts/fetmeeting/create.html"
|
||||
form_class = FetMeetingCreateForm
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.author = self.request.user
|
||||
add_log_action(self.request, form, "posts", "fetmeeting", True)
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
|
||||
try:
|
||||
q = kwargs["data"]
|
||||
|
||||
_mutable = q._mutable
|
||||
q._mutable = True
|
||||
|
||||
event_start_0 = q.pop("event_start_0")[0]
|
||||
event_start_1 = q.pop("event_start_1")[0]
|
||||
q.update({"event_start": f"{event_start_0} {event_start_1}"})
|
||||
|
||||
event_end_0 = q.pop("event_end_0")[0]
|
||||
event_end_1 = q.pop("event_end_1")[0]
|
||||
q.update({"event_end": f"{event_end_0} {event_end_1}"})
|
||||
|
||||
q._mutable = _mutable
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
return kwargs
|
||||
|
||||
|
||||
def show_pdf_agenda(request, id):
|
||||
post = Post.objects.published(True).get(slug=id)
|
||||
html = post.agenda_html
|
||||
|
||||
return show_pdf(request, html, post.slug + "-agenda")
|
||||
|
||||
|
||||
@authenticated_user
|
||||
def show_pdf_protocol(request, id):
|
||||
post = Post.objects.published(True).get(slug=id)
|
||||
html = post.protocol_html
|
||||
|
||||
return show_pdf(request, html, post.slug + "-protokoll")
|
||||
|
||||
|
||||
def show_pdf(request, html, filename):
|
||||
@@ -170,62 +315,3 @@ def show_pdf(request, html, filename):
|
||||
response["Content-Disposition"] = content
|
||||
|
||||
return response
|
||||
|
||||
|
||||
def show_pdf_agenda(request, id):
|
||||
post = __get_post_object(id)
|
||||
html = post.agenda_html
|
||||
|
||||
return show_pdf(request, html, post.slug + "-agenda")
|
||||
|
||||
|
||||
@authenticated_user
|
||||
def show_pdf_protocol(request, id):
|
||||
post = __get_post_object(id)
|
||||
html = post.protocol_html
|
||||
|
||||
return show_pdf(request, html, post.slug + "-protokoll")
|
||||
|
||||
|
||||
def __next(post=None, public=True):
|
||||
"""
|
||||
Helper function for getting next post
|
||||
"""
|
||||
posts = None
|
||||
d = post.slug
|
||||
|
||||
if post:
|
||||
posts = Post.objects.date_sorted_list(public).filter(post_type=post.post_type)
|
||||
|
||||
if posts:
|
||||
for k, v in enumerate(posts):
|
||||
if post.slug == v.slug:
|
||||
if (k + 1) < len(posts):
|
||||
d = posts[k + 1].slug
|
||||
else:
|
||||
d = posts[0].slug
|
||||
break
|
||||
|
||||
return d
|
||||
|
||||
|
||||
def __previous(post=None, public=True):
|
||||
"""
|
||||
Helper function for getting previous post
|
||||
"""
|
||||
posts = None
|
||||
d = post.slug
|
||||
|
||||
if post:
|
||||
posts = Post.objects.date_sorted_list(public).filter(post_type=post.post_type)
|
||||
|
||||
if posts:
|
||||
for k, v in enumerate(posts):
|
||||
if post.slug == v.slug:
|
||||
if k < 1:
|
||||
d = posts[len(posts) - 1].slug
|
||||
else:
|
||||
d = posts[k - 1].slug
|
||||
break
|
||||
|
||||
return d
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework import viewsets
|
||||
|
||||
from .models import Post, FetMeeting
|
||||
from .models import FetMeeting, Post
|
||||
from .serializers import PostSerializer
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from django.urls import path, re_path
|
||||
|
||||
from . import apps
|
||||
from . import views
|
||||
from . import apps, views
|
||||
|
||||
app_name = apps.SearchConfig.name
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.shortcuts import render
|
||||
from haystack.generic_views import SearchView
|
||||
from haystack.query import SearchQuerySet
|
||||
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.shortcuts import render
|
||||
|
||||
from authentications.decorators import authenticated_user
|
||||
|
||||
from .forms import FetUserSearchForm, NonUserSearchForm
|
||||
|
||||
|
||||
|
||||
1
fet2020/static/css/flowbite@1.5.5.css
Normal file
1
fet2020/static/css/flowbite@1.5.5.css
Normal file
File diff suppressed because one or more lines are too long
2
fet2020/static/js/flowbite@1.5.5.js
Normal file
2
fet2020/static/js/flowbite@1.5.5.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1,5 +1,4 @@
|
||||
from ckeditor.widgets import CKEditorWidget
|
||||
|
||||
from django import forms
|
||||
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||
from django.contrib.auth.models import User
|
||||
@@ -7,7 +6,7 @@ from django.core.validators import ValidationError
|
||||
from django.forms.widgets import HiddenInput
|
||||
from django.utils import timezone
|
||||
|
||||
from .models import Task, TaskList, Document
|
||||
from .models import Document, Task, TaskList
|
||||
|
||||
|
||||
class DateInput(forms.DateInput):
|
||||
|
||||
@@ -13,6 +13,7 @@ from django.utils.text import slugify
|
||||
from documents import create_pad
|
||||
from documents.api import get_pad_link
|
||||
from fet2020.utils import create_random_id
|
||||
|
||||
from .managers import TaskManager
|
||||
|
||||
|
||||
@@ -91,7 +92,7 @@ class Task(models.Model):
|
||||
return self.title
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse("tasks:task-detail", kwargs={"slug": self.slug})
|
||||
return reverse("tasks:task", kwargs={"slug": self.slug})
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import apps
|
||||
from . import views
|
||||
from .views import TaskCreateView, TaskDetailView, TaskUpdateView, DocumentCreateView
|
||||
|
||||
from . import apps, views
|
||||
from .views import DocumentCreateView, TaskCreateView, TaskDetailView, TaskUpdateView
|
||||
|
||||
app_name = apps.TasksConfig.name
|
||||
|
||||
urlpatterns = [
|
||||
path("", views.index, name="index"),
|
||||
path("add/", TaskCreateView.as_view(), name="task-create"),
|
||||
path("<slug:slug>/", TaskDetailView.as_view(), name="task-detail"),
|
||||
path("<slug:slug>/update/", TaskUpdateView.as_view(), name="task-update"),
|
||||
path("<slug:slug>/add/", DocumentCreateView.as_view(), name="docu-create"),
|
||||
path("create-task/", TaskCreateView.as_view(), name="task_create"),
|
||||
path("<slug:slug>/", TaskDetailView.as_view(), name="task"),
|
||||
path("<slug:slug>/update/", TaskUpdateView.as_view(), name="task_update"),
|
||||
path(
|
||||
"<slug:slug>/create-document/", DocumentCreateView.as_view(), name="docu_create"
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import logging
|
||||
|
||||
from collections import deque
|
||||
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.shortcuts import render
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.views.generic.detail import DetailView
|
||||
from django.views.generic.edit import CreateView, UpdateView
|
||||
from django.urls import reverse_lazy, reverse
|
||||
from django.utils import timezone
|
||||
|
||||
from authentications.decorators import authenticated_user
|
||||
from documents.api import get_pad_link
|
||||
from documents.etherpadlib import add_ep_cookie
|
||||
from fet2020.utils import add_log_action
|
||||
from intern.models import Topic
|
||||
|
||||
from .forms import DocumentCreateForm, TaskCreateForm, TaskUpdateForm
|
||||
from .models import Document, Task, TaskList
|
||||
|
||||
@@ -146,4 +146,4 @@ class DocumentCreateView(LoginRequiredMixin, CreateView):
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse("tasks:task-detail", kwargs=self.kwargs)
|
||||
return reverse("tasks:task", kwargs=self.kwargs)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
{% if site_url %}
|
||||
<a class="button" href="{{ site_url }}">Zurück zur FET Homepage</a>
|
||||
{% endif %}
|
||||
<a class="button" href="{% url 'authentications:password_change' %}">Passwort ändern</a>
|
||||
<a class="button" href="{% url 'admin:logout' %}">{% translate 'Log out' %}</a>
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
29
fet2020/templates/authentications/change_password.html
Normal file
29
fet2020/templates/authentications/change_password.html
Normal file
@@ -0,0 +1,29 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}LDAP Passwort ändern{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Main Content -->
|
||||
<main class="container mx-auto w-full px-4 my-8 flex-grow flex flex-col">
|
||||
<h1 class="page-title">LDAP Passwort ändern</h1>
|
||||
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<span class="text-gray-700 dark:text-gray-200">Aus Sicherheitsgründen bitte zuerst das alte Passwort und darunter dann zweimal das neue Passwort eingeben, um sicherzustellen, dass es es korrekt eingegeben wurde.</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="w-full h-full flex-1 flex justify-center items-center">
|
||||
<form action="{% url 'authentications:password_change' %}" method="POST" class="sm:p-4 sm:w-3/5 md:w-1/2 lg:w-2/5 xl:w-1/3 2xl:w-1/4 grid grid-cols-1 gap-3 sm:gap-6">
|
||||
{% csrf_token %}
|
||||
|
||||
{% include "baseform/non_field_errors.html" %}
|
||||
|
||||
{% include "baseform/password.html" with field=form.old_password %}
|
||||
{% include "baseform/password.html" with field=form.new_password1 %}
|
||||
{% include "baseform/password.html" with field=form.new_password2 %}
|
||||
|
||||
<input type="submit" class="block btn btn-primary" value="Passwort ändern">
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
15
fet2020/templates/authentications/change_password_done.html
Normal file
15
fet2020/templates/authentications/change_password_done.html
Normal file
@@ -0,0 +1,15 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}LDAP Passwort erfolgreich geändert{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Main Content -->
|
||||
<main class="container mx-auto w-full px-4 my-8 flex-grow flex flex-col">
|
||||
<h1 class="page-title">LDAP Passwort erfolgreich geändert</h1>
|
||||
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<span class="text-gray-700 dark:text-gray-200">Ihr Passwort wurde geändert.</span>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
{% endblock %}
|
||||
@@ -7,9 +7,11 @@
|
||||
<main class="container mx-auto w-full px-4 my-8 flex-grow flex flex-col">
|
||||
<h1 class="page-title">Anmeldung für FET-Mitarbeiter</h1>
|
||||
<div class="w-full h-full flex-1 flex justify-center items-center">
|
||||
<form action="" method="POST" class="sm:p-4 sm:w-3/5 md:w-1/2 lg:w-2/5 xl:w-1/3 2xl:w-1/4 grid grid-cols-1 gap-3 sm:gap-6">
|
||||
<form action="{% url 'authentications:login' %}" method="POST" class="sm:p-4 sm:w-3/5 md:w-1/2 lg:w-2/5 xl:w-1/3 2xl:w-1/4 grid grid-cols-1 gap-3 sm:gap-6">
|
||||
{% csrf_token %}
|
||||
|
||||
{% include "baseform/non_field_errors.html" %}
|
||||
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-danger">
|
||||
<i class="alert-icon fa-solid fa-check-circle"></i>
|
||||
@@ -18,15 +20,11 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<label class="block">
|
||||
<span class="text-gray-700 dark:text-gray-200">Benutzername</span>
|
||||
<input type="text" name="username" class="mt-1 block w-full rounded-md border-gray-300 dark:border-none shadow-sm focus:border-none focus:ring focus:ring-blue-200 dark:focus:ring-sky-700 focus:ring-opacity-50" required="required">
|
||||
</label>
|
||||
<label class="block">
|
||||
<span class="text-gray-700 dark:text-gray-200">Passwort</span>
|
||||
<input type="password" name="password" class="mt-1 block w-full rounded-md border-gray-300 dark:border-none shadow-sm focus:border-none focus:ring focus:ring-blue-200 dark:focus:ring-sky-700 focus:ring-opacity-50" required="required">
|
||||
</label>
|
||||
{% include "baseform/text.html" with field=form.username %}
|
||||
{% include "baseform/password.html" with field=form.password %}
|
||||
|
||||
<input type="submit" class="block btn btn-primary" value="Anmelden">
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -1,38 +1,91 @@
|
||||
{% extends 'head.html' %}
|
||||
|
||||
{% load flatpages %}
|
||||
{% load static %}
|
||||
{% load version %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>FET: {% block title %}Startseite{% endblock %}</title>
|
||||
|
||||
<meta name="author" content="Fachschaft Elektrotechnik (FET)">
|
||||
<meta name="description" content="Die Fachschaft Elektrotechnik besteht aus ET Studierenden, die sich um die Anliegen der Studenten und Studentinnen kümmern.">
|
||||
<meta property="og:image" content="#"> <!--og:... = Facebook metadata-->
|
||||
<meta property="og:description" content="Die Fachschaft Elektrotechnik besteht aus ET Studierenden, die sich um die Anliegen der Studenten und Studentinnen kümmern.">
|
||||
<meta property="og:title" content="Fachschaft Elektrotechnik (FET)">
|
||||
<meta name="twitter:title" content="Fachschaft Elektrotechnik (FET)">
|
||||
<meta name="theme-color" content="#006599">
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{% static 'img/favicons/apple-touch-icon.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{% static 'img/favicons/favicon-32x32.png' %}">
|
||||
<!--<link rel="icon" type="image/png" sizes="16x16" href="{% static 'img/favicons/favicon-16x16.png' %}">-->
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="{% static 'img/fet_logo_white.png' %}">
|
||||
<link rel="manifest" href="{% static 'img/favicons/site.webmanifest' %}">
|
||||
<link rel="mask-icon" href="{% static 'img/favicons/safari-pinned-tab.svg' %}" color="#000000">
|
||||
<link rel="shortcut icon" href="{% static 'img/fet_logo_white.png' %}">
|
||||
<meta name="apple-mobile-web-app-title" content="FET - Fachschaft Elektrotechnik">
|
||||
<meta name="application-name" content="FET - Fachschaft Elektrotechnik">
|
||||
<meta name="msapplication-TileColor" content="#ffffff">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="stylesheet" href="{% static 'css/flowbite@1.5.5.css' %}" type="text/css">
|
||||
|
||||
<link rel="stylesheet" href="{% static 'css/styles.css' %}">
|
||||
<!-- FontAwesome Kit -->
|
||||
<!--<script src="https://kit.fontawesome.com/fb26f70535.js" crossorigin="anonymous"></script>-->
|
||||
<link href="{% static 'fontawesomefree/css/fontawesome.css' %}" rel="stylesheet" type="text/css">
|
||||
<link href="{% static 'fontawesomefree/css/brands.css' %}" rel="stylesheet" type="text/css">
|
||||
<link href="{% static 'fontawesomefree/css/solid.css' %}" rel="stylesheet" type="text/css">
|
||||
<link href="{% static 'fontawesomefree/css/regular.css' %}" rel="stylesheet" type="text/css">
|
||||
|
||||
<!-- Prism.js Theme -->
|
||||
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prism-themes@1/themes/prism-one-dark.min.css"> -->
|
||||
<link rel="stylesheet" href="{% static 'css/prism-one-dark.min.css' %}">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="{% static 'fonts/Inter-3.19/inter.css' %}">
|
||||
<!-- <link rel="stylesheet" href="{% static 'fonts/Poppins-4.003/poppins.css' %}"> -->
|
||||
<link rel="stylesheet" href="{% static 'fonts/Besley-2.0/besley.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'fonts/Fira_Code-6.2/fira_code.css' %}">
|
||||
|
||||
{% block galleryheader %}
|
||||
{% endblock %}
|
||||
{% block extraheader %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
{% block body %}
|
||||
<body x-data="search" x-ref="overflow" @keyup.escape="closeShowSearch">
|
||||
{% if request.user.is_authenticated %}
|
||||
<!-- SEARCH-BAR -->
|
||||
<div class="fixed w-screen h-screen z-30 backdrop-blur-sm backdrop-saturate-50"
|
||||
x-show="showSearch"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="transform backdrop-blur-none backdrop-saturate-100"
|
||||
x-transition:enter-end="transform backdrop-blur-sm backdrop-saturate-50"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="transform backdrop-blur-sm backdrop-saturate-50"
|
||||
x-transition:leave-end="transform backdrop-blur-none backdrop-saturate-100"
|
||||
>
|
||||
<form action="{% url 'search:index' %}" class="flex items-center opacity-90 gap-x-4 mt-[33vh] sm:max-w-md lg:max-w-lg xl:max-w-xl mx-4 sm:mx-auto py-2 px-4 shadow-lg dark:shadow-none bg-gray-200 dark:bg-gray-800 text-gray-800 dark:text-gray-200 rounded-lg dark:border-2 dark:border-gray-700"
|
||||
@click.outside="closeShowSearch"
|
||||
<!-- SEARCH-BAR -->
|
||||
<div class="fixed w-screen h-screen z-30 backdrop-blur-sm backdrop-saturate-50"
|
||||
x-show="showSearch"
|
||||
x-transition:enter="transition transform ease-out duration-300"
|
||||
x-transition:enter-start="scale-0 opacity-0"
|
||||
x-transition:enter-end="scale-100 opacity-90"
|
||||
x-transition:leave="transition transform ease-in duration-150"
|
||||
x-transition:leave-start="scale-100 opacity-90"
|
||||
x-transition:leave-end="scale-0 opacity-0"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="transform backdrop-blur-none backdrop-saturate-100"
|
||||
x-transition:enter-end="transform backdrop-blur-sm backdrop-saturate-50"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="transform backdrop-blur-sm backdrop-saturate-50"
|
||||
x-transition:leave-end="transform backdrop-blur-none backdrop-saturate-100"
|
||||
>
|
||||
<input class="flex-grow bg-inherit text-inherit h-10 p-0 border-0 focus:outline-none focus:border-transparent focus:ring-0" type="search" name="q" placeholder="Nach Person, Artikel oder Fotoalbum suchen...">
|
||||
<button type="submit" class="flex-none">
|
||||
<i class="fa-solid fa-magnifying-glass text-gray-500 dark:text-gray-600"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<form action="{% url 'search:index' %}" class="flex items-center opacity-90 gap-x-4 mt-[33vh] sm:max-w-md lg:max-w-lg xl:max-w-xl mx-4 sm:mx-auto py-2 px-4 shadow-lg dark:shadow-none bg-gray-200 dark:bg-gray-800 text-gray-800 dark:text-gray-200 rounded-lg dark:border-2 dark:border-gray-700"
|
||||
@click.outside="closeShowSearch"
|
||||
x-show="showSearch"
|
||||
x-transition:enter="transition transform ease-out duration-300"
|
||||
x-transition:enter-start="scale-0 opacity-0"
|
||||
x-transition:enter-end="scale-100 opacity-90"
|
||||
x-transition:leave="transition transform ease-in duration-150"
|
||||
x-transition:leave-start="scale-100 opacity-90"
|
||||
x-transition:leave-end="scale-0 opacity-0"
|
||||
>
|
||||
<input class="flex-grow bg-inherit text-inherit h-10 p-0 border-0 focus:outline-none focus:border-transparent focus:ring-0" type="search" name="q" placeholder="Nach Person, Artikel oder Fotoalbum suchen...">
|
||||
<button type="submit" class="flex-none">
|
||||
<i class="fa-solid fa-magnifying-glass text-gray-500 dark:text-gray-600"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- NAVBAR -->
|
||||
{% if not request.user.is_authenticated %}
|
||||
<nav class="navBar-md" x-data="myNavBar">
|
||||
@@ -132,9 +185,9 @@
|
||||
</nav>
|
||||
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
|
||||
<footer>
|
||||
{% endblock %}
|
||||
|
||||
<footer>
|
||||
<ul class="icon-list">
|
||||
<li>
|
||||
<a href="{% url 'facebook' %}"><i class="fa-brands fa-facebook"></i></a>
|
||||
@@ -159,7 +212,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
<hr class="legal-divider">
|
||||
<p class="copyright">© {% now 'Y' %} FET - Alle Rechte vorbehalten.</p>
|
||||
<p class="copyright">© {% now 'Y' %} FET - Alle Rechte vorbehalten. {% version %}.</p>
|
||||
</footer>
|
||||
<div class="super-duper-awesome-signature" x-data="counter">
|
||||
<span x-ref="countFour">Handcrafted </span>
|
||||
@@ -171,6 +224,7 @@
|
||||
|
||||
<script src="{% static 'js/alpine-csp.js' %}"></script>
|
||||
<script src="{% static 'js/dark-mode.js' %}"></script>
|
||||
<script src="{% static 'js/flowbite@1.5.5.js' %}"></script>
|
||||
|
||||
<script src="{% static 'js/gumshoe@5.1.1.js' %}"></script>
|
||||
<script src="{% static 'js/smooth-scroll@16.1.2.js' %}"></script>
|
||||
@@ -180,4 +234,4 @@
|
||||
<script src="{% static 'js/prism-core@1.25.0.js' %}"></script>
|
||||
<script src="{% static 'js/prism-autoloader@1.25.0.js' %}"></script>
|
||||
</body>
|
||||
{% endblock %}
|
||||
</html>
|
||||
|
||||
10
fet2020/templates/baseform/body.html
Normal file
10
fet2020/templates/baseform/body.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<label class="block">
|
||||
<span class="text-gray-700 dark:text-gray-200">{{ field.label }}</span>
|
||||
{% if field.errors %}
|
||||
<div class="alert alert-danger">
|
||||
<div class="alert-body">{{ field.errors }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{{ media }}
|
||||
{{ field }}
|
||||
</label>
|
||||
13
fet2020/templates/baseform/date_time.html
Normal file
13
fet2020/templates/baseform/date_time.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<label>
|
||||
<span class="text-gray-700 dark:text-gray-200">{{ field.label }}</span>
|
||||
{% if field.errors %}
|
||||
<div class="alert alert-danger">
|
||||
<div class="alert-body">{{ field.errors }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<input type="date" id="id_{{ field.name }}_0" name="{{ field.name }}_0" value="{{ field.value|date:"Y-m-d" }}" {% if field.required %}required{% endif %} class="block w-full mt-1 rounded-md border-gray-300 dark:border-none shadow-sm focus:border-none focus:ring focus:ring-blue-200 dark:focus:ring-sky-700 focus:ring-opacity-50">
|
||||
<input type="time" id="id_{{ field.name }}_1" name="{{ field.name }}_1" value="{{ field.value|time }}" {% if field.required %}required{% endif %} class="block w-full mt-1 rounded-md border-gray-300 dark:border-none shadow-sm focus:border-none focus:ring focus:ring-blue-200 dark:focus:ring-sky-700 focus:ring-opacity-50">
|
||||
{% if field.help_text %}
|
||||
<span class="text-gray-700 dark:text-gray-200">{{ field.help_text }}</span>
|
||||
{% endif %}
|
||||
</label>
|
||||
7
fet2020/templates/baseform/non_field_errors.html
Normal file
7
fet2020/templates/baseform/non_field_errors.html
Normal file
@@ -0,0 +1,7 @@
|
||||
{% if form.non_field_errors %}
|
||||
<div class="alert alert-danger">
|
||||
<i class="alert-icon fa-solid fa-check-circle"></i>
|
||||
<h2 class="alert-title">Fehler:</h2>
|
||||
<div class="alert-body">{{ form.non_field_errors }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
12
fet2020/templates/baseform/password.html
Normal file
12
fet2020/templates/baseform/password.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<label class="block">
|
||||
<span class="text-gray-700 dark:text-gray-200">{{ field.label }}</span>
|
||||
{% if field.errors %}
|
||||
<div class="alert alert-danger">
|
||||
<div class="alert-body">{{ field.errors }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<input type="password" id="id_{{ field.name }}" name="{{ field.name }}" {% if field.required %}required{% endif %} class="mt-1 block w-full rounded-md border-gray-300 dark:border-none shadow-sm focus:border-none focus:ring focus:ring-blue-200 dark:focus:ring-sky-700 focus:ring-opacity-50">
|
||||
{% if field.help_text %}
|
||||
<span class="text-gray-700 dark:text-gray-200">{{ field.help_text }}</span>
|
||||
{% endif %}
|
||||
</label>
|
||||
13
fet2020/templates/baseform/select.html
Normal file
13
fet2020/templates/baseform/select.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<label>
|
||||
<span class="text-gray-700 dark:text-gray-200">{{ field.label }}</span>
|
||||
{% if field.errors %}
|
||||
<div class="alert alert-danger">
|
||||
<div class="alert-body">{{ field.errors }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<select id="id_{{ field.name }}" name="{{ field.name }}" {% if field.required %}required{% endif %} class="block w-full mt-1 rounded-md border-gray-300 dark:border-none shadow-sm focus:border-none focus:ring focus:ring-blue-200 dark:focus:ring-sky-700 focus:ring-opacity-50">
|
||||
{% for elem in field %}
|
||||
{{ elem }}
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
12
fet2020/templates/baseform/text.html
Normal file
12
fet2020/templates/baseform/text.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<label class="block">
|
||||
<span class="text-gray-700 dark:text-gray-200">{{ field.label }}</span>
|
||||
{% if field.errors %}
|
||||
<div class="alert alert-danger">
|
||||
<div class="alert-body">{{ field.errors }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<input type="text" id="id_{{ field.name }}" name="{{ field.name }}" {% if field.value %}value="{{ field.value }}"{% endif %} {% if field.required %}required{% endif %} class="mt-1 block w-full rounded-md border-gray-300 dark:border-none shadow-sm focus:border-none focus:ring focus:ring-blue-200 dark:focus:ring-sky-700 focus:ring-opacity-50">
|
||||
{% if field.help_text %}
|
||||
<span class="text-gray-700 dark:text-gray-200">{{ field.help_text }}</span>
|
||||
{% endif %}
|
||||
</label>
|
||||
@@ -16,7 +16,7 @@
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 justify-items-center gap-4">
|
||||
{% for album in albums %}
|
||||
{% if request.user.is_authenticated and album.status == album.DRAFT %}
|
||||
<a href="{% url 'gallery:draft-album' album.slug %}" class="block max-w-xs sm:max-w-none">
|
||||
<a href="{% url 'gallery:album_draft' album.slug %}" class="block max-w-xs sm:max-w-none">
|
||||
<img src="{{ album.thumbnail }}" class="rounded">
|
||||
<h2 class="px-2 text-proprietary-dark dark:text-sky-300">{{ album.title }}</h2>
|
||||
<h3 class="px-2 text-sm text-proprietary dark:text-sky-400 font-normal"><i class="fa-solid fa-calendar-day mr-1"></i>{{ album.event_date }}</h3>
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
{% load static %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>FET: {% block title %}Startseite{% endblock %}</title>
|
||||
|
||||
<meta name="author" content="Fachschaft Elektrotechnik (FET)">
|
||||
<meta name="description" content="Die Fachschaft Elektrotechnik besteht aus ET Studierenden, die sich um die Anliegen der Studenten und Studentinnen kümmern.">
|
||||
<meta property="og:image" content="#"> <!--og:... = Facebook metadata-->
|
||||
<meta property="og:description" content="Die Fachschaft Elektrotechnik besteht aus ET Studierenden, die sich um die Anliegen der Studenten und Studentinnen kümmern.">
|
||||
<meta property="og:title" content="Fachschaft Elektrotechnik (FET)">
|
||||
<meta name="twitter:title" content="Fachschaft Elektrotechnik (FET)">
|
||||
<meta name="theme-color" content="#006599">
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{% static 'img/favicons/apple-touch-icon.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{% static 'img/favicons/favicon-32x32.png' %}">
|
||||
<!--<link rel="icon" type="image/png" sizes="16x16" href="{% static 'img/favicons/favicon-16x16.png' %}">-->
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="{% static 'img/fet_logo_white.png' %}">
|
||||
<link rel="manifest" href="{% static 'img/favicons/site.webmanifest' %}">
|
||||
<link rel="mask-icon" href="{% static 'img/favicons/safari-pinned-tab.svg' %}" color="#000000">
|
||||
<link rel="shortcut icon" href="{% static 'img/fet_logo_white.png' %}">
|
||||
<meta name="apple-mobile-web-app-title" content="FET - Fachschaft Elektrotechnik">
|
||||
<meta name="application-name" content="FET - Fachschaft Elektrotechnik">
|
||||
<meta name="msapplication-TileColor" content="#ffffff">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="stylesheet" href="{% static 'css/styles.css' %}">
|
||||
<!-- FontAwesome Kit -->
|
||||
<!--<script src="https://kit.fontawesome.com/fb26f70535.js" crossorigin="anonymous"></script>-->
|
||||
<link href="{% static 'fontawesomefree/css/fontawesome.css' %}" rel="stylesheet" type="text/css">
|
||||
<link href="{% static 'fontawesomefree/css/brands.css' %}" rel="stylesheet" type="text/css">
|
||||
<link href="{% static 'fontawesomefree/css/solid.css' %}" rel="stylesheet" type="text/css">
|
||||
<link href="{% static 'fontawesomefree/css/regular.css' %}" rel="stylesheet" type="text/css">
|
||||
|
||||
<!-- Prism.js Theme -->
|
||||
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prism-themes@1/themes/prism-one-dark.min.css"> -->
|
||||
<link rel="stylesheet" href="{% static 'css/prism-one-dark.min.css' %}">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="{% static 'fonts/Inter-3.19/inter.css' %}">
|
||||
<!-- <link rel="stylesheet" href="{% static 'fonts/Poppins-4.003/poppins.css' %}"> -->
|
||||
<link rel="stylesheet" href="{% static 'fonts/Besley-2.0/besley.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'fonts/Fira_Code-6.2/fira_code.css' %}">
|
||||
|
||||
{% block galleryheader %}
|
||||
{% endblock %}
|
||||
{% block extraheader %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
{% block body %}
|
||||
{% endblock %}
|
||||
|
||||
</html>
|
||||
@@ -15,7 +15,6 @@
|
||||
<div class="hidden sm:block flex-none w-2/5 lg:w-1/3 bg-white dark:bg-gray-800 p-2 lg:p-4 rounded shadow-xl dark:border-2 dark:border-gray-700">
|
||||
<h2 class="section-title sm:text-left"><i class="fa-solid fa-comments text-gray-300 dark:text-gray-400 mr-2"></i>Events</h2>
|
||||
<div class="-mb-2 text-gray-700 dark:text-gray-200 text-sm md:text-base">
|
||||
|
||||
{% if featured_event %}
|
||||
{% with post=featured_event %}
|
||||
{% include 'posts/partials/_meeting_row.html' %}
|
||||
@@ -26,7 +25,6 @@
|
||||
{% include 'posts/partials/_meeting_row.html' %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -34,7 +32,25 @@
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="container mx-auto w-full px-4 my-8 flex-1">
|
||||
<section class="sm:hidden">
|
||||
{% if request.user.is_authenticated %}
|
||||
<div data-dial-init class="fixed bottom-6 right-6 group">
|
||||
<div id="speed-dial-menu-dropdown" class="flex hidden flex-col justify-end py-1 mb-4 space-y-2 bg-white rounded-lg border border-gray-100 shadow-sm dark:border-gray-600 dark:bg-gray-700">
|
||||
<ul class="text-sm text-gray-500 dark:text-gray-300">
|
||||
<li>
|
||||
<a href="{% url 'posts:fetmeeting_create' %}" class="flex items-center py-2 px-5 hover:bg-gray-100 dark:hover:bg-gray-600 hover:text-gray-900 dark:hover:text-white">
|
||||
<i class="fa-solid fa-plus mr-2"></i>
|
||||
<span class="text-sm font-medium">Neue Fachschaftssitzung</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<button type="button" data-dial-toggle="speed-dial-menu-dropdown" aria-controls="speed-dial-menu-dropdown" aria-expanded="false" class="flex justify-center items-center ml-auto w-14 h-14 text-white bg-blue-700 rounded-full hover:bg-blue-800 dark:bg-blue-600 dark:hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 focus:outline-none dark:focus:ring-blue-800">
|
||||
<i class="fa-solid fa-pen-to-square"></i>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<section class="sm:hidden">
|
||||
<h2 class="section-title section-title-margins">Events</h2>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
@@ -63,7 +79,7 @@
|
||||
<a href="{% url 'posts:calendar' %}" class="btn btn-secondary block w-full"><i class="fa-solid fa-calendar-days mr-2"></i>Kalender abonnieren</a>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</aside>
|
||||
|
||||
<section class="my-8 sm:my-0 sm:w-3/5 xl:w-2/5 flex flex-col gap-4">
|
||||
<h2 class="section-title section-title-margins">Zuletzt veröffentlicht</h2>
|
||||
@@ -73,7 +89,6 @@
|
||||
{% include 'posts/partials/_posts_pinned.html' %}
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
|
||||
{% for post in posts %}
|
||||
{% include 'posts/partials/_posts_hero.html' %}
|
||||
{% endfor %}
|
||||
@@ -82,6 +97,7 @@
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div id="infoBox" class="sticky bottom-4 mt-8 p-4 rounded-lg shadow-lg bg-gray-600 dark:bg-gray-800 text-gray-200 dark:text-gray-300 flex gap-x-4 items-center leading-none dark:border-2 dark:border-gray-700"
|
||||
x-data="infoBox"
|
||||
x-show="consent"
|
||||
@@ -104,5 +120,6 @@
|
||||
@click="closeBox"
|
||||
><i class="fa-solid fa-xmark text-gray-300 dark:text-gray-500"></i></button>
|
||||
</div>
|
||||
</main>
|
||||
-->
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
</a> -->
|
||||
</div>
|
||||
<div class="documentList rounded divide-y divide-gray-300 dark:divide-gray-600">
|
||||
<a href="{% url 'intern:etherpad-create' attachment.topic.topic_group.slug attachment.topic.slug attachment.slug %}" class="flex justify-between">
|
||||
<a href="{% url 'intern:etherpad_create' attachment.topic.topic_group.slug attachment.topic.slug attachment.slug %}" class="flex justify-between">
|
||||
<h3 class="text-gray-800 dark:text-gray-200"><i class="fa-solid fa-plus fa-fw mr-1"></i>Neues Etherpad erstellen</h2>
|
||||
</a>
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<section>
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">Dokumente:</h2>
|
||||
<div class="documentList rounded divide-y divide-gray-300 dark:divide-gray-600">
|
||||
<a href="{% url 'intern:file-create' attachment.topic.topic_group.slug attachment.topic.slug attachment.slug %}" class="flex justify-between">
|
||||
<a href="{% url 'intern:file_create' attachment.topic.topic_group.slug attachment.topic.slug attachment.slug %}" class="flex justify-between">
|
||||
<h3 class="text-gray-800 dark:text-gray-200"><i class="fa-solid fa-plus fa-fw mr-1"></i>Neues Dokument hochladen</h2>
|
||||
</a>
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<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>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
||||
@@ -25,14 +25,14 @@
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="transform origin-top opacity-100 translate-x-0"
|
||||
x-transition:leave-end="transform origin-top opacity-0 -translate-x-6"
|
||||
><a href="{% url 'intern:topic-create' topic_group.grouper.slug %}"><i class="fa-solid fa-plus mr-1"></i>Eintrag hinzufügen</a></button>
|
||||
><a href="{% url 'intern:topic_create' topic_group.grouper.slug %}"><i class="fa-solid fa-plus mr-1"></i>Eintrag hinzufügen</a></button>
|
||||
</div>
|
||||
<ul class="ml-7 w-fit" x-show="getExpandList" x-collapse>
|
||||
{% for topic in topic_group.list %}
|
||||
<li><a href="{% url 'intern:topic' topic.topic_group.slug topic.slug %}" class="w-full py-1 inline-block">{{ topic.title }}</a></li>
|
||||
{% endfor %}
|
||||
<li class="py-1">
|
||||
<a href="{% url 'intern:topic-create' topic_group.grouper.slug %}" class="border border-gray-700 dark:border-gray-300 rounded px-1.5 py-1 text-sm sm:hidden">
|
||||
<a href="{% url 'intern:topic_create' topic_group.grouper.slug %}" class="border border-gray-700 dark:border-gray-300 rounded px-1.5 py-1 text-sm sm:hidden">
|
||||
<i class="fa-solid fa-plus mr-1"></i>Eintrag hinzufügen
|
||||
</a>
|
||||
</li>
|
||||
@@ -57,11 +57,11 @@
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="transform origin-top opacity-100 translate-x-0"
|
||||
x-transition:leave-end="transform origin-top opacity-0 -translate-x-6"
|
||||
><a href="{% url 'intern:topic-create' topic_group.slug %}"><i class="fa-solid fa-plus mr-1"></i>Eintrag hinzufügen</a></button>
|
||||
><a href="{% url 'intern:topic_create' topic_group.slug %}"><i class="fa-solid fa-plus mr-1"></i>Eintrag hinzufügen</a></button>
|
||||
</div>
|
||||
<ul class="ml-7 w-fit" x-show="getExpandList" x-collapse>
|
||||
<li class="py-1">
|
||||
<a href="{% url 'intern:topic-create' topic_group.slug %}" class="border border-gray-700 dark:border-gray-300 rounded px-1.5 py-1 text-sm sm:hidden">
|
||||
<a href="{% url 'intern:topic_create' topic_group.slug %}" class="border border-gray-700 dark:border-gray-300 rounded px-1.5 py-1 text-sm sm:hidden">
|
||||
<i class="fa-solid fa-plus mr-1"></i>Eintrag hinzufügen
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<ul class="flex flex-col gap-1 max-w-fit">
|
||||
{% for task in tasks %}
|
||||
<li>
|
||||
<span class="ml-2">{{ task.title|truncatechars:45 }} <a href="{% url 'tasks:task-detail' task.slug %}" class="inline-block text-proprietary dark:text-proprietary-lighter">Link <i class="fa-solid fa-link"></i></a></span>
|
||||
<span class="ml-2">{{ task.title|truncatechars:45 }} <a href="{% url 'tasks:task' task.slug %}" class="inline-block text-proprietary dark:text-proprietary-lighter">Link <i class="fa-solid fa-link"></i></a></span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
@@ -36,16 +36,16 @@
|
||||
{% endfor %}
|
||||
|
||||
<li class="mt-2">
|
||||
<a href="{% url 'intern:attachment-create' topic.topic_group.slug topic.slug %}" class="border border-gray-700 dark:border-gray-300 rounded px-1.5 py-1">
|
||||
<a href="{% url 'intern:attachment_create' topic.topic_group.slug topic.slug %}" class="border border-gray-700 dark:border-gray-300 rounded px-1.5 py-1">
|
||||
<i class="fa-solid fa-plus mr-1"></i>Eintrag hinzufügen
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<section class="flex flex-col sm:flex-row justify-end gap-4">
|
||||
<a href="{% url 'intern:topic-update' topic.topic_group.slug topic.slug %}" class="btn btn-primary block"><i class="fa-solid fa-pen-to-square mr-2"></i>Thema bearbeiten</a>
|
||||
<a href="{% url 'intern:topic_update' topic.topic_group.slug topic.slug %}" class="btn btn-primary block"><i class="fa-solid fa-pen-to-square mr-2"></i>Thema bearbeiten</a>
|
||||
{% if topic.task_list %}
|
||||
<a href="{% url 'intern:task-create' topic.topic_group.slug topic.slug %}" class="btn btn-primary block"><i class="fa-solid fa-plus-square mr-2"></i>Task hinzufügen</a>
|
||||
<a href="{% url 'intern:task_create' topic.topic_group.slug topic.slug %}" class="btn btn-primary block"><i class="fa-solid fa-plus-square mr-2"></i>Task hinzufügen</a>
|
||||
{% endif %}
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% load flatpages %}
|
||||
{% load job_groups %}
|
||||
{% load softhyphen_tags %}
|
||||
{% load static %}
|
||||
|
||||
@@ -41,9 +42,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<ul class="sideBarNav">
|
||||
{% for job in job_groups %}
|
||||
{% include 'members/partials/_jobs_side_bar.html' %}
|
||||
{% endfor %}
|
||||
{% get_jobs_sidebar request.resolver_match.kwargs.slug %}
|
||||
|
||||
<hr class="">
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<li class="{% if job.slug in request.path %}active{% endif %}">
|
||||
<a href="{% url 'members:jobs' job.slug %}">{{ job.name }}</a>
|
||||
</li>
|
||||
|
||||
{% if job.slug == active_job_group.slug %}
|
||||
{% regroup job_members by job.name as all_jobmem_list %}
|
||||
|
||||
<ul class="scrollSpyNav">
|
||||
{% for jobmem in all_jobmem_list %}
|
||||
<li class="{% if jobmem.grouper %}active{% endif %}">
|
||||
<a href="#{{ jobmem.list.0.job.slug }}">{{ jobmem.grouper }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
17
fet2020/templates/members/partials/jobs_sidebar.html
Normal file
17
fet2020/templates/members/partials/jobs_sidebar.html
Normal file
@@ -0,0 +1,17 @@
|
||||
{% for job in job_groups %}
|
||||
<li class="{% if job.slug == active_job_group.slug %}active{% endif %}">
|
||||
<a href="{% url 'members:jobs' job.slug %}">{{ job.name }}</a>
|
||||
</li>
|
||||
|
||||
{% if job.slug == active_job_group.slug %}
|
||||
{% regroup job_members by job.name as all_jobmem_list %}
|
||||
|
||||
<ul class="scrollSpyNav">
|
||||
{% for jobmem in all_jobmem_list %}
|
||||
<li>
|
||||
<a href="#{{ jobmem.list.0.job.slug }}">{{ jobmem.grouper }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
107
fet2020/templates/posts/event/detail.html
Normal file
107
fet2020/templates/posts/event/detail.html
Normal file
@@ -0,0 +1,107 @@
|
||||
{% extends 'posts/show.html' %}
|
||||
|
||||
{% load post_helpers %}
|
||||
|
||||
{% block title %}{{ post.title }}{% endblock %}
|
||||
|
||||
{% block prev_text_big %}Vorheriges<br>Event{% endblock %}
|
||||
{% block next_text_big %}Nächstes<br>Event{% endblock %}
|
||||
|
||||
{% block prev_text %}Vorheriges Event{% endblock %}
|
||||
{% block next_text %}Nächstes Event{% endblock %}
|
||||
|
||||
{% block update_button_desktop %}
|
||||
{% if request.user.is_authenticated %}
|
||||
<a href="{% url 'posts:post_update' post.slug %}" class="hidden sm:block btn-small btn-primary">
|
||||
<i class="fa-solid fa-pen-to-square mr-1"></i>Event bearbeiten
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block event_details_desktop %}
|
||||
<div class="hidden lg:block absolute top-0 right-0 bg-white dark:bg-gray-900 rounded-bl p-2 bg-opacity-80 dark:bg-opacity-70 gap-2">
|
||||
<div class="items-center lg:flex gap-2">
|
||||
<i class="flex-none fa-solid fa-calendar-day fa-fw text-gray-800 dark:text-gray-200"></i>
|
||||
<span class="flex-1 text-sm text-gray-800 dark:text-gray-200">
|
||||
Event-Start: {{ post.event_start|date }} um {{ post.event_start|time }} Uhr<br>
|
||||
Event-Ende: {{ post.event_end|date }} um {{ post.event_end|time }} Uhr
|
||||
</span>
|
||||
</div>
|
||||
{% if post.event_place %}
|
||||
<div class="items-center lg:flex gap-2">
|
||||
<i class="flex-none fa-solid fa-location-dot fa-fw text-gray-800 dark:text-gray-200"></i>
|
||||
<span class="flex-1 text-sm text-gray-800 dark:text-gray-200">
|
||||
Event-Ort: {{ post.event_place }}
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="hidden absolute top-0 right-0 bg-white dark:bg-gray-900 rounded-bl p-2 bg-opacity-80 dark:bg-opacity-70 items-center gap-2">
|
||||
<i class="flex-none fa-solid fa-calendar-day text-gray-800 dark:text-gray-200"></i>
|
||||
<span class="flex-1 text-sm text-gray-800 dark:text-gray-200">
|
||||
Event-Start: {{ post.event_start|date }} um {{ post.event_start|time }} Uhr<br>
|
||||
Event-Ende: {{ post.event_end|date }} um {{ post.event_end|time }} Uhr<br>
|
||||
{% if post.event_place %}
|
||||
Event-Ort: {{ post.event_place }}
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block post_body %}
|
||||
{% if post.body %}
|
||||
{{ post.body|safe|tags_to_url }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block event_details_mobile %}
|
||||
<hr class="lg:hidden -mx-4 border-gray-200 dark:border-gray-800 dark:border my-4">
|
||||
<div class="lg:hidden">
|
||||
<h2 class="text-gray-800 dark:text-gray-200 font-medium"><i class="fa-solid fa-calendar-days mr-2 text-gray-400 dark:text-gray-500"></i>Termindetails:</h2>
|
||||
<ul class="text-base text-gray-700 dark:text-gray-300 my-1">
|
||||
<li>Start: {{ post.event_start|date }} um {{ post.event_start|time }} Uhr</li>
|
||||
<li>Ende: {{ post.event_end|date }} um {{ post.event_end|time }} Uhr</li>
|
||||
{% if post.event_place %}
|
||||
<li>Ort: {{ post.event_place }}</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block files_buttons %}
|
||||
{% for file in files %}
|
||||
<div class="w-full my-2 flex items-center gap-4 text-gray-700 dark:text-gray-300" x-data="options">
|
||||
<span class="flex-1">{{ file.title }}</span>
|
||||
<div class="relative">
|
||||
<button class="sm:hidden px-2 py-1 border border-gray-300 dark:border-gray-700 rounded" @click="openShowOptions">
|
||||
<i class="fa-solid fa-ellipsis-vertical fa-fw"></i>
|
||||
</button>
|
||||
<ul class="z-10 absolute top-0 right-0 sm:flex flex-row sm:static flex-none border dark:border-2 border-gray-300 dark:border-gray-700 rounded divide-y-2 sm:divide-y-0 sm:divide-x divide-gray-300 dark:divide-gray-700 bg-gray-100 dark:bg-gray-800 shadow sm:bg-transparent sm:shadow-none"
|
||||
@click.outside="closeShowOptions"
|
||||
x-show="getShowOptions"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="transform origin-right opacity-0 scale-95"
|
||||
x-transition:enter-end="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave-end="transform origin-right opacity-0 scale-95"
|
||||
>
|
||||
<li class="block sm:inline-block group hover:bg-gray-200 dark:hover:bg-gray-800 hover:text-gray-800 dark:hover:text-gray-200">
|
||||
<a href="{{ file.file_field.url }}" class="inline-flex items-center px-2 py-1" target="_blank">
|
||||
<i class="fa-solid fa-file-pdf fa-fw text-red-800 dark:text-red-500 md:text-inherit group-hover:text-red-800 dark:group-hover:text-red-500"></i>
|
||||
<span class="ml-2 sm:ml-1">Download</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% block update_button_mobile %}
|
||||
{% if request.user.is_authenticated %}
|
||||
<a href="{% url 'posts:post_update' post.slug %}" class="sm:hidden block w-full btn btn-primary mt-4">
|
||||
<i class="fa-solid fa-pen-to-square mr-1"></i>Event bearbeiten
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
30
fet2020/templates/posts/event/update.html
Normal file
30
fet2020/templates/posts/event/update.html
Normal file
@@ -0,0 +1,30 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ object.title }} bearbeiten{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Main Content -->
|
||||
<main class="container mx-auto w-full px-4 my-8 flex-1">
|
||||
<h1 class="page-title">{{ object.title }} von {{ object.date }} bearbeiten</h1>
|
||||
<div class="w-full h-full flex-1 flex justify-center items-center">
|
||||
<form action="" method="POST" class="w-full max-w-prose sm:px-28 sm:py-4 grid grid-cols-1 gap-y-3 sm:gap-y-6 text-gray-900">
|
||||
{% csrf_token %}
|
||||
|
||||
{% include "baseform/non_field_errors.html" %}
|
||||
|
||||
{% include "baseform/text.html" with field=form.title %}
|
||||
{% include "baseform/select.html" with field=form.status %}
|
||||
{% include "baseform/date_time.html" with field=form.event_start %}
|
||||
{% include "baseform/date_time.html" with field=form.event_end %}
|
||||
{% include "baseform/text.html" with field=form.event_place %}
|
||||
|
||||
<div class="flex flex-col-reverse sm:flex-row gap-3 justify-end pt-4 sm:pt-0">
|
||||
<a href="{% url 'admin:posts_event_change' object.id %}" class="block btn btn-secondary-proprietary">Event im Admin bearbeiten</a>
|
||||
<input type="submit" class="block btn btn-primary" value="Bearbeiten">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
29
fet2020/templates/posts/fetmeeting/create.html
Normal file
29
fet2020/templates/posts/fetmeeting/create.html
Normal file
@@ -0,0 +1,29 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Fachschaftssitzung erstellen{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Main Content -->
|
||||
<main class="container mx-auto w-full px-4 my-8 flex-1">
|
||||
<h1 class="page-title">Fachschaftssitzung erstellen</h1>
|
||||
<div class="w-full h-full flex-1 flex justify-center items-center">
|
||||
<form action="" method="POST" class="w-full max-w-prose sm:px-28 sm:py-4 grid grid-cols-1 gap-y-3 sm:gap-y-6 text-gray-900">
|
||||
{% csrf_token %}
|
||||
|
||||
{% include "baseform/non_field_errors.html" %}
|
||||
|
||||
{% include "baseform/date_time.html" with field=form.event_start %}
|
||||
{% include "baseform/date_time.html" with field=form.event_end %}
|
||||
{% include "baseform/text.html" with field=form.event_place %}
|
||||
|
||||
<input type="hidden" class="ui-autocomplete-input" name="tags" value="{% for elem in form.tags.value %}{{ elem.name }}, {% endfor %}" id="id_tags" autocomplete="off">
|
||||
|
||||
<div class="flex flex-col-reverse sm:flex-row gap-3 justify-end pt-4 sm:pt-0">
|
||||
<input type="submit" class="block btn btn-primary" value="Hinzufügen">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
222
fet2020/templates/posts/fetmeeting/detail.html
Normal file
222
fet2020/templates/posts/fetmeeting/detail.html
Normal file
@@ -0,0 +1,222 @@
|
||||
{% extends 'posts/show.html' %}
|
||||
|
||||
{% load flatpages %}
|
||||
|
||||
{% block title %}{{ post.title }} vom {{ post.event_start|date }}{% endblock %}
|
||||
|
||||
{% block prev_text_big %}Vorherige<br>Sitzung{% endblock %}
|
||||
{% block next_text_big %}Nächste<br>Sitzung{% endblock %}
|
||||
|
||||
{% block prev_text %}Vorherige Sitzung{% endblock %}
|
||||
{% block next_text %}Nächste Sitzung{% endblock %}
|
||||
|
||||
{% block update_button_desktop %}
|
||||
{% if request.user.is_authenticated %}
|
||||
<a href="{% url 'posts:post_update' post.slug %}" class="hidden sm:block btn-small btn-primary">
|
||||
<i class="fa-solid fa-pen-to-square mr-1"></i>FET Sitzung bearbeiten
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block event_details_desktop %}
|
||||
<div class="hidden lg:block absolute top-0 right-0 bg-white dark:bg-gray-900 rounded-bl p-2 bg-opacity-80 dark:bg-opacity-70 gap-2">
|
||||
<div class="items-center lg:flex gap-2">
|
||||
<i class="flex-none fa-solid fa-calendar-day fa-fw text-gray-800 dark:text-gray-200"></i>
|
||||
<span class="flex-1 text-sm text-gray-800 dark:text-gray-200">
|
||||
Event-Start: {{ post.event_start|date }} um {{ post.event_start|time }} Uhr<br>
|
||||
Event-Ende: {{ post.event_end|date }} um {{ post.event_end|time }} Uhr
|
||||
</span>
|
||||
</div>
|
||||
{% if post.event_place %}
|
||||
<div class="items-center lg:flex gap-2">
|
||||
<i class="flex-none fa-solid fa-location-dot fa-fw text-gray-800 dark:text-gray-200"></i>
|
||||
<span class="flex-1 text-sm text-gray-800 dark:text-gray-200">
|
||||
Event-Ort: {{ post.event_place }}
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="hidden absolute top-0 right-0 bg-white dark:bg-gray-900 rounded-bl p-2 bg-opacity-80 dark:bg-opacity-70 items-center gap-2">
|
||||
<i class="flex-none fa-solid fa-calendar-day text-gray-800 dark:text-gray-200"></i>
|
||||
<span class="flex-1 text-sm text-gray-800 dark:text-gray-200">
|
||||
Event-Start: {{ post.event_start|date }} um {{ post.event_start|time }} Uhr<br>
|
||||
Event-Ende: {{ post.event_end|date }} um {{ post.event_end|time }} Uhr<br>
|
||||
{% if post.event_place %}
|
||||
Event-Ort: {{ post.event_place }}
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block post_body %}
|
||||
{% if post.has_agenda %}
|
||||
<h2>Agenda</h2>
|
||||
{{ post.agenda_html|safe }}
|
||||
{% endif %}
|
||||
|
||||
{% if request.user.is_authenticated and post.has_protocol %}
|
||||
<hr>
|
||||
<h2>Protokoll</h2>
|
||||
{{ post.protocol_html|safe }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block event_details_mobile %}
|
||||
<hr class="lg:hidden -mx-4 border-gray-200 dark:border-gray-800 dark:border my-4">
|
||||
<div class="lg:hidden">
|
||||
<h2 class="text-gray-800 dark:text-gray-200 font-medium"><i class="fa-solid fa-calendar-days mr-2 text-gray-400 dark:text-gray-500"></i>Termindetails:</h2>
|
||||
<ul class="text-base text-gray-700 dark:text-gray-300 my-1">
|
||||
<li>Start: {{ post.event_start|date }} um {{ post.event_start|time }} Uhr</li>
|
||||
<li>Ende: {{ post.event_end|date }} um {{ post.event_end|time }} Uhr</li>
|
||||
{% if post.event_place %}
|
||||
<li>Ort: {{ post.event_place }}</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block docu_buttons %}
|
||||
{% if request.user.is_authenticated %}
|
||||
{% if post.has_agenda or post.has_protocol %}
|
||||
<hr class="-mx-4 border-gray-200 dark:border-gray-800 dark:border my-4">
|
||||
<h2 class="text-gray-800 dark:text-gray-200 font-medium"><i class="fa-solid fa-inbox mr-2 text-gray-400 dark:text-gray-500"></i>Dokument(e):</h2>
|
||||
{% endif %}
|
||||
|
||||
{% if post.has_agenda %}
|
||||
<div class="w-full my-2 flex items-center gap-4 text-gray-700 dark:text-gray-300" x-data="options">
|
||||
<span class="flex-1">Agenda</span>
|
||||
<div class="relative">
|
||||
<button class="sm:hidden px-2 py-1 border border-gray-300 dark:border-gray-700 rounded" @click="openShowOptions">
|
||||
<i class="fa-solid fa-ellipsis-vertical fa-fw"></i>
|
||||
</button>
|
||||
<ul class="z-10 absolute top-0 right-0 sm:flex flex-row sm:static flex-none border dark:border-2 border-gray-300 dark:border-gray-700 rounded divide-y-2 sm:divide-y-0 sm:divide-x divide-gray-300 dark:divide-gray-700 bg-gray-100 dark:bg-gray-800 shadow sm:bg-transparent sm:shadow-none"
|
||||
@click.outside="closeShowOptions"
|
||||
x-show="getShowOptions"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="transform origin-right opacity-0 scale-95"
|
||||
x-transition:enter-end="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave-end="transform origin-right opacity-0 scale-95"
|
||||
>
|
||||
<li class="block sm:inline-block group hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-800 dark:hover:text-gray-200">
|
||||
<a href="{{ post.agenda_link }}" class="inline-flex items-center px-2 py-1">
|
||||
<i class="fa-solid fa-file-signature fa-fw text-proprietary dark:text-proprietary-light md:text-inherit group-hover:text-proprietary dark:group-hover:text-proprietary-light"></i>
|
||||
<span class="ml-2 sm:ml-1">Bearbeiten</span>
|
||||
</a>
|
||||
</li>
|
||||
{% if post.filename_agenda %}
|
||||
<li class="block sm:inline-block group hover:bg-gray-200 dark:hover:bg-gray-800 hover:text-gray-800 dark:hover:text-gray-200">
|
||||
<a href="{% url 'posts:show_pdf_agenda' post.slug %}" class="inline-flex items-center px-2 py-1">
|
||||
<i class="fa-solid fa-file-pdf fa-fw text-red-800 dark:text-red-500 md:text-inherit group-hover:text-red-800 dark:group-hover:text-red-500"></i>
|
||||
<span class="ml-2 sm:ml-1">Download</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if post.has_protocol %}
|
||||
<div class="w-full my-2 flex items-center gap-4 text-gray-700 dark:text-gray-300" x-data="options">
|
||||
<span class="flex-1">Protokoll</span>
|
||||
<div class="relative">
|
||||
<button class="sm:hidden px-2 py-1 border border-gray-300 dark:border-gray-700 rounded" @click="openShowOptions">
|
||||
<i class="fa-solid fa-ellipsis-vertical fa-fw"></i>
|
||||
</button>
|
||||
<ul class="z-10 absolute top-0 right-0 sm:flex flex-row sm:static flex-none border dark:border-2 border-gray-300 dark:border-gray-700 rounded divide-y-2 sm:divide-y-0 sm:divide-x divide-gray-300 dark:divide-gray-700 bg-gray-100 dark:bg-gray-800 shadow sm:bg-transparent sm:shadow-none"
|
||||
@click.outside="closeShowOptions"
|
||||
x-show="getShowOptions"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="transform origin-right opacity-0 scale-95"
|
||||
x-transition:enter-end="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave-end="transform origin-right opacity-0 scale-95"
|
||||
>
|
||||
<li class="block sm:inline-block group hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-800 dark:hover:text-gray-200">
|
||||
<a href="{{ post.protocol_link }}" class="inline-flex items-center px-2 py-1"><i class="fa-solid fa-file-signature fa-fw text-proprietary dark:text-proprietary-light md:text-inherit group-hover:text-proprietary dark:group-hover:text-proprietary-light"></i>
|
||||
<span class="ml-2 sm:ml-1">Bearbeiten</span>
|
||||
</a>
|
||||
</li>
|
||||
{% if post.filename_protocol %}
|
||||
<li class="block sm:inline-block group hover:bg-gray-200 dark:hover:bg-gray-800 hover:text-gray-800 dark:hover:text-gray-200">
|
||||
<a href="{% url 'posts:show_pdf_protocol' post.slug %}" class="inline-flex items-center px-2 py-1">
|
||||
<i class="fa-solid fa-file-pdf fa-fw text-red-800 dark:text-red-500 md:text-inherit group-hover:text-red-800 dark:group-hover:text-red-500"></i>
|
||||
<span class="ml-2 sm:ml-1">Download</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% get_flatpages '/bs/' for user as pages %}
|
||||
{% if pages %}
|
||||
<div class="w-full my-2 flex items-center gap-4 text-gray-700 dark:text-gray-300" x-data="options">
|
||||
<span class="flex-1">{{ pages.first.title }}</span>
|
||||
<div class="relative">
|
||||
<button class="sm:hidden px-2 py-1 border border-gray-300 dark:border-gray-700 rounded" @click="openShowOptions">
|
||||
<i class="fa-solid fa-ellipsis-vertical fa-fw"></i>
|
||||
</button>
|
||||
<ul class="z-10 absolute top-0 right-0 sm:flex flex-row sm:static flex-none border dark:border-2 border-gray-300 dark:border-gray-700 rounded divide-y-2 sm:divide-y-0 sm:divide-x divide-gray-300 dark:divide-gray-700 bg-gray-100 dark:bg-gray-800 shadow sm:bg-transparent sm:shadow-none"
|
||||
@click.outside="closeShowOptions"
|
||||
x-show="getShowOptions"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="transform origin-right opacity-0 scale-95"
|
||||
x-transition:enter-end="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave-end="transform origin-right opacity-0 scale-95"
|
||||
>
|
||||
<li class="block sm:inline-block group hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-800 dark:hover:text-gray-200">
|
||||
<a href="{{ pages.first.url }}" class="inline-flex items-center px-2 py-1"><i class="fa-solid fa-file-lines fa-fw text-proprietary dark:text-proprietary-light md:text-inherit group-hover:text-proprietary dark:group-hover:text-proprietary-light"></i>
|
||||
<span class="ml-2 sm:ml-1">Übersicht</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block files_buttons %}
|
||||
{% for file in files %}
|
||||
<div class="w-full my-2 flex items-center gap-4 text-gray-700 dark:text-gray-300" x-data="options">
|
||||
<span class="flex-1">{{ file.title }}</span>
|
||||
<div class="relative">
|
||||
<button class="sm:hidden px-2 py-1 border border-gray-300 dark:border-gray-700 rounded" @click="openShowOptions">
|
||||
<i class="fa-solid fa-ellipsis-vertical fa-fw"></i>
|
||||
</button>
|
||||
<ul class="z-10 absolute top-0 right-0 sm:flex flex-row sm:static flex-none border dark:border-2 border-gray-300 dark:border-gray-700 rounded divide-y-2 sm:divide-y-0 sm:divide-x divide-gray-300 dark:divide-gray-700 bg-gray-100 dark:bg-gray-800 shadow sm:bg-transparent sm:shadow-none"
|
||||
@click.outside="closeShowOptions"
|
||||
x-show="getShowOptions"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="transform origin-right opacity-0 scale-95"
|
||||
x-transition:enter-end="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave-end="transform origin-right opacity-0 scale-95"
|
||||
>
|
||||
<li class="block sm:inline-block group hover:bg-gray-200 dark:hover:bg-gray-800 hover:text-gray-800 dark:hover:text-gray-200">
|
||||
<a href="{{ file.file_field.url }}" class="inline-flex items-center px-2 py-1" target="_blank">
|
||||
<i class="fa-solid fa-file-pdf fa-fw text-red-800 dark:text-red-500 md:text-inherit group-hover:text-red-800 dark:group-hover:text-red-500"></i>
|
||||
<span class="ml-2 sm:ml-1">Download</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% block update_button_mobile %}
|
||||
{% if request.user.is_authenticated %}
|
||||
<a href="{% url 'posts:post_update' post.slug %}" class="sm:hidden block w-full btn btn-primary mt-4">
|
||||
<i class="fa-solid fa-pen-to-square mr-1"></i>FET Sitzung bearbeiten
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
28
fet2020/templates/posts/fetmeeting/update.html
Normal file
28
fet2020/templates/posts/fetmeeting/update.html
Normal file
@@ -0,0 +1,28 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ object.title }} bearbeiten{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Main Content -->
|
||||
<main class="container mx-auto w-full px-4 my-8 flex-1">
|
||||
<h1 class="page-title">{{ object.title }} von {{ object.date }} bearbeiten</h1>
|
||||
<div class="w-full h-full flex-1 flex justify-center items-center">
|
||||
<form action="" method="POST" class="w-full max-w-prose sm:px-28 sm:py-4 grid grid-cols-1 gap-y-3 sm:gap-y-6 text-gray-900">
|
||||
{% csrf_token %}
|
||||
|
||||
{% include "baseform/non_field_errors.html" %}
|
||||
|
||||
{% include "baseform/date_time.html" with field=form.event_start %}
|
||||
{% include "baseform/date_time.html" with field=form.event_end %}
|
||||
{% include "baseform/text.html" with field=form.event_place %}
|
||||
|
||||
<div class="flex flex-col-reverse sm:flex-row gap-3 justify-end pt-4 sm:pt-0">
|
||||
<a href="{% url 'admin:posts_fetmeeting_change' object.id %}" class="block btn btn-secondary-proprietary">Fachschaftssitzung im Admin bearbeiten</a>
|
||||
<input type="submit" class="block btn btn-primary" value="Bearbeiten">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
62
fet2020/templates/posts/news/detail.html
Normal file
62
fet2020/templates/posts/news/detail.html
Normal file
@@ -0,0 +1,62 @@
|
||||
{% extends 'posts/show.html' %}
|
||||
|
||||
{% load post_helpers %}
|
||||
|
||||
{% block title %}{{ post.title }}{% endblock %}
|
||||
|
||||
{% block update_button_desktop %}
|
||||
{% if request.user.is_authenticated %}
|
||||
<a href="{% url 'posts:post_update' post.slug %}" class="hidden sm:block btn-small btn-primary">
|
||||
<i class="fa-solid fa-pen-to-square mr-1"></i>Artikel bearbeiten
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block post_body %}
|
||||
{% if post.body %}
|
||||
{{ post.body|safe|tags_to_url }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block files_buttons %}
|
||||
{% if files %}
|
||||
<hr class="-mx-4 border-gray-200 dark:border-gray-800 dark:border my-4">
|
||||
<h2 class="text-gray-800 dark:text-gray-200 font-medium"><i class="fa-solid fa-inbox mr-2 text-gray-400 dark:text-gray-500"></i>Dokument(e):</h2>
|
||||
|
||||
{% for file in files %}
|
||||
<div class="w-full my-2 flex items-center gap-4 text-gray-700 dark:text-gray-300" x-data="options">
|
||||
<span class="flex-1">{{ file.title }}</span>
|
||||
<div class="relative">
|
||||
<button class="sm:hidden px-2 py-1 border border-gray-300 dark:border-gray-700 rounded" @click="openShowOptions">
|
||||
<i class="fa-solid fa-ellipsis-vertical fa-fw"></i>
|
||||
</button>
|
||||
<ul class="z-10 absolute top-0 right-0 sm:flex flex-row sm:static flex-none border dark:border-2 border-gray-300 dark:border-gray-700 rounded divide-y-2 sm:divide-y-0 sm:divide-x divide-gray-300 dark:divide-gray-700 bg-gray-100 dark:bg-gray-800 shadow sm:bg-transparent sm:shadow-none"
|
||||
@click.outside="closeShowOptions"
|
||||
x-show="getShowOptions"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="transform origin-right opacity-0 scale-95"
|
||||
x-transition:enter-end="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave-end="transform origin-right opacity-0 scale-95"
|
||||
>
|
||||
<li class="block sm:inline-block group hover:bg-gray-200 dark:hover:bg-gray-800 hover:text-gray-800 dark:hover:text-gray-200">
|
||||
<a href="{{ file.file_field.url }}" class="inline-flex items-center px-2 py-1" target="_blank">
|
||||
<i class="fa-solid fa-file-pdf fa-fw text-red-800 dark:text-red-500 md:text-inherit group-hover:text-red-800 dark:group-hover:text-red-500"></i>
|
||||
<span class="ml-2 sm:ml-1">Download</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block update_button_mobile %}
|
||||
{% if request.user.is_authenticated %}
|
||||
<a href="{% url 'posts:post_update' post.slug %}" class="sm:hidden block w-full btn btn-primary mt-4">
|
||||
<i class="fa-solid fa-pen-to-square mr-1"></i>Artikel bearbeiten
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
26
fet2020/templates/posts/news/update.html
Normal file
26
fet2020/templates/posts/news/update.html
Normal file
@@ -0,0 +1,26 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}{{ object.title }} bearbeiten{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Main Content -->
|
||||
<main class="container mx-auto w-full px-4 my-8 flex-1">
|
||||
<h1 class="page-title">News '{{ object.title }}' bearbeiten</h1>
|
||||
<div class="w-full h-full flex-1 flex justify-center items-center">
|
||||
<form action="" method="POST" class="w-full max-w-prose sm:px-28 sm:py-4 grid grid-cols-1 gap-y-3 sm:gap-y-6 text-gray-900">
|
||||
{% csrf_token %}
|
||||
|
||||
{% include "baseform/non_field_errors.html" %}
|
||||
|
||||
{% include "baseform/text.html" with field=form.title %}
|
||||
{% include "baseform/select.html" with field=form.status %}
|
||||
{% include "baseform/body.html" with field=form.body media=form.media %}
|
||||
|
||||
<div class="flex flex-col-reverse sm:flex-row gap-3 justify-end pt-4 sm:pt-0">
|
||||
<a href="{% url 'admin:posts_news_change' object.id %}" class="block btn btn-secondary-proprietary">News im Admin bearbeiten</a>
|
||||
<input type="submit" class="block btn btn-primary" value="Bearbeiten">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
@@ -1,10 +1,7 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% load flatpages %}
|
||||
{% load post_helpers %}
|
||||
|
||||
{% block title %}News{% endblock %}
|
||||
|
||||
{% block extraheader %}
|
||||
<meta content="{{ post.imageurl }}" property="og:image">
|
||||
<meta content="{{ post.title }}" property="og:title">
|
||||
@@ -15,37 +12,37 @@
|
||||
{% block content %}
|
||||
<!-- Main Content -->
|
||||
<main class="container mx-auto w-full flex-1 my-8 sm:flex flex-col sm:px-4">
|
||||
<a href="{% url 'posts:show' previous %}" class="hidden z-20 fixed left-0 top-1/2 -mt-8 p-2 xl:flex items-center text-gray-400 dark:text-gray-300 rounded-md"
|
||||
x-data="prevArticleButton"
|
||||
@mouseleave="closeShowPrevArticleButton"
|
||||
@mouseover="openShowPrevArticleButton"
|
||||
>
|
||||
<i class="fa-solid fa-chevron-left text-5xl -m-2 p-2 bg-gray-100 dark:bg-gray-700 rounded-md"></i>
|
||||
<span class="text-gray-600 dark:text-gray-300 font-medium bg-gray-100 dark:bg-gray-700 -m-2 p-2 rounded-r-md origin-left"
|
||||
x-show="getShowPrevArticleButton"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 bg-opacity-0 transform scale-90"
|
||||
x-transition:enter-end="opacity-100 transform scale-100"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="opacity-100 transform scale-100"
|
||||
x-transition:leave-end="opacity-0 bg-opacity-0 transform scale-100"
|
||||
>Vorheriger<br>Artikel</span>
|
||||
</a>
|
||||
<a href="{% url 'posts:show' next %}" class="hidden z-20 fixed right-0 top-1/2 -mt-8 p-2 xl:flex items-center text-gray-400 dark:text-gray-300 rounded-md"
|
||||
x-data="nextArticleButton"
|
||||
@mouseleave="closeShowNextArticleButton"
|
||||
@mouseover="openShowNextArticleButton"
|
||||
>
|
||||
<span class="z-30 text-gray-600 dark:text-gray-300 font-medium bg-gray-100 dark:bg-gray-700 -m-2 p-2 rounded-l-md text-right origin-right"
|
||||
x-show="getShowNextArticleButton"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 bg-opacity-0 transform scale-90"
|
||||
x-transition:enter-end="opacity-100 transform scale-100"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="opacity-100 transform scale-100"
|
||||
x-transition:leave-end="opacity-0 bg-opacity-0 transform scale-100"
|
||||
>Nächster<br>Artikel</span>
|
||||
<i class="fa-solid fa-chevron-right text-5xl -m-2 p-2 bg-gray-100 dark:bg-gray-700 rounded-md"></i>
|
||||
<a href="{% url 'posts:post' previous %}" class="hidden z-20 fixed left-0 top-1/2 -mt-8 p-2 xl:flex items-center text-gray-400 dark:text-gray-300 rounded-md"
|
||||
x-data="prevArticleButton"
|
||||
@mouseleave="closeShowPrevArticleButton"
|
||||
@mouseover="openShowPrevArticleButton"
|
||||
>
|
||||
<i class="fa-solid fa-chevron-left text-5xl -m-2 p-2 bg-gray-100 dark:bg-gray-700 rounded-md"></i>
|
||||
<span class="text-gray-600 dark:text-gray-300 font-medium bg-gray-100 dark:bg-gray-700 -m-2 p-2 rounded-r-md origin-left"
|
||||
x-show="getShowPrevArticleButton"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 bg-opacity-0 transform scale-90"
|
||||
x-transition:enter-end="opacity-100 transform scale-100"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="opacity-100 transform scale-100"
|
||||
x-transition:leave-end="opacity-0 bg-opacity-0 transform scale-100"
|
||||
>{% block prev_text_big %}Vorheriger<br>Artikel{% endblock %}</span>
|
||||
</a>
|
||||
<a href="{% url 'posts:post' next %}" class="hidden z-20 fixed right-0 top-1/2 -mt-8 p-2 xl:flex items-center text-gray-400 dark:text-gray-300 rounded-md"
|
||||
x-data="nextArticleButton"
|
||||
@mouseleave="closeShowNextArticleButton"
|
||||
@mouseover="openShowNextArticleButton"
|
||||
>
|
||||
<span class="z-30 text-gray-600 dark:text-gray-300 font-medium bg-gray-100 dark:bg-gray-700 -m-2 p-2 rounded-l-md text-right origin-right"
|
||||
x-show="getShowNextArticleButton"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 bg-opacity-0 transform scale-90"
|
||||
x-transition:enter-end="opacity-100 transform scale-100"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="opacity-100 transform scale-100"
|
||||
x-transition:leave-end="opacity-0 bg-opacity-0 transform scale-100"
|
||||
>{% block next_text_big %}Nächster<br>Artikel{% endblock %}</span>
|
||||
<i class="fa-solid fa-chevron-right text-5xl -m-2 p-2 bg-gray-100 dark:bg-gray-700 rounded-md"></i>
|
||||
</a>
|
||||
<section>
|
||||
<div class="mb-4 flex flex-col sm:flex-col gap-2 mx-auto">
|
||||
@@ -54,300 +51,75 @@
|
||||
<li class="inline-block py-1 px-2 bg-sky-100 dark:bg-sky-900 rounded-full"><a href="{% url 'posts:tags' t %}">#{{ t }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<h1 class="px-4 sm:px-0 text-lg sm:text-xl lg:text-3xl text-center sm:text-left font-medium text-gray-900 dark:text-gray-100 font-serif tracking-wider leading-normal" style="line-height: 1.5;">{{ post.title|tags_to_url }}</h1>
|
||||
<h1 class="px-4 sm:px-0 text-lg sm:text-xl lg:text-3xl text-center sm:text-left font-medium text-gray-900 dark:text-gray-100 font-serif tracking-wider leading-normal" style="line-height: 1.5;">{{ post.title }}</h1>
|
||||
<div class="mx-auto max-w-max sm:mx-0 sm:max-w-none sm:flex justify-between items-center">
|
||||
|
||||
<div class="max-w-max flex flex-row justify-center sm:justify-start gap-2 self-center md:self-start">
|
||||
|
||||
{% if author_image and author %}
|
||||
|
||||
<img class="hidden sm:block w-12 rounded-full" src="{{ author_image }}" alt="Portraitfoto von {{ author.firstname }}">
|
||||
<div class="sm:flex flex-col justify-evenly text-gray-600 dark:text-gray-300 text-sm sm:text-base">
|
||||
<a href="{% url 'members:member' author.id %}" class="underline">{{ author.firstname }}</a>
|
||||
<span class="sm:hidden"> am </span>
|
||||
<span>{{ post.date|date:"d. F Y" }}</span>
|
||||
</div>
|
||||
|
||||
{% elif post.author %}
|
||||
|
||||
<div class="sm:flex flex-col justify-evenly text-gray-600 dark:text-gray-300 text-sm sm:text-base">
|
||||
<a class="underline">{{ post.author|capfirst }}</a>
|
||||
<span class="sm:hidden"> am </span>
|
||||
<span>{{ post.date|date:"d. F Y" }}</span>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
|
||||
<div class="sm:flex flex-col justify-evenly text-gray-600 dark:text-gray-300 text-sm sm:text-base">
|
||||
<a class="underline">fet.at Redaktion</a>
|
||||
<span class="sm:hidden"> am </span>
|
||||
<span>{{ post.date|date:"d. F Y" }}</span>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
{% if request.user.is_authenticated %}
|
||||
{% if post.post_type == 'N' %}
|
||||
<a href="{% url 'admin:posts_news_change' post.id %}" class="hidden sm:block btn-small btn-primary">
|
||||
<i class="fa-solid fa-pen-to-square mr-1"></i>Artikel bearbeiten
|
||||
</a>
|
||||
{% elif post.post_type == 'E' %}
|
||||
<a href="{% url 'admin:posts_event_change' post.id %}" class="hidden sm:block btn-small btn-primary">
|
||||
<i class="fa-solid fa-pen-to-square mr-1"></i>Event bearbeiten
|
||||
</a>
|
||||
{% elif post.post_type == 'F' %}
|
||||
<a href="{% url 'admin:posts_fetmeeting_change' post.id %}" class="hidden sm:block btn-small btn-primary">
|
||||
<i class="fa-solid fa-pen-to-square mr-1"></i>FET Sitzung bearbeiten
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% block update_button_desktop %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- <img src="img/article-cover-3.jpg" alt="" class="h-44 sm:h-56 lg:h-64 xl:h-80 w-full object-cover sm:rounded-md max-w-5xl mx-auto"> -->
|
||||
<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 }}');">
|
||||
{% if post.post_type != 'N' %}
|
||||
<div class="hidden lg:block absolute top-0 right-0 bg-white dark:bg-gray-900 rounded-bl p-2 bg-opacity-80 dark:bg-opacity-70 gap-2">
|
||||
<div class="items-center lg:flex gap-2">
|
||||
<i class="flex-none fa-solid fa-calendar-day fa-fw text-gray-800 dark:text-gray-200"></i>
|
||||
<span class="flex-1 text-sm text-gray-800 dark:text-gray-200">
|
||||
Event-Start: {{ post.event_start|date }} um {{ post.event_start|time }} Uhr<br>
|
||||
Event-Ende: {{ post.event_end|date }} um {{ post.event_end|time }} Uhr
|
||||
</span>
|
||||
</div>
|
||||
{% if post.event_place %}
|
||||
<div class="items-center lg:flex gap-2">
|
||||
<i class="flex-none fa-solid fa-location-dot fa-fw text-gray-800 dark:text-gray-200"></i>
|
||||
<span class="flex-1 text-sm text-gray-800 dark:text-gray-200">
|
||||
Event-Ort: {{ post.event_place }}
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="hidden absolute top-0 right-0 bg-white dark:bg-gray-900 rounded-bl p-2 bg-opacity-80 dark:bg-opacity-70 items-center gap-2">
|
||||
<i class="flex-none fa-solid fa-calendar-day text-gray-800 dark:text-gray-200"></i>
|
||||
<span class="flex-1 text-sm text-gray-800 dark:text-gray-200">
|
||||
Event-Start: {{ post.event_start|date }} um {{ post.event_start|time }} Uhr<br>
|
||||
Event-Ende: {{ post.event_end|date }} um {{ post.event_end|time }} Uhr<br>
|
||||
{% if post.event_place %}
|
||||
Event-Ort: {{ post.event_place }}
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% block event_details_desktop %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</section>
|
||||
<section class="mx-4 z-10">
|
||||
<article class="p-4 mt-4 sm:-mt-16 xl:-mt-24 w-full max-w-prose mx-auto bg-white dark:bg-gray-900 rounded dark:border-2 dark:border-gray-800">
|
||||
<!-- <div class="w-full flex justify-end">
|
||||
<div class="hidden lg:block max-w-max text-sm text-gray-600">
|
||||
Event-Start: 23. August 2021 um 18:00 Uhr<br>
|
||||
Event-Ende: 23. August 2021 um 20:00 Uhr
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="db-page-content-left big-first-letter">
|
||||
<!-- Content from DB here: -->
|
||||
{% if post.has_agenda %}
|
||||
<h2>Agenda</h2>
|
||||
{{ post.agenda_html|safe }}
|
||||
{% elif post.body %}
|
||||
{{ post.body|safe|tags_to_url }}
|
||||
{% endif %}
|
||||
|
||||
{% if request.user.is_authenticated and post.has_protocol %}
|
||||
<hr>
|
||||
<h2>Protokoll</h2>
|
||||
{{ post.protocol_html|safe }}
|
||||
{% endif %}
|
||||
{% block post_body %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
{% if post.post_type != 'N' %}
|
||||
<hr class="lg:hidden -mx-4 border-gray-200 dark:border-gray-800 dark:border my-4">
|
||||
<div class="lg:hidden">
|
||||
<h2 class="text-gray-800 dark:text-gray-200 font-medium"><i class="fa-solid fa-calendar-days mr-2 text-gray-400 dark:text-gray-500"></i>Termindetails:</h2>
|
||||
<ul class="text-base text-gray-700 dark:text-gray-300 my-1">
|
||||
<li>Start: {{ post.event_start|date }} um {{ post.event_start|time }} Uhr</li>
|
||||
<li>Ende: {{ post.event_end|date }} um {{ post.event_end|time }} Uhr</li>
|
||||
{% if post.event_place %}
|
||||
<li>Ort: {{ post.event_place }}</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% block event_details_mobile %}
|
||||
{% endblock %}
|
||||
|
||||
{% if post.has_agenda or post.has_protocol %}
|
||||
{% if request.user.is_authenticated %}
|
||||
<hr class="-mx-4 border-gray-200 dark:border-gray-800 dark:border my-4">
|
||||
<h2 class="text-gray-800 dark:text-gray-200 font-medium"><i class="fa-solid fa-inbox mr-2 text-gray-400 dark:text-gray-500"></i>Dokument(e):</h2>
|
||||
{% endif %}
|
||||
{% elif files %}
|
||||
<hr class="-mx-4 border-gray-200 dark:border-gray-800 dark:border my-4">
|
||||
<h2 class="text-gray-800 dark:text-gray-200 font-medium"><i class="fa-solid fa-inbox mr-2 text-gray-400 dark:text-gray-500"></i>Dokument(e):</h2>
|
||||
{% endif %}
|
||||
{% block docu_buttons %}
|
||||
{% endblock %}
|
||||
|
||||
{% if request.user.is_authenticated %}
|
||||
{% if post.has_agenda %}
|
||||
<div class="w-full my-2 flex items-center gap-4 text-gray-700 dark:text-gray-300" x-data="options">
|
||||
<span class="flex-1">Agenda</span>
|
||||
<div class="relative">
|
||||
<button class="sm:hidden px-2 py-1 border border-gray-300 dark:border-gray-700 rounded" @click="openShowOptions">
|
||||
<i class="fa-solid fa-ellipsis-vertical fa-fw"></i>
|
||||
</button>
|
||||
<ul class="z-10 absolute top-0 right-0 sm:flex flex-row sm:static flex-none border dark:border-2 border-gray-300 dark:border-gray-700 rounded divide-y-2 sm:divide-y-0 sm:divide-x divide-gray-300 dark:divide-gray-700 bg-gray-100 dark:bg-gray-800 shadow sm:bg-transparent sm:shadow-none"
|
||||
@click.outside="closeShowOptions"
|
||||
x-show="getShowOptions"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="transform origin-right opacity-0 scale-95"
|
||||
x-transition:enter-end="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave-end="transform origin-right opacity-0 scale-95"
|
||||
>
|
||||
<li class="block sm:inline-block group hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-800 dark:hover:text-gray-200">
|
||||
<a href="{{ post.agenda_link }}" class="inline-flex items-center px-2 py-1">
|
||||
<i class="fa-solid fa-file-signature fa-fw text-proprietary dark:text-proprietary-light md:text-inherit group-hover:text-proprietary dark:group-hover:text-proprietary-light"></i>
|
||||
<span class="ml-2 sm:ml-1">Bearbeiten</span>
|
||||
</a>
|
||||
</li>
|
||||
{% if post.filename_agenda %}
|
||||
<li class="block sm:inline-block group hover:bg-gray-200 dark:hover:bg-gray-800 hover:text-gray-800 dark:hover:text-gray-200">
|
||||
<a href="{% url 'posts:show_pdf_agenda' post.slug %}" class="inline-flex items-center px-2 py-1">
|
||||
<i class="fa-solid fa-file-pdf fa-fw text-red-800 dark:text-red-500 md:text-inherit group-hover:text-red-800 dark:group-hover:text-red-500"></i>
|
||||
<span class="ml-2 sm:ml-1">Download</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if post.has_protocol %}
|
||||
<div class="w-full my-2 flex items-center gap-4 text-gray-700 dark:text-gray-300" x-data="options">
|
||||
<span class="flex-1">Protokoll</span>
|
||||
<div class="relative">
|
||||
<button class="sm:hidden px-2 py-1 border border-gray-300 dark:border-gray-700 rounded" @click="openShowOptions">
|
||||
<i class="fa-solid fa-ellipsis-vertical fa-fw"></i>
|
||||
</button>
|
||||
<ul class="z-10 absolute top-0 right-0 sm:flex flex-row sm:static flex-none border dark:border-2 border-gray-300 dark:border-gray-700 rounded divide-y-2 sm:divide-y-0 sm:divide-x divide-gray-300 dark:divide-gray-700 bg-gray-100 dark:bg-gray-800 shadow sm:bg-transparent sm:shadow-none"
|
||||
@click.outside="closeShowOptions"
|
||||
x-show="getShowOptions"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="transform origin-right opacity-0 scale-95"
|
||||
x-transition:enter-end="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave-end="transform origin-right opacity-0 scale-95"
|
||||
>
|
||||
<li class="block sm:inline-block group hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-800 dark:hover:text-gray-200">
|
||||
<a href="{{ post.protocol_link }}" class="inline-flex items-center px-2 py-1"><i class="fa-solid fa-file-signature fa-fw text-proprietary dark:text-proprietary-light md:text-inherit group-hover:text-proprietary dark:group-hover:text-proprietary-light"></i>
|
||||
<span class="ml-2 sm:ml-1">Bearbeiten</span>
|
||||
</a>
|
||||
</li>
|
||||
{% if post.filename_protocol %}
|
||||
<li class="block sm:inline-block group hover:bg-gray-200 dark:hover:bg-gray-800 hover:text-gray-800 dark:hover:text-gray-200">
|
||||
<a href="{% url 'posts:show_pdf_protocol' post.slug %}" class="inline-flex items-center px-2 py-1">
|
||||
<i class="fa-solid fa-file-pdf fa-fw text-red-800 dark:text-red-500 md:text-inherit group-hover:text-red-800 dark:group-hover:text-red-500"></i>
|
||||
<span class="ml-2 sm:ml-1">Download</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if post.post_type == 'F' %}
|
||||
{% get_flatpages '/bs/' for user as pages %}
|
||||
{% if pages %}
|
||||
<div class="w-full my-2 flex items-center gap-4 text-gray-700 dark:text-gray-300" x-data="options">
|
||||
<span class="flex-1">{{ pages.first.title }}</span>
|
||||
<div class="relative">
|
||||
<button class="sm:hidden px-2 py-1 border border-gray-300 dark:border-gray-700 rounded" @click="openShowOptions">
|
||||
<i class="fa-solid fa-ellipsis-vertical fa-fw"></i>
|
||||
</button>
|
||||
<ul class="z-10 absolute top-0 right-0 sm:flex flex-row sm:static flex-none border dark:border-2 border-gray-300 dark:border-gray-700 rounded divide-y-2 sm:divide-y-0 sm:divide-x divide-gray-300 dark:divide-gray-700 bg-gray-100 dark:bg-gray-800 shadow sm:bg-transparent sm:shadow-none"
|
||||
@click.outside="closeShowOptions"
|
||||
x-show="getShowOptions"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="transform origin-right opacity-0 scale-95"
|
||||
x-transition:enter-end="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave-end="transform origin-right opacity-0 scale-95"
|
||||
>
|
||||
<li class="block sm:inline-block group hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-800 dark:hover:text-gray-200">
|
||||
<a href="{{ pages.first.url }}" class="inline-flex items-center px-2 py-1"><i class="fa-solid fa-file-lines fa-fw text-proprietary dark:text-proprietary-light md:text-inherit group-hover:text-proprietary dark:group-hover:text-proprietary-light"></i>
|
||||
<span class="ml-2 sm:ml-1">Übersicht</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% for file in files %}
|
||||
<div class="w-full my-2 flex items-center gap-4 text-gray-700 dark:text-gray-300" x-data="options">
|
||||
<span class="flex-1">{{ file.title }}</span>
|
||||
<div class="relative">
|
||||
<button class="sm:hidden px-2 py-1 border border-gray-300 dark:border-gray-700 rounded" @click="openShowOptions">
|
||||
<i class="fa-solid fa-ellipsis-vertical fa-fw"></i>
|
||||
</button>
|
||||
<ul class="z-10 absolute top-0 right-0 sm:flex flex-row sm:static flex-none border dark:border-2 border-gray-300 dark:border-gray-700 rounded divide-y-2 sm:divide-y-0 sm:divide-x divide-gray-300 dark:divide-gray-700 bg-gray-100 dark:bg-gray-800 shadow sm:bg-transparent sm:shadow-none"
|
||||
@click.outside="closeShowOptions"
|
||||
x-show="getShowOptions"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="transform origin-right opacity-0 scale-95"
|
||||
x-transition:enter-end="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="transform origin-right opacity-100 scale-100"
|
||||
x-transition:leave-end="transform origin-right opacity-0 scale-95"
|
||||
>
|
||||
<li class="block sm:inline-block group hover:bg-gray-200 dark:hover:bg-gray-800 hover:text-gray-800 dark:hover:text-gray-200">
|
||||
<a href="{{ file.file_field.url }}" class="inline-flex items-center px-2 py-1" target="_blank">
|
||||
<i class="fa-solid fa-file-pdf fa-fw text-red-800 dark:text-red-500 md:text-inherit group-hover:text-red-800 dark:group-hover:text-red-500"></i>
|
||||
<span class="ml-2 sm:ml-1">Download</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% block files_buttons %}
|
||||
{% endblock %}
|
||||
|
||||
<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">
|
||||
<a href="{% url 'posts:show' previous %}" class="w-1/2 p-4 flex items-center gap-2">
|
||||
<a href="{% url 'posts:post' previous %}" class="w-1/2 p-4 flex items-center gap-2">
|
||||
<i class="fa-solid fa-chevron-left text-gray-600 dark:text-gray-400"></i>
|
||||
<span class="text-gray-700 dark:text-gray-300 font-medium">Vorheriger Artikel</span>
|
||||
<span class="text-gray-700 dark:text-gray-300 font-medium">{% block prev_text %}Vorheriger Artikel{% endblock %}</span>
|
||||
</a>
|
||||
<a href="{% url 'posts:show' next %}" class="w-1/2 p-4 flex flex-row-reverse items-center gap-2">
|
||||
<a href="{% url 'posts:post' next %}" class="w-1/2 p-4 flex flex-row-reverse items-center gap-2">
|
||||
<i class="fa-solid fa-chevron-right text-gray-600 dark:text-gray-400"></i>
|
||||
<span class="text-gray-700 dark:text-gray-300 font-medium">Nächster Artikel</span>
|
||||
<span class="text-gray-700 dark:text-gray-300 font-medium">{% block next_text %}Nächster Artikel{% endblock %}</span>
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
{% if request.user.is_authenticated %}
|
||||
{% if post.post_type == 'N' %}
|
||||
<a href="{% url 'admin:posts_news_change' post.id %}" class="sm:hidden block w-full btn btn-primary mt-4">
|
||||
<i class="fa-solid fa-pen-to-square mr-1"></i>Artikel bearbeiten
|
||||
</a>
|
||||
{% elif post.post_type == 'E' %}
|
||||
<a href="{% url 'admin:posts_event_change' post.id %}" class="sm:hidden block w-full btn btn-primary mt-4">
|
||||
<i class="fa-solid fa-pen-to-square mr-1"></i>Event bearbeiten
|
||||
</a>
|
||||
{% elif post.post_type == 'F' %}
|
||||
<a href="{% url 'admin:posts_fetmeeting_change' post.id %}" class="sm:hidden block w-full btn btn-primary mt-4">
|
||||
<i class="fa-solid fa-pen-to-square mr-1"></i>FET Sitzung bearbeiten
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% block update_button_mobile %}
|
||||
{% endblock %}
|
||||
</section>
|
||||
|
||||
{% if related_posts %}
|
||||
{% if related_posts and related_posts|length > 1 %}
|
||||
<section class="mx-auto w-full px-4">
|
||||
<h2 class="my-4 sm:my-8 text-proprietary dark:text-proprietary-lighter text-xl text-center uppercase tracking-wider">Weiterlesen</h2>
|
||||
<div class="flex justify-evenly flex-wrap gap-4">
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<i class="fa-solid fa-xmark"></i>
|
||||
</div>
|
||||
</div>
|
||||
<form action="" method="GET" class="grid grid-cols-1 gap-2 sm:gap-4">
|
||||
<form action="" method="post" class="grid grid-cols-1 gap-2 sm:gap-4">
|
||||
<label class="block">
|
||||
<span class="text-gray-700 dark:text-gray-200">Task-Gruppe</span>
|
||||
<select id="id_tasklist" name="tasklist" class="block w-full mt-1 rounded-md border-gray-300 dark:border-none shadow-sm focus:border-none focus:ring focus:ring-blue-200 dark:focus:ring-sky-700 focus:ring-opacity-50">
|
||||
@@ -95,7 +95,7 @@
|
||||
<label class="mb-2 flex justify-between items-start">
|
||||
<div class="inline-flex items-baseline mr-2">
|
||||
<input type="checkbox" name="checkbox" value="{{ task.id }}" {% if task.completed %}checked{% endif %} class="rounded border-gray-300 dark:border-none text-proprietary shadow-sm focus:border-blue-300 focus:ring focus:ring-offset-0 focus:ring-blue-200 dark:focus:ring-sky-700 focus:ring-opacity-50">
|
||||
<span class="ml-2">{{ task.title|truncatechars:45 }} <a href="{% url 'tasks:task-detail' task.slug %}" class="inline-block text-proprietary dark:text-proprietary-lighter">Link <i class="fa-solid fa-link"></i></a></span>
|
||||
<span class="ml-2">{{ task.title|truncatechars:45 }} <a href="{% url 'tasks:task' task.slug %}" class="inline-block text-proprietary dark:text-proprietary-lighter">Link <i class="fa-solid fa-link"></i></a></span>
|
||||
</div>
|
||||
{% if task.due_date %}
|
||||
<div class="inline-flex gap-2 flex-shrink-0">
|
||||
@@ -110,7 +110,7 @@
|
||||
<div class="flex flex-col md:flex-row gap-y-2 md:gap-y-0 md:gap-x-2 lg:justify-end mt-4">
|
||||
<input type="submit" name="btn_checkbox" value="Tasks abschließen" class="btn btn-success block md:flex-grow lg:flex-grow-0">
|
||||
|
||||
<a href="{% url 'tasks:task-create' %}" class="btn btn-primary block md:flex-grow lg:flex-grow-0"><i class="fa-solid fa-plus-square mr-2"></i>Task hinzufügen</a>
|
||||
<a href="{% url 'tasks:task_create' %}" class="btn btn-primary block md:flex-grow lg:flex-grow-0"><i class="fa-solid fa-plus-square mr-2"></i>Task hinzufügen</a>
|
||||
</div>
|
||||
</form>
|
||||
{% else %}
|
||||
@@ -118,7 +118,7 @@
|
||||
<h2 class="mb-1 text-gray-700 dark:text-gray-200">Keine Tasks in dieser Liste für dich.</h2>
|
||||
|
||||
<div class="flex flex-col md:flex-row gap-y-2 md:gap-y-0 md:gap-x-2 lg:justify-end mt-4">
|
||||
<a href="{% url 'tasks:task-create' %}" class="btn btn-primary block md:flex-grow lg:flex-grow-0"><i class="fa-solid fa-plus-square mr-2"></i>Task hinzufügen</a>
|
||||
<a href="{% url 'tasks:task_create' %}" class="btn btn-primary block md:flex-grow lg:flex-grow-0"><i class="fa-solid fa-plus-square mr-2"></i>Task hinzufügen</a>
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
<section>
|
||||
<div class="documentList rounded divide-y divide-gray-300 dark:divide-gray-600">
|
||||
<a href="{% url 'tasks:docu-create' task.slug %}" class="flex justify-between">
|
||||
<a href="{% url 'tasks:docu_create' task.slug %}" class="flex justify-between">
|
||||
<h3 class="text-gray-800 dark:text-gray-200"><i class="fa-solid fa-plus fa-fw mr-1"></i>Neues Etherpad erstellen</h2>
|
||||
</a>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<a href="{% url 'tasks:task-update' task.slug %}" class="btn btn-primary block place-self-end"><i class="fa-solid fa-pen-to-square mr-2"></i>Task bearbeiten</a>
|
||||
<a href="{% url 'tasks:task_update' task.slug %}" class="btn btn-primary block place-self-end"><i class="fa-solid fa-pen-to-square mr-2"></i>Task bearbeiten</a>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import pytest
|
||||
from django.urls import reverse
|
||||
from django.core.validators import ValidationError
|
||||
import datetime
|
||||
from documents.api import ep_client, createPadifNotExists
|
||||
|
||||
import pytest
|
||||
from django.core.validators import ValidationError
|
||||
from django.urls import reverse
|
||||
|
||||
from documents.api import createPadifNotExists, ep_client
|
||||
|
||||
|
||||
class TestEtherpad:
|
||||
|
||||
@@ -1,51 +1,61 @@
|
||||
# pylint: skip-file
|
||||
import pytest
|
||||
from posts.models import News, Post, Event, FetMeeting
|
||||
from django.urls import reverse
|
||||
from django.core.validators import ValidationError
|
||||
import datetime
|
||||
from urllib.request import URLError
|
||||
import random
|
||||
import random
|
||||
import string
|
||||
from urllib.request import URLError
|
||||
|
||||
import pytest
|
||||
from django.core.validators import ValidationError
|
||||
from django.urls import reverse
|
||||
|
||||
from posts.models import Event, FetMeeting, News, Post
|
||||
|
||||
|
||||
def get_random_string(size):
|
||||
chars = string.ascii_lowercase+string.ascii_uppercase+string.digits
|
||||
return ''.join(random.choice(chars) for _ in range(size))
|
||||
chars = string.ascii_lowercase + string.ascii_uppercase + string.digits
|
||||
return "".join(random.choice(chars) for _ in range(size))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_createpad(mocker):
|
||||
create_pad=mocker.patch('posts.models.createPadifNotExists',autospec=True, return_value="slug")
|
||||
create_pad = mocker.patch(
|
||||
"posts.models.createPadifNotExists", autospec=True, return_value="slug"
|
||||
)
|
||||
return create_pad
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def post(db):
|
||||
return Post(title="asdf"+get_random_string(20), post_type="N")
|
||||
return Post(title="asdf" + get_random_string(20), post_type="N")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def post_saved(post, mock_createpad):
|
||||
post.save()
|
||||
return post
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fetmeeting(db):
|
||||
return FetMeeting(event_start = datetime.datetime(2020,1,1,18,0))
|
||||
return FetMeeting(event_start=datetime.datetime(2020, 1, 1, 18, 0))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fetmeeting_saved(fetmeeting):
|
||||
fetmeeting.save()
|
||||
return fetmeeting
|
||||
|
||||
|
||||
def test_true():
|
||||
assert True
|
||||
|
||||
|
||||
class TestPostModel:
|
||||
def test_model(self, post):
|
||||
assert isinstance(post,Post)
|
||||
assert isinstance(post, Post)
|
||||
|
||||
def test_model_id(self, post_saved):
|
||||
assert post_saved.id==1
|
||||
assert post_saved.id == 1
|
||||
|
||||
def test_model_notnews(self, post):
|
||||
assert not isinstance(post, News)
|
||||
@@ -57,35 +67,34 @@ class TestPostModel:
|
||||
assert reverse("show", kwargs={"id": post_saved.slug}) == post_saved.url
|
||||
|
||||
def test_default_hidden(self, post_saved):
|
||||
assert post_saved.is_hidden==False
|
||||
assert post_saved.is_hidden == False
|
||||
|
||||
def test_default_pinned(self, post_saved):
|
||||
assert post_saved.is_pinned==False
|
||||
assert post_saved.is_pinned == False
|
||||
|
||||
def test_validate_title(self, post):
|
||||
post.title=""
|
||||
post.title = ""
|
||||
with pytest.raises(ValidationError):
|
||||
post.full_clean()
|
||||
|
||||
def test_validate_eventdate(self, post):
|
||||
post.post_type="E"
|
||||
post.event_start=datetime.datetime(2020,10,1,18,0)
|
||||
post.event_end=datetime.datetime(2020,10,1,19,0)
|
||||
post.post_type = "E"
|
||||
post.event_start = datetime.datetime(2020, 10, 1, 18, 0)
|
||||
post.event_end = datetime.datetime(2020, 10, 1, 19, 0)
|
||||
post.full_clean()
|
||||
assert True
|
||||
|
||||
|
||||
def test_validate_dates(self,post):
|
||||
post.post_type="E"
|
||||
post.event_start=datetime.datetime(2020,10,1,19,0)
|
||||
post.event_end=datetime.datetime(2020,10,1,18,0)
|
||||
def test_validate_dates(self, post):
|
||||
post.post_type = "E"
|
||||
post.event_start = datetime.datetime(2020, 10, 1, 19, 0)
|
||||
post.event_end = datetime.datetime(2020, 10, 1, 18, 0)
|
||||
with pytest.raises(ValidationError):
|
||||
post.full_clean()
|
||||
|
||||
def test_validate_date2(self,post):
|
||||
post.post_type="N"
|
||||
post.event_start=datetime.datetime(2020,10,1,18,0)
|
||||
post.event_end=datetime.datetime(2020,10,1,19,0)
|
||||
def test_validate_date2(self, post):
|
||||
post.post_type = "N"
|
||||
post.event_start = datetime.datetime(2020, 10, 1, 18, 0)
|
||||
post.event_end = datetime.datetime(2020, 10, 1, 19, 0)
|
||||
with pytest.raises(ValidationError):
|
||||
post.full_clean()
|
||||
|
||||
@@ -94,63 +103,64 @@ class TestPostModel:
|
||||
assert True
|
||||
|
||||
|
||||
|
||||
class TestPostEtherpad:
|
||||
def setUp(self):
|
||||
print("running setup")
|
||||
|
||||
def test_agenda_id(self,post_saved, mock_createpad):
|
||||
mock_createpad.return_value=post_saved.slug+"-agenda"
|
||||
k=post_saved.get_agenda_key()
|
||||
mock_createpad.assert_called_with(post_saved.slug+"-agenda")
|
||||
|
||||
def test_agenda_id(self, post_saved, mock_createpad):
|
||||
mock_createpad.return_value = post_saved.slug + "-agenda"
|
||||
k = post_saved.get_agenda_key()
|
||||
mock_createpad.assert_called_with(post_saved.slug + "-agenda")
|
||||
assert post_saved.slug in str(k)
|
||||
|
||||
def test_protocol_id(self,post_saved,mock_createpad):
|
||||
mock_createpad.return_value=post_saved.slug+"-protocol"
|
||||
k=post_saved.get_protocol_key()
|
||||
mock_createpad.assert_called_with(post_saved.slug+"-protocol")
|
||||
def test_protocol_id(self, post_saved, mock_createpad):
|
||||
mock_createpad.return_value = post_saved.slug + "-protocol"
|
||||
k = post_saved.get_protocol_key()
|
||||
mock_createpad.assert_called_with(post_saved.slug + "-protocol")
|
||||
assert post_saved.slug in str(k)
|
||||
|
||||
def test_catch_url_error(self,post_saved,mock_createpad):
|
||||
def test_catch_url_error(self, post_saved, mock_createpad):
|
||||
def raise_url_error(key):
|
||||
raise URLError("Mocked Etherpad Down")
|
||||
mock_createpad.side_effect=raise_url_error
|
||||
k=post_saved.get_protocol_key()
|
||||
|
||||
mock_createpad.side_effect = raise_url_error
|
||||
k = post_saved.get_protocol_key()
|
||||
mock_createpad.assert_called()
|
||||
assert k == None
|
||||
|
||||
|
||||
class TestFetmeetingModel:
|
||||
def test_clean_fetmeeting(self, fetmeeting, mock_createpad):
|
||||
fetmeeting.full_clean()
|
||||
fetmeeting.save()
|
||||
assert True
|
||||
|
||||
|
||||
def test_clean_fetmeeting(self, fetmeeting):
|
||||
fetmeeting.event_start=None
|
||||
fetmeeting.event_start = None
|
||||
with pytest.raises(ValidationError):
|
||||
fetmeeting.clean()
|
||||
|
||||
def test_clean_fetmeeting(self, fetmeeting):
|
||||
fetmeeting.event_start=None
|
||||
fetmeeting.event_start = None
|
||||
with pytest.raises(ValidationError):
|
||||
fetmeeting.clean()
|
||||
|
||||
#Test Defaults for Fetmeeting
|
||||
|
||||
# Test Defaults for Fetmeeting
|
||||
def test_title(self, fetmeeting):
|
||||
fetmeeting.save()
|
||||
assert fetmeeting.title=="Fachschaftssitzung"
|
||||
assert fetmeeting.title == "Fachschaftssitzung"
|
||||
|
||||
def test_slug(self, fetmeeting_saved):
|
||||
assert "fachschaftssitzung" in fetmeeting_saved.slug
|
||||
|
||||
def test_slug_fachschaftsitzung(self,fetmeeting_saved):
|
||||
def test_slug_fachschaftsitzung(self, fetmeeting_saved):
|
||||
assert "2020-01-01" in fetmeeting_saved.slug
|
||||
|
||||
def test_sitzung_end(self, fetmeeting_saved):
|
||||
assert datetime.datetime(2020,1,1,20,0)==fetmeeting_saved.event_end
|
||||
assert datetime.datetime(2020, 1, 1, 20, 0) == fetmeeting_saved.event_end
|
||||
|
||||
def test_sitzung_start(self, fetmeeting_saved):
|
||||
assert datetime.datetime(2020,1,1,18,0)==fetmeeting_saved.event_start
|
||||
assert datetime.datetime(2020, 1, 1, 18, 0) == fetmeeting_saved.event_start
|
||||
|
||||
def test_post_type(self, fetmeeting_saved):
|
||||
assert fetmeeting_saved.post_type == "F"
|
||||
@@ -161,7 +171,8 @@ class TestFetmeetingModel:
|
||||
def test_meeting_hasprotocol(self, fetmeeting_saved):
|
||||
assert fetmeeting_saved.has_protocol == True
|
||||
|
||||
|
||||
class TestPostViews:
|
||||
def test_home(self,post_saved,client,db):
|
||||
res=client.get("/").content
|
||||
assert post_saved.title in str(res)
|
||||
def test_home(self, post_saved, client, db):
|
||||
res = client.get("/").content
|
||||
assert post_saved.title in str(res)
|
||||
|
||||
Reference in New Issue
Block a user