add and remove users from groups

This commit is contained in:
Hugh Rundle 2021-09-25 17:34:44 +10:00
parent e800106be4
commit b645d75303
6 changed files with 100 additions and 33 deletions

View file

@ -43,3 +43,10 @@ class GroupMember(models.Model):
group = models.ForeignKey("Group", on_delete=models.CASCADE) group = models.ForeignKey("Group", on_delete=models.CASCADE)
user = models.ForeignKey("User", on_delete=models.CASCADE) user = models.ForeignKey("User", on_delete=models.CASCADE)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["group", "user"], name="unique_member"
)
]

View file

@ -30,36 +30,37 @@
{% endblock %} {% endblock %}
<ul start="{{ members.start_index }}" class="ordered-list"> <ul start="{{ members.start_index }}" class="ordered-list">
{% for member in group.members.all %}
<div class="column is-flex is-flex-grow-0"> <div class="column is-flex is-flex-grow-0">
<div class="box has-text-centered is-shadowless has-background-white-bis m-0"> {% for member in group.members.all %}
<a href="{{ user.local_path }}" class="has-text-black"> <span class="box has-text-centered is-shadowless has-background-white-bis m-0">
{% include 'snippets/avatar.html' with user=user large=True %} <a href="{{ member.local_path }}" class="has-text-black">
<span title="{{ user.display_name }}" class="is-block is-6 has-text-weight-bold">{{ user.display_name|truncatechars:10 }}</span> {% include 'snippets/avatar.html' with user=member large=True %}
<span title="@{{ user|username }}" class="is-block pb-3">@{{ user|username|truncatechars:8 }}</span> <span title="{{ member.display_name }}" class="is-block is-6 has-text-weight-bold">{{ member.display_name|truncatechars:10 }}</span>
<span title="@{{ member|username }}" class="is-block pb-3">@{{ member|username|truncatechars:8 }}</span>
</a> </a>
{% include 'snippets/add_to_group_button.html' with user=user minimal=True %} {% include 'snippets/add_to_group_button.html' with user=member minimal=True %}
{% if user.mutuals %} {% if member.mutuals %}
<p class="help"> <p class="help">
{% blocktrans trimmed with mutuals=user.mutuals|intcomma count counter=user.mutuals %} {% blocktrans trimmed with mutuals=member.mutuals|intcomma count counter=member.mutuals %}
{{ mutuals }} follower you follow {{ mutuals }} follower you follow
{% plural %} {% plural %}
{{ mutuals }} followers you follow{% endblocktrans %} {{ mutuals }} followers you follow{% endblocktrans %}
</p> </p>
{% elif user.shared_books %} {% elif member.shared_books %}
<p class="help"> <p class="help">
{% blocktrans trimmed with shared_books=user.shared_books|intcomma count counter=user.shared_books %} {% blocktrans trimmed with shared_books=member.shared_books|intcomma count counter=member.shared_books %}
{{ shared_books }} book on your shelves {{ shared_books }} book on your shelves
{% plural %} {% plural %}
{{ shared_books }} books on your shelves {{ shared_books }} books on your shelves
{% endblocktrans %} {% endblocktrans %}
</p> </p>
{% elif request.user in user.following.all %} {% elif request.user in member.following.all %}
<p class="help"> <p class="help">
{% trans "Follows you" %} {% trans "Follows you" %}
</p> </p>
{% endif %} {% endif %}
</div> </span>
</div> </div>
{% endfor %} {% endfor %}
</ul> </ul>

View file

