diff --git a/fet2020/fet2020/urls.py b/fet2020/fet2020/urls.py index d0fbda3e..bb0bac6d 100644 --- a/fet2020/fet2020/urls.py +++ b/fet2020/fet2020/urls.py @@ -17,11 +17,11 @@ from django.contrib import admin from django.urls import path, include from django.conf.urls.static import static from django.conf import settings +from django.views.generic import RedirectView from . import views from posts.views import PostViewSet from members.views import MemberViewSet from rest_framework import routers -# from authentications.decorators import authenticated_user router = routers.DefaultRouter() router.register(r'posts', PostViewSet) @@ -30,6 +30,7 @@ router.register(r'members', MemberViewSet) urlpatterns = [ path('posts/', include('posts.urls')), path('admin/doc/', include('django.contrib.admindocs.urls')), + path('admin/login/', RedirectView.as_view(pattern_name='login')), path('admin/', admin.site.urls), path('auth/', include('authentications.urls')), path('', views.index, name='home'), diff --git a/fet2020/fet2020/views.py b/fet2020/fet2020/views.py index caab4b18..4ad4f151 100644 --- a/fet2020/fet2020/views.py +++ b/fet2020/fet2020/views.py @@ -5,7 +5,7 @@ from posts.models import Post, FetMeeting, Event def index(request): - posts = deque(Post.article_objects.all()) + posts = deque(Post.articles.get_visible_articles()) def get_tags(lst): for p in lst: @@ -13,16 +13,24 @@ def index(request): yield "#" + t t = set(t for t in get_tags(posts)) - if len(posts) >= 1: - featured_post = posts.popleft() + + # set the pinned post + featured_post = Post.articles.get_pinned_article() + if not featured_post: + # if a pinned post does not exist, take the last published one. + if len(posts) >= 1: + featured_post = posts.popleft() + else: + featured_post = 0 else: - featured_post = 0 + # remove the pinned post + posts.remove(featured_post) context = { 'posts': posts, - 'events': Event.objects.get_five_events(), + 'events': Event.all_events.get_five_events(), 'featured_post': featured_post, - 'featured_post2': FetMeeting.objects.first(), + 'featured_meeting': FetMeeting.objects.get_meetings(), 'tags_list': ", ".join(t) } diff --git a/fet2020/members/urls.py b/fet2020/members/urls.py index d0f901f7..05a767ce 100644 --- a/fet2020/members/urls.py +++ b/fet2020/members/urls.py @@ -8,6 +8,5 @@ urlpatterns = [ path('', views.members_view), path('jobs/', views.jobs_view), - path('job/', views.job_view), path('member/', views.profile_view, name="member"), ] diff --git a/fet2020/members/views.py b/fet2020/members/views.py index d82db449..21512f26 100644 --- a/fet2020/members/views.py +++ b/fet2020/members/views.py @@ -1,9 +1,8 @@ from django.shortcuts import render -# from django.http import HttpResponse from collections import deque -from .models import Member, JobMember, JobGroup, MemberSerializer, Job +from .models import Member, JobMember, JobGroup, MemberSerializer from rest_framework import viewsets from rest_framework import permissions @@ -47,24 +46,6 @@ def jobs_view(request, slug=None): return render(request, 'members/index.html', context) -def job_view(request, slug=None): - job_group = deque(JobGroup.objects.all()) - job_list = [] - job = Job.objects.get(slug=slug) - job_names = [job.name] - active_members = JobMember.active_member.get_members_of_job(job_names=job_names) - inactive_members = JobMember.inactive_member.get_members_of_job(job_names=job_names) - - for idx, item in enumerate(job_names): - job_list.append((job_names[idx], active_members[idx], inactive_members[idx])) - - context = { - "job_group": job_group, - "job_list": job_list, - } - return render(request, 'members/index.html', context) - - def members_view(request, filter=None): job_group = deque(JobGroup.objects.all()) @@ -86,12 +67,15 @@ def members_view(request, filter=None): def profile_view(request, member_name=None): job_group = deque(JobGroup.objects.all()) member = None + jobs = None member = deque(Member.all_members.filter(nickname=member_name)) + jobs = deque(JobMember.members.filter(member__nickname=member_name)) context = { "job_group": job_group, "member": member, + "jobs": jobs, } return render(request, 'members/index.html', context) diff --git a/fet2020/posts/admin.py b/fet2020/posts/admin.py index 24f3b901..c964ad38 100644 --- a/fet2020/posts/admin.py +++ b/fet2020/posts/admin.py @@ -22,8 +22,9 @@ make_fetmeeting.short_description = "In eine Fachschaftssitzung konvertieren" class MyPostAdmin(admin.ModelAdmin): form = MyPostForm model = Post - list_filter = ['is_event'] - list_display = ['title', 'subtitle', 'slug', 'public_date'] + list_filter = ['is_pinned', 'is_hidden'] + list_display = ['title', 'subtitle', 'slug', 'public_date', 'is_pinned', 'is_hidden'] + ordering = ['is_hidden', '-public_date'] def add_view(self, request, form_url='', extra_context=None): extra_context = extra_context or {} @@ -59,7 +60,8 @@ class MyPostAdmin(admin.ModelAdmin): class MyEventAdmin(MyPostAdmin): form = MyEventForm model = Event - list_display = ['title', 'subtitle', 'slug', 'event_start', 'public_date'] + list_display = ['title', 'subtitle', 'slug', 'event_start', 'public_date', 'is_pinned'] + ordering = ['-event_start'] actions = [make_fetmeeting] @@ -77,6 +79,8 @@ admin.site.register(News, MyNewsAdmin) class MyFetMeetingAdmin(MyEventAdmin): form = MyFetMeetingForm model = FetMeeting + list_filter = [] + list_display = ['title', 'slug', 'event_start', 'public_date'] admin.site.register(FetMeeting, MyFetMeetingAdmin) diff --git a/fet2020/posts/forms.py b/fet2020/posts/forms.py index ef256577..d6b0c1e5 100644 --- a/fet2020/posts/forms.py +++ b/fet2020/posts/forms.py @@ -21,7 +21,10 @@ class MyPostForm(forms.ModelForm): class MyNewsForm(MyPostForm): class Meta: model = News - fields = ['title', 'subtitle', 'tags', 'image', 'body', 'slug', 'author', 'public_date'] + fields = [ + 'title', 'subtitle', 'tags', 'image', 'body', 'slug', 'author', 'public_date', + 'is_pinned', 'is_hidden', + ] widgets = {'body': CKEditorUploadingWidget(config_name='default')} @@ -34,7 +37,7 @@ class MyEventForm(MyPostForm): model = Event fields = [ 'title', 'subtitle', 'tags', 'image', 'body', 'event_start', 'event_end', - 'event_place', 'slug', 'author', 'public_date', + 'event_place', 'slug', 'author', 'public_date', 'is_pinned', ] widgets = {'body': CKEditorUploadingWidget(config_name='default')} diff --git a/fet2020/posts/managers.py b/fet2020/posts/managers.py new file mode 100644 index 00000000..f46c1e24 --- /dev/null +++ b/fet2020/posts/managers.py @@ -0,0 +1,82 @@ +from django.db import models +from django.db.models import Q +from django.utils import timezone + + +class PostManager(models.Manager): + def get_queryset(self): + return super().get_queryset() + + def get_visible_articles(self): + return self.get_queryset().filter(is_hidden=False) + + +class ArticleManager(models.Manager): + """ + Provide a query set only for "Article" + regular fet meetings should not be contained in the news stream + """ + def get_queryset(self): + return super().get_queryset().filter( + Q(post_type='E') + | Q(post_type='N') + ).order_by('-public_date') + + def get_visible_articles(self): + return self.get_queryset().filter(is_hidden=False) + + def get_pinned_article(self): + return self.get_visible_articles().filter(is_pinned=True).first() + + +class NewsManager(models.Manager): + """ + Provide a query set only for "News" + """ + def get_queryset(self): + return super().get_queryset().filter(post_type='N') + + def get_visible_articles(self): + return self.get_queryset().filter(is_hidden=False) + + +class AllEventManager(models.Manager): + """ + Provide a query set for all events ("Event" and "Fet Meeting") + """ + def get_queryset(self): + return super().get_queryset().filter(Q(post_type='E') | Q(post_type='F')) + + def get_five_events(self): + return self.get_queryset().order_by('-event_start')[:5] + + +class EventManager(models.Manager): + """ + Provide a query set only for "Events" + regular fet meetings should not be contained in the news stream + """ + def get_queryset(self): + return super().get_queryset().filter(post_type='E') + + +class FetMeetingManager(models.Manager): + """ + Provide a query set only for "Fet Meeting" + """ + def get_queryset(self): + return super().get_queryset().filter(post_type='F') + + def _get_future_events(self): + date_today = timezone.now() + return self.get_queryset().filter(event_start__gt=date_today).order_by('event_start') + + def _get_past_events(self): + date_today = timezone.now() + return self.get_queryset().filter(event_start__lt=date_today).order_by('-event_start') + + def get_meetings(self): + meetings = [] + meetings.append(self._get_future_events().first()) + meetings.append(self._get_past_events().first()) + return meetings diff --git a/fet2020/posts/models.py b/fet2020/posts/models.py index 483618cd..59fb0d4a 100644 --- a/fet2020/posts/models.py +++ b/fet2020/posts/models.py @@ -1,16 +1,21 @@ from django.contrib.auth.models import User from django.core.validators import ValidationError from django.db import models -from django.db.models import Q 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 members.models import Member from taggit.managers import TaggableManager + # import documents from documents import ep +from .managers import ( + PostManager, ArticleManager, NewsManager, AllEventManager, EventManager, FetMeetingManager +) + + + # from ckeditor_uploader import RichTextUploadingField # import uuid import re @@ -22,65 +27,6 @@ import logging logger = logging.getLogger('posts') -############ -# MANAGERS # -############ - -class PostManager(models.Manager): - def get_queryset(self): - return super().get_queryset() - - -class ArticleManager(models.Manager): - """ - Provide a query set only for "Article" - regular fet meetings should not be contained in the news stream - """ - def get_queryset(self): - return super().get_queryset().filter( - Q(post_type='E') - | Q(post_type='N') - ).order_by('-public_date') - - -class NewsManager(models.Manager): - def get_queryset(self): - return super().get_queryset().filter(~Q(is_event=True)) - - -class EventManager(models.Manager): - """ - Provide a query set only for "Events" - regular fet meetings should not be contained in the news stream - """ - def get_queryset(self): - return super().get_queryset().filter( - Q(is_event=True) - & ~Q(post_type='F') - ).order_by('-public_date') - - def get_all_events(self): - date_today = timezone.now().date() - return super().get_queryset().filter( - Q(is_event=True) - & Q(event_start__gt=date_today) - ).order_by('-event_start') - - def get_five_events(self): - return super().get_queryset().filter( - Q(is_event=True) - ).order_by('-event_start')[:5] - - -class FetMeetingManager(models.Manager): - def get_queryset(self): - return super().get_queryset().filter(Q(post_type='F')) - - -########## -# MODELS # -########## - class Category(models.Model): # Titel des Posts title = models.CharField(max_length=200) @@ -128,7 +74,11 @@ class Post(models.Model): ] post_type = models.CharField(max_length=1, choices=__choices, editable=False) - is_event = models.BooleanField(default=False) + # post is pinned at main page + is_pinned = models.BooleanField(default=False) + + # post is hidden from newsfeed (e.g. about) + is_hidden = models.BooleanField(default=False) # Zusatz Info wenn ein Event gepostet wird event_start = models.DateTimeField('Event Start', null=True, blank=True) @@ -147,7 +97,7 @@ class Post(models.Model): # Managers objects = PostManager() - article_objects = ArticleManager() + articles = ArticleManager() def get_tags(self): """Returns assigned tags as a comma seperated list.""" @@ -191,10 +141,6 @@ class Post(models.Model): def url(self): return reverse('posts.show', kwargs={"id": self.slug}) - @property - def author_member(self): - return Member.all_members.filter(nickname=self.author.username).first() - def save(self, *args, **kwargs): "save the post with some defaults" if (self.id is None) and (not self.slug): @@ -253,7 +199,8 @@ class News(Post): class Event(Post): - objects = EventManager() + only_events = EventManager() + all_events = AllEventManager() class Meta: proxy = True @@ -276,8 +223,6 @@ class Event(Post): raise ValidationError(_('Das Ende des Events liegt vor dem Beginn.')) def save(self, *args, **kwargs): - self.is_event = True - if not self.post_type: self.post_type = 'E' @@ -296,12 +241,15 @@ class FetMeeting(Event): def clean(self): if self.event_start is None: raise ValidationError(_('Das Datum des Events fehlt.')) + elif (self.event_end) and (self.event_end < self.event_start): + raise ValidationError(_('Das Ende des Events liegt vor dem Beginn.')) slug = slugify(self.event_start.date()) + "-" + slugify("Fachschaftssitzung") - #dieser Check verhindert erneutes speichern - #if Post.objects.filter(slug=slug).count() != 0: - # raise ValidationError(_('Es existiert bereits eine Sitzung mit demselben Datum.')) + + if Post.objects.filter(slug=slug).exists(): + if Post.objects.get(slug=slug).id != self.id: + raise ValidationError(_('Es existiert bereits eine Sitzung mit demselben Datum.')) def save(self, *args, **kwargs): self.title = "Fachschaftssitzung" diff --git a/fet2020/posts/views.py b/fet2020/posts/views.py index 4a1d1d9a..40eb18f4 100644 --- a/fet2020/posts/views.py +++ b/fet2020/posts/views.py @@ -9,8 +9,8 @@ from taggit.models import Tag from rest_framework import viewsets # from rest_framework import permissions -from .models import Post, PostSerializer -# from members.models import Member +from .models import Post, PostSerializer, FetMeeting +from members.models import Member # from documents import add_ep_to_response, get_ep_sessionid2 from documents import get_pad_link from documents.etherpadlib import add_ep_cookie @@ -23,7 +23,7 @@ from collections import deque ################## def index(request): - posts = deque(Post.objects.order_by('-public_date').all()) + posts = deque(Post.objects.get_visible_articles().order_by('-public_date')) def f(p): return p.tags @@ -33,13 +33,15 @@ def index(request): def tags(request, tag=""): - posts = deque(Post.objects.filter(tags__name=tag)) - featured_post = Post.objects.filter(slug=tag).first() + posts = deque(Post.objects.get_visible_articles().filter(tags__name=tag)) + featured_post = Post.objects.get_visible_articles().filter(slug=tag).first() + context = { "posts": posts, "featured_post": featured_post, "tags_list": None, } + return render(request, 'posts/tag.html', context) @@ -49,14 +51,18 @@ def show(request, id=None): elif id != "" and id is not None: p = Post.objects.get(slug=(id)) - # post_author = Member.all_members.filter(nickname=p.author) + post_author = Member.all_members.filter(nickname=p.author).first() + author_image = None + + if post_author: + author_image = post_author.image['avatar'].url context = { "post": p, - "next": get_next_dict().get(p.slug, None), + "author_image": author_image, + "next": get_next_dict(p), "related_posts": p.tags.similar_objects(), "ep_link": get_pad_link(p.slug + "-agenda"), - } response = render(request, 'posts/show.html', context) @@ -106,17 +112,27 @@ def tag_complete(request): return HttpResponseServerError("Requires a term field.") -def get_next_dict(): +def get_next_dict(post=None): # TODO: Docstring - posts = Post.article_objects.order_by('-public_date').values('slug') - print(posts) - d = {} - print(d) - for k, v in enumerate(posts): - if k == len(posts) - 1: - break - d[v['slug']] = posts[k + 1]['slug'] - print(d) + posts = None + d = post.slug + + if post: + # TODO: bad implementation but it works!! + if post.post_type == 'N' or post.post_type == 'E': + posts = Post.articles.get_visible_articles() + elif post.post_type == 'F': + posts = FetMeeting.objects.get_queryset().order_by('-event_start') + + 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 diff --git a/fet2020/requirements.txt b/fet2020/requirements.txt index 51907c99..c900a656 100644 --- a/fet2020/requirements.txt +++ b/fet2020/requirements.txt @@ -1,9 +1,9 @@ -django -django-taggit -django-ckeditor -Pillow -djangorestframework -django-static-jquery-ui -docutils -easy-thumbnails -etherpad-lite \ No newline at end of file +django==3.1.* +django-taggit==1.3.0 +django-ckeditor==5.9.0 +Pillow==7.2.0 +djangorestframework==3.11.0 +django-static-jquery-ui==1.12.1.1 +docutils==0.16 +easy-thumbnails==2.7.0 +etherpad-lite==0.5 \ No newline at end of file diff --git a/fet2020/templates/home.html b/fet2020/templates/home.html index c79ec622..cd5d3f09 100644 --- a/fet2020/templates/home.html +++ b/fet2020/templates/home.html @@ -34,9 +34,9 @@ {% with post=featured_post %} {% include 'posts/partials/_article_row.html' %} {% endwith %} - {% with post=featured_post2 %} - {% include 'posts/partials/_article_row.html' %} - {% endwith %} + {% for post in featured_meeting %} + {% include 'posts/partials/_meeting_row.html' %} + {% endfor %} diff --git a/fet2020/templates/members/jobs_list.html b/fet2020/templates/members/jobs_list.html index e2514a16..7d09c3bd 100644 --- a/fet2020/templates/members/jobs_list.html +++ b/fet2020/templates/members/jobs_list.html @@ -1,3 +1,4 @@ + {% if active_members %} Aktuelle Mitglieder: {% with job_memberships=active_members %} diff --git a/fet2020/templates/members/members_list.html b/fet2020/templates/members/members_list.html index e97af0d6..72dddb02 100644 --- a/fet2020/templates/members/members_list.html +++ b/fet2020/templates/members/members_list.html @@ -1,18 +1,11 @@
-

