2021-09-23 21:49:25 +00:00
|
|
|
"""group views"""
|
2021-09-28 08:53:11 +00:00
|
|
|
from django.apps import apps
|
2021-09-23 21:49:25 +00:00
|
|
|
from django.contrib.auth.decorators import login_required
|
2021-09-25 07:34:44 +00:00
|
|
|
from django.db import IntegrityError
|
2021-09-23 21:49:25 +00:00
|
|
|
from django.core.paginator import Paginator
|
2021-10-05 07:04:47 +00:00
|
|
|
from django.http import HttpResponseBadRequest
|
2021-09-23 21:49:25 +00:00
|
|
|
from django.shortcuts import get_object_or_404, redirect
|
|
|
|
from django.template.response import TemplateResponse
|
|
|
|
from django.utils.decorators import method_decorator
|
|
|
|
from django.views import View
|
|
|
|
from django.views.decorators.http import require_POST
|
2021-09-25 01:14:04 +00:00
|
|
|
from django.contrib.postgres.search import TrigramSimilarity
|
|
|
|
from django.db.models.functions import Greatest
|
2021-09-23 21:49:25 +00:00
|
|
|
|
|
|
|
from bookwyrm import forms, models
|
2021-09-25 01:14:04 +00:00
|
|
|
from bookwyrm.suggested_users import suggested_users
|
2021-09-23 21:49:25 +00:00
|
|
|
from .helpers import get_user_from_username
|
2021-10-04 10:31:28 +00:00
|
|
|
|
2021-10-05 07:04:47 +00:00
|
|
|
# pylint: disable=no-self-use
|
2021-10-02 06:52:34 +00:00
|
|
|
class Group(View):
|
2021-09-24 04:12:36 +00:00
|
|
|
"""group page"""
|
|
|
|
|
2021-09-24 10:34:11 +00:00
|
|
|
def get(self, request, group_id):
|
2021-09-24 04:12:36 +00:00
|
|
|
"""display a group"""
|
|
|
|
|
2021-10-02 06:52:34 +00:00
|
|
|
group = get_object_or_404(models.Group, id=group_id)
|
2021-10-03 01:02:57 +00:00
|
|
|
group.raise_visible_to_user(request.user)
|
2021-10-09 11:15:24 +00:00
|
|
|
lists = (
|
|
|
|
models.List.privacy_filter(request.user)
|
|
|
|
.filter(group=group)
|
|
|
|
.order_by("-updated_date")
|
|
|
|
)
|
2021-09-27 07:51:18 +00:00
|
|
|
|
2021-09-24 04:12:36 +00:00
|
|
|
data = {
|
2021-09-24 10:34:11 +00:00
|
|
|
"group": group,
|
2021-09-26 05:56:02 +00:00
|
|
|
"lists": lists,
|
2021-09-27 06:49:56 +00:00
|
|
|
"group_form": forms.GroupForm(instance=group),
|
2021-09-24 04:12:36 +00:00
|
|
|
"path": "/group",
|
|
|
|
}
|
|
|
|
return TemplateResponse(request, "groups/group.html", data)
|
|
|
|
|
2021-09-27 05:34:14 +00:00
|
|
|
@method_decorator(login_required, name="dispatch")
|
|
|
|
def post(self, request, group_id):
|
|
|
|
"""edit a group"""
|
2021-10-02 06:52:34 +00:00
|
|
|
user_group = get_object_or_404(models.Group, id=group_id)
|
2021-09-27 05:34:14 +00:00
|
|
|
form = forms.GroupForm(request.POST, instance=user_group)
|
|
|
|
if not form.is_valid():
|
|
|
|
return redirect("group", user_group.id)
|
|
|
|
user_group = form.save()
|
2021-10-22 09:23:45 +00:00
|
|
|
|
|
|
|
# let the other members know something about the group changed
|
|
|
|
memberships = models.GroupMember.objects.filter(group=user_group)
|
|
|
|
model = apps.get_model("bookwyrm.Notification", require_ready=True)
|
|
|
|
for field in form.changed_data:
|
|
|
|
notification_type = (
|
|
|
|
"GROUP_PRIVACY"
|
|
|
|
if field == "privacy"
|
|
|
|
else "GROUP_NAME"
|
|
|
|
if field == "name"
|
|
|
|
else "GROUP_DESCRIPTION"
|
|
|
|
if field == "description"
|
|
|
|
else None
|
|
|
|
)
|
|
|
|
if notification_type:
|
|
|
|
for membership in memberships:
|
|
|
|
member = membership.user
|
|
|
|
if member != request.user:
|
|
|
|
model.objects.create(
|
|
|
|
user=member,
|
|
|
|
related_user=request.user,
|
|
|
|
related_group=user_group,
|
|
|
|
notification_type=notification_type,
|
|
|
|
)
|
|
|
|
|
2021-10-04 10:31:28 +00:00
|
|
|
return redirect("group", user_group.id)
|
|
|
|
|
2021-09-27 05:34:14 +00:00
|
|
|
|
2021-09-23 21:49:25 +00:00
|
|
|
@method_decorator(login_required, name="dispatch")
|
|
|
|
class UserGroups(View):
|
|
|
|
"""a user's groups page"""
|
|
|
|
|
|
|
|
def get(self, request, username):
|
|
|
|
"""display a group"""
|
|
|
|
user = get_user_from_username(request.user, username)
|
2021-10-09 11:15:24 +00:00
|
|
|
groups = (
|
|
|
|
models.Group.privacy_filter(request.user)
|
|
|
|
.filter(memberships__user=user)
|
|
|
|
.order_by("-updated_date")
|
|
|
|
)
|
2021-10-09 05:11:11 +00:00
|
|
|
paginated = Paginator(groups, 12)
|
2021-09-23 21:49:25 +00:00
|
|
|
|
|
|
|
data = {
|
2021-10-09 05:11:11 +00:00
|
|
|
"groups": paginated.get_page(request.GET.get("page")),
|
2021-10-02 00:10:37 +00:00
|
|
|
"is_self": request.user.id == user.id,
|
2021-09-23 21:49:25 +00:00
|
|
|
"user": user,
|
2021-09-24 04:12:36 +00:00
|
|
|
"group_form": forms.GroupForm(),
|
2021-09-24 10:34:11 +00:00
|
|
|
"path": user.local_path + "/group",
|
2021-09-23 21:49:25 +00:00
|
|
|
}
|
|
|
|
return TemplateResponse(request, "user/groups.html", data)
|
2021-09-24 10:34:11 +00:00
|
|
|
|
2021-09-27 05:34:14 +00:00
|
|
|
@method_decorator(login_required, name="dispatch")
|
|
|
|
# pylint: disable=unused-argument
|
|
|
|
def post(self, request, username):
|
|
|
|
"""create a user group"""
|
|
|
|
form = forms.GroupForm(request.POST)
|
|
|
|
if not form.is_valid():
|
2021-10-16 06:35:36 +00:00
|
|
|
return redirect(request.user.local_path + "/groups")
|
2021-09-27 05:34:14 +00:00
|
|
|
group = form.save()
|
|
|
|
# add the creator as a group member
|
2021-10-02 06:52:34 +00:00
|
|
|
models.GroupMember.objects.create(group=group, user=request.user)
|
2021-09-27 05:34:14 +00:00
|
|
|
return redirect("group", group.id)
|
|
|
|
|
2021-10-04 10:31:28 +00:00
|
|
|
|
2021-09-25 01:14:04 +00:00
|
|
|
@method_decorator(login_required, name="dispatch")
|
2021-09-25 07:34:44 +00:00
|
|
|
class FindUsers(View):
|
2021-09-25 01:14:04 +00:00
|
|
|
"""find friends to add to your group"""
|
2021-10-04 10:31:28 +00:00
|
|
|
|
2021-10-04 11:22:00 +00:00
|
|
|
# this is mostly borrowed from the Get Started friend finder
|
2021-09-25 01:14:04 +00:00
|
|
|
|
|
|
|
def get(self, request, group_id):
|
|
|
|
"""basic profile info"""
|
2021-10-16 05:39:50 +00:00
|
|
|
user_query = request.GET.get("user_query")
|
2021-10-05 07:04:47 +00:00
|
|
|
group = get_object_or_404(models.Group, id=group_id)
|
|
|
|
|
|
|
|
if not group:
|
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
|
|
|
if not group.user == request.user:
|
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
2021-09-25 01:14:04 +00:00
|
|
|
user_results = (
|
|
|
|
models.User.viewer_aware_objects(request.user)
|
2021-10-04 10:31:28 +00:00
|
|
|
.exclude(
|
|
|
|
memberships__in=group.memberships.all()
|
|
|
|
) # don't suggest users who are already members
|
2021-09-25 01:14:04 +00:00
|
|
|
.annotate(
|
|
|
|
similarity=Greatest(
|
2021-10-16 05:39:50 +00:00
|
|
|
TrigramSimilarity("username", user_query),
|
|
|
|
TrigramSimilarity("localname", user_query),
|
2021-09-25 01:14:04 +00:00
|
|
|
)
|
|
|
|
)
|
2021-10-04 10:31:28 +00:00
|
|
|
.filter(similarity__gt=0.5, local=True)
|
2021-09-25 01:14:04 +00:00
|
|
|
.order_by("-similarity")[:5]
|
|
|
|
)
|
|
|
|
data = {"no_results": not user_results}
|
|
|
|
|
|
|
|
if user_results.count() < 5:
|
2021-10-04 10:25:38 +00:00
|
|
|
user_results = list(user_results) + suggested_users.get_suggestions(
|
2021-10-04 10:31:28 +00:00
|
|
|
request.user, local=True
|
2021-09-25 01:14:04 +00:00
|
|
|
)
|
|
|
|
|
2021-10-02 00:10:37 +00:00
|
|
|
data = {
|
2021-10-04 10:31:28 +00:00
|
|
|
"suggested_users": user_results,
|
|
|
|
"group": group,
|
2021-10-16 05:39:50 +00:00
|
|
|
"group_form": forms.GroupForm(instance=group),
|
|
|
|
"user_query": user_query,
|
2021-10-04 10:31:28 +00:00
|
|
|
"requestor_is_manager": request.user == group.user,
|
2021-10-02 00:10:37 +00:00
|
|
|
}
|
2021-09-25 01:14:04 +00:00
|
|
|
return TemplateResponse(request, "groups/find_users.html", data)
|
|
|
|
|
2021-10-04 10:31:28 +00:00
|
|
|
|
2021-10-16 06:35:36 +00:00
|
|
|
@require_POST
|
|
|
|
@login_required
|
|
|
|
def delete_group(request, group_id):
|
|
|
|
"""delete a group"""
|
|
|
|
group = get_object_or_404(models.Group, id=group_id)
|
|
|
|
|
|
|
|
# only the owner can delete a group
|
|
|
|
group.raise_not_deletable(request.user)
|
|
|
|
|
|
|
|
# deal with any group lists
|
|
|
|
models.List.objects.filter(group=group).update(curation="closed", group=None)
|
|
|
|
|
|
|
|
group.delete()
|
|
|
|
return redirect(request.user.local_path + "/groups")
|
|
|
|
|
|
|
|
|
2021-09-25 07:34:44 +00:00
|
|
|
@require_POST
|
|
|
|
@login_required
|
2021-10-02 00:10:37 +00:00
|
|
|
def invite_member(request):
|
|
|
|
"""invite a member to the group"""
|
2021-09-25 07:34:44 +00:00
|
|
|
|
2021-10-02 06:52:34 +00:00
|
|
|
group = get_object_or_404(models.Group, id=request.POST.get("group"))
|
2021-09-25 07:34:44 +00:00
|
|
|
if not group:
|
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
|
|
|
user = get_user_from_username(request.user, request.POST["user"])
|
|
|
|
if not user:
|
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
2021-09-27 05:34:14 +00:00
|
|
|
if not group.user == request.user:
|
2021-09-25 07:34:44 +00:00
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
|
|
|
try:
|
2021-10-04 10:31:28 +00:00
|
|
|
models.GroupMemberInvitation.objects.create(user=user, group=group)
|
2021-09-25 07:34:44 +00:00
|
|
|
|
|
|
|
except IntegrityError:
|
|
|
|
pass
|
|
|
|
|
2021-10-02 00:10:37 +00:00
|
|
|
return redirect(user.local_path)
|
|
|
|
|
2021-10-04 10:31:28 +00:00
|
|
|
|
2021-10-02 00:10:37 +00:00
|
|
|
@require_POST
|
|
|
|
@login_required
|
2021-10-02 02:30:48 +00:00
|
|
|
def remove_member(request):
|
2021-10-02 05:48:55 +00:00
|
|
|
"""remove a member from the group"""
|
2021-10-02 00:10:37 +00:00
|
|
|
|
2021-10-02 06:52:34 +00:00
|
|
|
group = get_object_or_404(models.Group, id=request.POST.get("group"))
|
2021-10-02 00:10:37 +00:00
|
|
|
if not group:
|
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
|
|
|
user = get_user_from_username(request.user, request.POST["user"])
|
|
|
|
if not user:
|
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
2021-10-09 11:11:46 +00:00
|
|
|
# you can't be removed from your own group
|
2021-10-09 11:15:24 +00:00
|
|
|
if request.POST["user"] == group.user:
|
2021-10-09 11:11:46 +00:00
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
2021-10-04 10:31:28 +00:00
|
|
|
is_member = models.GroupMember.objects.filter(group=group, user=user).exists()
|
|
|
|
is_invited = models.GroupMemberInvitation.objects.filter(
|
|
|
|
group=group, user=user
|
|
|
|
).exists()
|
2021-09-25 07:34:44 +00:00
|
|
|
|
2021-10-02 02:30:48 +00:00
|
|
|
if is_invited:
|
|
|
|
try:
|
|
|
|
invitation = models.GroupMemberInvitation.objects.get(
|
2021-10-04 10:31:28 +00:00
|
|
|
user=user, group=group
|
|
|
|
)
|
2021-09-25 07:34:44 +00:00
|
|
|
|
2021-10-02 02:30:48 +00:00
|
|
|
invitation.reject()
|
2021-09-28 08:53:11 +00:00
|
|
|
|
2021-10-02 02:30:48 +00:00
|
|
|
except IntegrityError:
|
|
|
|
pass
|
2021-09-25 07:34:44 +00:00
|
|
|
|
2021-10-02 02:30:48 +00:00
|
|
|
if is_member:
|
2021-09-25 07:34:44 +00:00
|
|
|
|
2021-10-02 02:30:48 +00:00
|
|
|
try:
|
2021-10-09 11:11:46 +00:00
|
|
|
models.List.remove_from_group(group.user, user)
|
|
|
|
models.GroupMember.remove(group.user, user)
|
2021-10-02 22:16:19 +00:00
|
|
|
|
2021-10-02 02:30:48 +00:00
|
|
|
except IntegrityError:
|
|
|
|
pass
|
2021-09-25 07:34:44 +00:00
|
|
|
|
2021-10-02 06:52:34 +00:00
|
|
|
memberships = models.GroupMember.objects.filter(group=group)
|
2021-10-02 04:41:23 +00:00
|
|
|
model = apps.get_model("bookwyrm.Notification", require_ready=True)
|
2021-10-02 11:24:26 +00:00
|
|
|
notification_type = "LEAVE" if user == request.user else "REMOVE"
|
2021-10-02 05:48:55 +00:00
|
|
|
# let the other members know about it
|
2021-10-02 04:41:23 +00:00
|
|
|
for membership in memberships:
|
2021-10-04 10:31:28 +00:00
|
|
|
member = membership.user
|
2021-10-02 04:41:23 +00:00
|
|
|
if member != request.user:
|
|
|
|
model.objects.create(
|
|
|
|
user=member,
|
2021-10-02 05:48:55 +00:00
|
|
|
related_user=user,
|
|
|
|
related_group=group,
|
|
|
|
notification_type=notification_type,
|
2021-10-02 04:41:23 +00:00
|
|
|
)
|
2021-09-25 07:34:44 +00:00
|
|
|
|
2021-10-02 05:48:55 +00:00
|
|
|
# let the user (now ex-member) know as well, if they were removed
|
|
|
|
if notification_type == "REMOVE":
|
|
|
|
model.objects.create(
|
|
|
|
user=user,
|
|
|
|
related_group=group,
|
|
|
|
notification_type=notification_type,
|
|
|
|
)
|
|
|
|
|
2021-10-16 05:39:50 +00:00
|
|
|
return redirect(group.local_path)
|
2021-10-02 00:10:37 +00:00
|
|
|
|
2021-10-04 10:31:28 +00:00
|
|
|
|
2021-10-02 00:10:37 +00:00
|
|
|
@require_POST
|
|
|
|
@login_required
|
|
|
|
def accept_membership(request):
|
|
|
|
"""accept an invitation to join a group"""
|
|
|
|
|
2021-10-02 06:52:34 +00:00
|
|
|
group = models.Group.objects.get(id=request.POST["group"])
|
2021-10-02 00:10:37 +00:00
|
|
|
if not group:
|
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
2021-10-04 10:31:28 +00:00
|
|
|
invite = models.GroupMemberInvitation.objects.get(group=group, user=request.user)
|
2021-10-02 00:10:37 +00:00
|
|
|
if not invite:
|
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
|
|
|
try:
|
|
|
|
invite.accept()
|
|
|
|
|
|
|
|
except IntegrityError:
|
|
|
|
pass
|
|
|
|
|
2021-10-02 02:30:48 +00:00
|
|
|
return redirect(group.local_path)
|
2021-10-02 00:10:37 +00:00
|
|
|
|
2021-10-04 10:31:28 +00:00
|
|
|
|
2021-10-02 00:10:37 +00:00
|
|
|
@require_POST
|
|
|
|
@login_required
|
|
|
|
def reject_membership(request):
|
|
|
|
"""reject an invitation to join a group"""
|
|
|
|
|
2021-10-02 06:52:34 +00:00
|
|
|
group = models.Group.objects.get(id=request.POST["group"])
|
2021-10-02 00:10:37 +00:00
|
|
|
if not group:
|
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
2021-10-04 10:31:28 +00:00
|
|
|
invite = models.GroupMemberInvitation.objects.get(group=group, user=request.user)
|
2021-10-02 00:10:37 +00:00
|
|
|
if not invite:
|
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
|
|
|
try:
|
|
|
|
invite.reject()
|
|
|
|
|
|
|
|
except IntegrityError:
|
|
|
|
pass
|
|
|
|
|
2021-10-04 10:31:28 +00:00
|
|
|
return redirect(request.user.local_path)
|