@ -7,21 +7,21 @@
<div class="field{% if not minimal %} has-addons{% else %} mb-0{% endif %}"> <div class="field{% if not minimal %} has-addons{% else %} mb-0{% endif %}">
<div class="control"> <div class="control">
<!-- TODO: make this "add-to-group" --> <form action="{% url 'add-group-member' %}" method="POST" class="interaction add_{{ user.id }} {% if user in group.members.all %}is-hidden{%endif %}" data-id="add_{{ user.id }}">
<form action="{% url 'follow' %}" method="POST" class="interaction follow_{{ user.id }} {% if request.user in user.followers.all or request.user in user.follower_requests.all %}is-hidden{%endif %}" data-id="follow_{{ user.id }}">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="group" value="{{ group.id }}">
<input type="hidden" name="user" value="{{ user.username }}"> <input type="hidden" name="user" value="{{ user.username }}">
<button class="button is-small{% if not minimal %} is-link{% endif %}" type="submit"> <button class="button is-small{% if not minimal %} is-link{% endif %}" type="submit">
{% if show_username %} {% if show_username %}
{% blocktrans with username=user.localname %}Follow @{{ username }}{% endblocktrans %} {% blocktrans with username=user.localname %}Add @{{ username }}{% endblocktrans %}
{% else %} {% else %}
{% trans "Follow" %} {% trans "Add" %}
{% endif %} {% endif %}
</button> </button>
</form> </form>
<!-- TODO: make this "remove-from-group" --> <form action="{% url 'remove-group-member' %}" method="POST" class="interaction add_{{ user.id }} {% if user not in group.members.all %}is-hidden{%endif %}" data-id="add_{{ user.id }}">
<form action="{% url 'unfollow' %}" method="POST" class="interaction follow_{{ user.id }} {% if not request.user in user.followers.all and not request.user in user.follower_requests.all %}is-hidden{%endif %}" data-id="follow_{{ user.id }}">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="group" value="{{ group.id }}">
<input type="hidden" name="user" value="{{ user.username }}"> <input type="hidden" name="user" value="{{ user.username }}">
{% if user.manually_approves_followers and request.user not in user.followers.all %} {% if user.manually_approves_followers and request.user not in user.followers.all %}
<button class="button is-small is-danger is-light" type="submit"> <button class="button is-small is-danger is-light" type="submit">
@ -30,9 +30,9 @@
{% else %} {% else %}
<button class="button is-small is-danger is-light" type="submit"> <button class="button is-small is-danger is-light" type="submit">
{% if show_username %} {% if show_username %}
{% blocktrans with username=user.localname %}Unfollow @{{ username }}{% endblocktrans %} {% blocktrans with username=user.localname %}Remove @{{ username }}{% endblocktrans %}
{% else %} {% else %}
{% trans "Unfollow" %} {% trans "Remove" %}
{% endif %} {% endif %}
</button> </button>
{% endif %} {% endif %}

View file

