diff --git a/forums/admin.py b/forums/admin.py index d2eb2e42b9ff67ed8db407396905be7734f70ad4..eb93544bf80a3350f869917de301142b8a7899cf 100644 --- a/forums/admin.py +++ b/forums/admin.py @@ -6,7 +6,7 @@ from django.contrib import admin from guardian.admin import GuardedModelAdmin -from .models import Forum, Post +from .models import Forum, Post, Motion class ForumAdmin(GuardedModelAdmin): @@ -20,3 +20,9 @@ class PostAdmin(admin.ModelAdmin): search_fields = ['posted_by', 'subject', 'text'] admin.site.register(Post, PostAdmin) + + +class MotionAdmin(admin.ModelAdmin): + search_fields = ['posted_by', 'subject', 'text'] + +admin.site.register(Motion, MotionAdmin) diff --git a/forums/templates/forums/forum_detail.html b/forums/templates/forums/forum_detail.html index 36554f75e7f682e1ac55a9ea2f98c21eb1fda0dc..9d75e0e5c5698cfa8431304d3ef4c3c508d2112a 100644 --- a/forums/templates/forums/forum_detail.html +++ b/forums/templates/forums/forum_detail.html @@ -88,17 +88,28 @@ <h3 class="highlight">Description</h3> {{ forum.description|restructuredtext }} - <h3 class="highlight">Posts</h3> - {% for post in forum.posts.all %} - {% include 'forums/post_card.html' with forum=forum post=post %} + {% if forum.meeting %} + <h3 class="highlight">Motions</h3> + <ul> + <li><a href="{% url 'forums:motion_create' slug=forum.slug parent_model='forum' parent_id=forum.id %}">Add a new Motion</a></li> + </ul> + {% for motion in forum.motions.all %} + {% include 'forums/post_card.html' with forum=forum post=motion %} {% endfor %} + {% endif %} + + <h3 class="highlight">Posts</h3> <ul> - <li><a href="{% url 'forums:post_create' slug=forum.slug parent_model='forum' parent_id=forum.id %}">Add a Post</a></li> - <li><a href="{% url 'forums:motion_create' slug=forum.slug parent_model='forum' parent_id=forum.id %}">Add a Motion</a></li> + <li><a href="{% url 'forums:post_create' slug=forum.slug parent_model='forum' parent_id=forum.id %}">Add a new Post</a></li> </ul> + + {% for post in forum.posts.all %} + {% include 'forums/post_card.html' with forum=forum post=post %} + {% endfor %} + </div> </div> diff --git a/forums/templates/forums/post_card.html b/forums/templates/forums/post_card.html index d3374fe6257cc969915f03c8773770c50c5479d7..3103355c14777befa28dc6495d1c6e5395eea386 100644 --- a/forums/templates/forums/post_card.html +++ b/forums/templates/forums/post_card.html @@ -6,7 +6,7 @@ <div class="postInfo"> {{ post.posted_by.first_name }} {{ post.posted_by.last_name }} on {{ post.posted_on|date:"Y-m-d" }} - <a href="{{ post.get_absolute_url }}"><i class="fa fa-link" title="permalink to this Post"></i></a> - {% if post.parent %} + {% if post.parent and not post.motion %} - regarding <a href="{{ post.parent.get_absolute_url }}">{{ post.parent }}</a> {% endif %} </div> @@ -15,10 +15,56 @@ {{ post.text|restructuredtext }} </div> <div class="card-footer"> - {% if post.followup_posts.all|length > 0 %} - <span class="badge badge-primary badge-pill">{% with nr_followups=post.nr_followups %}{{ nr_followups }} repl{{ nr_followups|pluralize:"y,ies" }}{% endwith %}</span> - {% endif %} - <a href="{% url 'forums:post_create' slug=forum.slug parent_model='post' parent_id=post.id %}">Reply to the above post</a> + <div class="d-flex flex-wrap justify-content-end align-items-center"> + <div class="flex-grow-1"> + {% if post.followup_posts.all|length > 0 %} + <span class="badge badge-primary badge-pill">{% with nr_followups=post.nr_followups %}{{ nr_followups }} repl{{ nr_followups|pluralize:"y,ies" }}{% endwith %}</span> + {% endif %} + <a href="{% url 'forums:post_create' slug=forum.slug parent_model='post' parent_id=post.id %}">{% if post.motion %}Post a Comment on this Motion{% else %}Reply to the above post{% endif %}</a> + </div> + + {% if post.motion %} + <div class="align-self-center px-2"> + Voting results + <span class="text-muted"> + {% if request.user in post.motion.in_agreement.all %} + <br/>You have voted <span class="text-success">Agree</span> + {% elif request.user in post.motion.in_doubt.all %} + <br/>You have voted <span class="text-warning">Doubt</span> + {% elif request.user in post.motion.in_disagreement.all %} + <br/>You have voted <span class="text-danger">Disagree</span> + {% elif request.user in post.motion.in_abstain.all %} + <br/>You have <span class="text-dark">Abstained</span> + {% elif request.user in post.motion.eligible_for_voting.all %} + <br/>[click to vote] + {% endif %} + </span> + </div> + <div> + <form action="{% url 'forums:motion_vote' slug=forum.slug motion_id=post.motion.id vote='Y' %}" method="post">{% csrf_token %} + <input type="submit" class="btn btn-success" data-toggle="tooltip" data-placement="top" title="Agree" value="{{ post.motion.in_agreement.all|length }}"> + </form> + </div> + <div> + <form action="{% url 'forums:motion_vote' slug=forum.slug motion_id=post.motion.id vote='M' %}" method="post">{% csrf_token %} + <input type="submit" class="btn btn-warning" data-toggle="tooltip" data-placement="top" title="Doubt" value="{{ post.motion.in_doubt.all|length }}"> + </form> + </div> + <div> + <form action="{% url 'forums:motion_vote' slug=forum.slug motion_id=post.motion.id vote='N' %}" method="post">{% csrf_token %} + <input type="submit" class="btn btn-danger" data-toggle="tooltip" data-placement="top" title="Disagree" value="{{ post.motion.in_disagreement.all|length }}"> + </form> + </div> + <div> + <form action="{% url 'forums:motion_vote' slug=forum.slug motion_id=post.motion.id vote='A' %}" method="post">{% csrf_token %} + <input type="submit" class="btn btn-secondary" data-toggle="tooltip" data-placement="top" title="Abstain" value="{{ post.motion.in_abstain.all|length }}"> + </form> + </div> + <div class="align-self-center px-2"> + Voting deadline:<br/>{{ post.motion.voting_deadline|date:'Y-m-d' }} + </div> + {% endif %} + </div> </div> {% if post.followup_posts.all|length > 0 %} <div class="followupPosts"> diff --git a/forums/urls.py b/forums/urls.py index 73e201e06d103bb4a3df2cd7658c775882c873ba..892e6da21b9bdb5945fb6f3e2cca9999712a8aea 100644 --- a/forums/urls.py +++ b/forums/urls.py @@ -53,23 +53,28 @@ urlpatterns = [ name='forums' ), url( - r'(?P<slug>[\w-]+)/post/(?P<parent_model>[a-z]+)/(?P<parent_id>[0-9]+)/add/$', + r'^(?P<slug>[\w-]+)/post/(?P<parent_model>[a-z]+)/(?P<parent_id>[0-9]+)/add/$', views.PostCreateView.as_view(), name='post_create' ), url( - r'(?P<slug>[\w-]+)/motion/(?P<parent_model>[a-z]+)/(?P<parent_id>[0-9]+)/add/$', + r'^(?P<slug>[\w-]+)/motion/(?P<parent_model>[a-z]+)/(?P<parent_id>[0-9]+)/add/$', views.MotionCreateView.as_view(), name='motion_create' ), url( - r'(?P<slug>[\w-]+)/post/(?P<parent_model>[a-z]+)/(?P<parent_id>[0-9]+)/add/confirm/$', + r'^(?P<slug>[\w-]+)/post/(?P<parent_model>[a-z]+)/(?P<parent_id>[0-9]+)/add/confirm/$', views.PostConfirmCreateView.as_view(), name='post_confirm_create' ), url( - r'(?P<slug>[\w-]+)/motion/(?P<parent_model>[a-z]+)/(?P<parent_id>[0-9]+)/add/confirm/$', + r'^(?P<slug>[\w-]+)/motion/(?P<parent_model>[a-z]+)/(?P<parent_id>[0-9]+)/add/confirm/$', views.MotionConfirmCreateView.as_view(), name='motion_confirm_create' ), + url( + r'^(?P<slug>[\w-]+)/motion/(?P<motion_id>[0-9]+)/(?P<vote>[YMNA])/$', + views.motion_vote, + name='motion_vote' + ), ] diff --git a/forums/views.py b/forums/views.py index 4f4a136b443a5318b1ad8a186ab68916a4a20adb..dc9eb86728d7b1d623d1779db96f6280ec8aa782 100644 --- a/forums/views.py +++ b/forums/views.py @@ -19,6 +19,7 @@ from django.views.generic.detail import DetailView from django.views.generic.edit import CreateView, UpdateView, DeleteView from django.views.generic.list import ListView +from guardian.decorators import permission_required_or_403 from guardian.mixins import PermissionRequiredMixin from guardian.shortcuts import (assign_perm, remove_perm, get_objects_for_user, get_perms, get_users_with_perms, get_groups_with_perms) @@ -330,3 +331,28 @@ class MotionConfirmCreateView(PostConfirmCreateView): del self.request.session['voting_deadline_day'] self.object = form.save() return super().form_valid(form) + + +@permission_required_or_403('forums.can_post_to_forum', + (Forum, 'slug', 'slug')) +def motion_vote(request, slug, motion_id, vote): + motion = get_object_or_404(Motion, pk=motion_id) + if datetime.date.today() > motion.voting_deadline: + messages.warning(request, 'The voting deadline on this Motion has passed.') + elif motion.eligible_for_voting.filter(pk=request.user.id).exists(): + motion.in_agreement.remove(request.user) + motion.in_doubt.remove(request.user) + motion.in_disagreement.remove(request.user) + motion.in_abstain.remove(request.user) + if vote == 'Y': + motion.in_agreement.add(request.user) + elif vote == 'M': + motion.in_doubt.add(request.user) + elif vote == 'N': + motion.in_disagreement.add(request.user) + elif vote == 'A': + motion.in_abstain.add(request.user) + motion.save() + else: + messages.warning(request, 'You do not have voting rights on this Motion.') + return redirect(motion.get_absolute_url())