Grid Style

{% for member in members %}
+

{{member.surname}}

{% include 'members/partials/_member.html' %}
{% endfor %}
- -

Grid Style 2

-
- {% for member in members %} - {% include 'members/partials/_member.html' %} - {% endfor %} -
diff --git a/fet2020/templates/members/partials/_job_membership_grid.html b/fet2020/templates/members/partials/_job_membership_grid.html index ca4ad034..db804308 100644 --- a/fet2020/templates/members/partials/_job_membership_grid.html +++ b/fet2020/templates/members/partials/_job_membership_grid.html @@ -1,8 +1,10 @@
{% for mem in job_memberships %} {% with member=mem.member %} -
{{mem.get_job_role_display}} (seit {{mem.job_start}}) +
+

{{mem.member.surname}}

{% include 'members/partials/_member.html' %} +

{{mem.get_job_role_display}} ({{mem.job_start}} - {{mem.job_end}})

{%endwith %} {% endfor %} diff --git a/fet2020/templates/members/partials/_member.html b/fet2020/templates/members/partials/_member.html index b2ae4f58..4ed16643 100644 --- a/fet2020/templates/members/partials/_member.html +++ b/fet2020/templates/members/partials/_member.html @@ -1,8 +1,10 @@ - - {# only thumb and name of member #} -{% load thumbnail %} -

{{member.nickname}}

{{member.firstname}} {{member.surname}}

-
\ No newline at end of file +
+
+

{{member.nickname}}

+

{{member.firstname}} {{member.surname}}

+
+
+ diff --git a/fet2020/templates/members/partials/_member_details.html b/fet2020/templates/members/partials/_member_details.html index f395b813..9584f317 100644 --- a/fet2020/templates/members/partials/_member_details.html +++ b/fet2020/templates/members/partials/_member_details.html @@ -1,19 +1,20 @@ {# This template shows one member and all the details (that are ment for public) including a list of current jobs #} -{% load thumbnail %}
-
-
- - +
+
+ +
+
+
+

{{member.firstname}} {{member.surname}}

+

Spitzname: {{member.nickname}}
+ Name: {{member.firstname}} {{member.surname}}
+ Mailaccount: {{member.mailaccount}}
+ Beschreibung: {{member.description|safe}}
+

+ ehrenamtliche Tätigkeiten
+ {% for jobm in jobs %} + {{jobm.job.name}}: {{jobm.job_start|date}} - {{jobm.job_end|date}}
+ {% endfor %}
-
-
-

{{member.firstname}} {{member.surname}}

-

Spitzname: {{member.nickname}}
- Name: {{member.firstname}} {{member.surname}}
- Mailaccount: {{member.mailaccount}}
- Beschreibung: {{member.description|safe}}
-

-
- \ No newline at end of file diff --git a/fet2020/templates/posts/partials/_article_row.html b/fet2020/templates/posts/partials/_article_row.html index d6cf5ce3..55f062ac 100644 --- a/fet2020/templates/posts/partials/_article_row.html +++ b/fet2020/templates/posts/partials/_article_row.html @@ -1,15 +1,12 @@ - - + diff --git a/fet2020/templates/posts/partials/_date_box.html b/fet2020/templates/posts/partials/_date_box.html index 63b0adab..a982b693 100644 --- a/fet2020/templates/posts/partials/_date_box.html +++ b/fet2020/templates/posts/partials/_date_box.html @@ -1,10 +1,11 @@ - - + diff --git a/fet2020/templates/posts/partials/_meeting_row.html b/fet2020/templates/posts/partials/_meeting_row.html new file mode 100644 index 00000000..7bf9409b --- /dev/null +++ b/fet2020/templates/posts/partials/_meeting_row.html @@ -0,0 +1,11 @@ + +
+
+

{{post.title}}

+ {% if post.subtitle is not None %} +

{{post.subtitle}}

+ {% endif %} + +
+
+
diff --git a/fet2020/templates/posts/show.html b/fet2020/templates/posts/show.html index aba04016..1cde872d 100644 --- a/fet2020/templates/posts/show.html +++ b/fet2020/templates/posts/show.html @@ -15,9 +15,9 @@
{{post.subtitle | tags_to_url }} - {% if post.author_member != None %} -