@ -255,7 +255,9 @@ urlpatterns = [
re_path(rf"{USER_PATH}/groups/?$", views.UserGroups.as_view(), name="user-groups"), re_path(rf"{USER_PATH}/groups/?$", views.UserGroups.as_view(), name="user-groups"),
re_path(r"^create-group/?$", views.create_group, name="create-group"), re_path(r"^create-group/?$", views.create_group, name="create-group"),
re_path(r"^group/(?P<group_id>\d+)(.json)?/?$", views.Group.as_view(), name="group"), re_path(r"^group/(?P<group_id>\d+)(.json)?/?$", views.Group.as_view(), name="group"),
re_path(r"^group/(?P<group_id>\d+)/add-users/?$", views.FindAndAddUsers.as_view(), name="group-find-users"), re_path(r"^group/(?P<group_id>\d+)/add-users/?$", views.FindUsers.as_view(), name="group-find-users"),
re_path(r"^add-group-member/?$", views.add_member, name="add-group-member"),
re_path(r"^remove-group-member/?$", views.remove_member, name="remove-group-member"),
# lists # lists
re_path(rf"{USER_PATH}/lists/?$", views.UserLists.as_view(), name="user-lists"), re_path(rf"{USER_PATH}/lists/?$", views.UserLists.as_view(), name="user-lists"),
re_path(r"^list/?$", views.Lists.as_view(), name="lists"), re_path(r"^list/?$", views.Lists.as_view(), name="lists"),

View file

@ -32,7 +32,7 @@ from .follow import follow, unfollow
from .follow import accept_follow_request, delete_follow_request from .follow import accept_follow_request, delete_follow_request
from .get_started import GetStartedBooks, GetStartedProfile, GetStartedUsers from .get_started import GetStartedBooks, GetStartedProfile, GetStartedUsers
from .goal import Goal, hide_goal from .goal import Goal, hide_goal
from .group import Group, UserGroups, FindAndAddUsers, create_group from .group import Group, UserGroups, FindUsers, create_group, add_member, remove_member
from .import_data import Import, ImportStatus from .import_data import Import, ImportStatus
from .inbox import Inbox from .inbox import Inbox
from .interaction import Favorite, Unfavorite, Boost, Unboost from .interaction import Favorite, Unfavorite, Boost, Unboost

View file

@ -1,11 +1,8 @@
"""group views""" """group views"""
from typing import Optional
from urllib.parse import urlencode
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied from django.db import IntegrityError
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.http import HttpResponseNotFound, HttpResponseBadRequest, HttpResponse from django.http import HttpResponseNotFound, HttpResponseBadRequest
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
@ -16,7 +13,7 @@ from django.db.models.functions import Greatest
from bookwyrm import forms, models from bookwyrm import forms, models
from bookwyrm.suggested_users import suggested_users from bookwyrm.suggested_users import suggested_users
from .helpers import privacy_filter from .helpers import privacy_filter # TODO:
from .helpers import get_user_from_username from .helpers import get_user_from_username
class Group(View): class Group(View):
@ -58,9 +55,9 @@ class UserGroups(View):
return TemplateResponse(request, "user/groups.html", data) return TemplateResponse(request, "user/groups.html", data)
@method_decorator(login_required, name="dispatch") @method_decorator(login_required, name="dispatch")
class FindAndAddUsers(View): class FindUsers(View):
"""find friends to add to your group""" """find friends to add to your group"""
"""this is taken from the Get Started friend finder""" """this is mostly taken from the Get Started friend finder"""
def get(self, request, group_id): def get(self, request, group_id):
"""basic profile info""" """basic profile info"""
@ -103,3 +100,63 @@ def create_group(request):
# add the creator as a group member # add the creator as a group member
models.GroupMember.objects.create(group=group, user=request.user) models.GroupMember.objects.create(group=group, user=request.user)
return redirect(group.local_path) return redirect(group.local_path)
@require_POST
@login_required
def add_member(request):
"""add a member to the group"""
# TODO: if groups become AP values we need something like get_group_from_group_fullname
# group = get_object_or_404(models.Group, id=request.POST.get("group"))
group = models.Group.objects.get(id=request.POST["group"])
if not group:
return HttpResponseBadRequest()
user = get_user_from_username(request.user, request.POST["user"])
if not user:
return HttpResponseBadRequest()
if not group.manager == request.user:
return HttpResponseBadRequest()
try:
models.GroupMember.objects.create(
group=group,
user=user
)
except IntegrityError:
print("no integrity")
pass
# TODO: how do we return and update AJAX data?
return redirect(user.local_path)
@require_POST
@login_required
def remove_member(request):
"""remove a member from the group"""
# TODO: if groups become AP values we need something like get_group_from_group_fullname
# group = get_object_or_404(models.Group, id=request.POST.get("group"))
group = models.Group.objects.get(id=request.POST["group"])
if not group:
return HttpResponseBadRequest()
user = get_user_from_username(request.user, request.POST["user"])
if not user:
return HttpResponseBadRequest()
if not group.manager == request.user:
return HttpResponseBadRequest()
try:
membership = models.GroupMember.objects.get(group=group,user=user)
membership.delete()
except IntegrityError:
print("no integrity")
pass
# TODO: how do we return and update AJAX data?
return redirect(user.local_path)