bookwyrm/bookwyrm/views/group.py

307 lines
10 KiB
Python
Raw Permalink Normal View History

2021-09-23 21:49:25 +00:00
"""group views"""
from django.apps import apps
2021-09-23 21:49:25 +00:00
from django.contrib.auth.decorators import login_required
2022-08-05 16:12:48 +00:00
from django.db import IntegrityError, transaction
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
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
from bookwyrm.models import NotificationType
from bookwyrm.suggested_users import suggested_users
2022-03-02 09:12:32 +00:00
from .helpers import get_user_from_username, maybe_redirect_local_path
2021-10-04 10:31:28 +00:00
2021-10-05 07:04:47 +00:00
# pylint: disable=no-self-use
class Group(View):
"""group page"""
2022-03-12 05:19:20 +00:00
# pylint: disable=unused-argument
def get(self, request, group_id, slug=None):
"""display a group"""
group = get_object_or_404(models.Group, id=group_id)
group.raise_visible_to_user(request.user)
2022-03-02 09:12:32 +00:00
2022-03-12 06:28:05 +00:00
if redirect_local_path := maybe_redirect_local_path(request, group):
return redirect_local_path
2022-03-02 09:12:32 +00:00
lists = (
models.List.privacy_filter(request.user)
.filter(group=group)
.order_by("-updated_date")
)
data = {
2021-09-24 10:34:11 +00:00
"group": group,
"lists": lists,
2022-01-01 23:19:57 +00:00
"group_form": forms.GroupForm(instance=group, auto_id="group_form_id_%s"),
"list_form": forms.ListForm(),
"path": "/group",
}
return TemplateResponse(request, "groups/group.html", data)
@method_decorator(login_required, name="dispatch")
def post(self, request, group_id):
"""edit a group"""
user_group = get_object_or_404(models.Group, id=group_id)
form = forms.GroupForm(request.POST, instance=user_group)
if not form.is_valid():
return redirect("group", user_group.id)
user_group = form.save(request)
# 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 = (
NotificationType.GROUP_PRIVACY
if field == "privacy"
else NotificationType.GROUP_NAME
if field == "name"
else NotificationType.GROUP_DESCRIPTION
if field == "description"
else None
)
if notification_type:
for membership in memberships:
member = membership.user
if member != request.user:
model.notify(
member,
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-23 21:49:25 +00:00
@method_decorator(login_required, name="dispatch")
class UserGroups(View):
"""a user's groups page"""
2022-03-12 05:19:20 +00:00
# pylint: disable=unused-argument
def get(self, request, username, slug=None):
2021-09-23 21:49:25 +00:00
"""display a group"""
user = get_user_from_username(request.user, username)
groups = (
models.Group.privacy_filter(request.user)
.filter(memberships__user=user)
.order_by("-updated_date")
)
paginated = Paginator(groups, 12)
2021-09-23 21:49:25 +00:00
data = {
"groups": paginated.get_page(request.GET.get("page")),
"is_self": request.user.id == user.id,
2021-09-23 21:49:25 +00:00
"user": user,
"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
@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():
return redirect(request.user.local_path + "/groups")
2022-08-05 16:12:48 +00:00
with transaction.atomic():
group = form.save(request)
2022-08-05 16:12:48 +00:00
# add the creator as a group member
models.GroupMember.objects.create(group=group, user=request.user)
return redirect("group", group.id)
2021-10-04 10:31:28 +00:00
@method_decorator(login_required, name="dispatch")
2021-09-25 07:34:44 +00:00
class FindUsers(View):
"""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
def get(self, request, group_id):
"""Search for a user to add the a group, or load suggested users cache"""
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)
# only users who can edit can add users
2022-08-05 16:12:48 +00:00
group.raise_not_editable(request.user)
lists = (
models.List.privacy_filter(request.user)
.filter(group=group)
.order_by("-updated_date")
)
2021-10-05 07:04:47 +00:00
if not group:
return HttpResponseBadRequest()
if not group.user == request.user:
return HttpResponseBadRequest()
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
.annotate(
similarity=Greatest(
TrigramSimilarity("username", user_query),
TrigramSimilarity("localname", user_query),
)
)
2021-10-04 10:31:28 +00:00
.filter(similarity__gt=0.5, local=True)
.order_by("-similarity")[:5]
)
no_results = not user_results
if user_results.count() < 5:
user_results = list(user_results) + list(
suggested_users.get_suggestions(request.user, local=True)
)
data = {
2021-10-04 10:31:28 +00:00
"suggested_users": user_results,
"no_results": no_results,
2021-10-04 10:31:28 +00:00
"group": group,
"lists": lists,
2022-01-01 23:19:57 +00:00
"group_form": forms.GroupForm(instance=group, auto_id="group_form_id_%s"),
"list_form": forms.ListForm(),
"user_query": user_query,
2021-10-04 10:31:28 +00:00
"requestor_is_manager": request.user == group.user,
}
return TemplateResponse(request, "groups/find_users.html", data)
2021-10-04 10:31:28 +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)
2022-08-05 16:12:48 +00:00
with transaction.atomic():
# deal with any group lists
models.List.objects.filter(group=group).update(curation="closed", group=None)
2022-08-05 16:12:48 +00:00
group.delete()
return redirect(request.user.local_path + "/groups")
2021-09-25 07:34:44 +00:00
@require_POST
@login_required
def invite_member(request):
"""invite a member to the group"""
group = get_object_or_404(models.Group, id=request.POST.get("group"))
2021-09-25 07:34:44 +00:00
user = get_user_from_username(request.user, request.POST["user"])
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
return redirect(user.local_path)
2021-10-04 10:31:28 +00:00
@require_POST
@login_required
def remove_member(request):
"""remove a member from the group"""
group = get_object_or_404(models.Group, id=request.POST.get("group"))
user = get_user_from_username(request.user, request.POST["user"])
# you can't be removed from your own group
2021-12-10 19:21:11 +00:00
if user == group.user:
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
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
invitation.reject()
except IntegrityError:
pass
2021-09-25 07:34:44 +00:00
if is_member:
try:
models.List.remove_from_group(group.user, user)
models.GroupMember.remove(group.user, user)
except IntegrityError:
pass
2021-09-25 07:34:44 +00:00
memberships = models.GroupMember.objects.filter(group=group)
model = apps.get_model("bookwyrm.Notification", require_ready=True)
notification_type = (
NotificationType.LEAVE if user == request.user else NotificationType.REMOVE
)
# let the other members know about it
for membership in memberships:
2021-10-04 10:31:28 +00:00
member = membership.user
if member != request.user:
model.notify(
member,
user,
related_group=group,
notification_type=notification_type,
)
2021-09-25 07:34:44 +00:00
# let the user (now ex-member) know as well, if they were removed
if notification_type == NotificationType.REMOVE:
model.notify(
user, None, related_group=group, notification_type=notification_type
)
return redirect(group.local_path)
2021-10-04 10:31:28 +00:00
@require_POST
@login_required
def accept_membership(request):
"""accept an invitation to join a group"""
group = get_object_or_404(models.Group, id=request.POST.get("group"))
invite = get_object_or_404(
models.GroupMemberInvitation, group=group, user=request.user
)
try:
invite.accept()
except IntegrityError:
pass
return redirect(group.local_path)
2021-10-04 10:31:28 +00:00
@require_POST
@login_required
def reject_membership(request):
"""reject an invitation to join a group"""
group = get_object_or_404(models.Group, id=request.POST.get("group"))
invite = get_object_or_404(
models.GroupMemberInvitation, group=group, user=request.user
)
invite.reject()
2021-10-04 10:31:28 +00:00
return redirect(request.user.local_path)