mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-12-02 06:21:09 +00:00
group membership invitations
- fix display of group information on user and group pages - send, receive, accept and reject invitations
This commit is contained in:
parent
89dea44614
commit
0984972b05
10 changed files with 72 additions and 59 deletions
|
@ -20,6 +20,7 @@
|
||||||
<span class="is-sr-only">Manager</span>
|
<span class="is-sr-only">Manager</span>
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<!-- TODO: change this to a remove_from_group button -->
|
||||||
{% include 'snippets/add_to_group_button.html' with user=member group=group minimal=True %}
|
{% include 'snippets/add_to_group_button.html' with user=member group=group minimal=True %}
|
||||||
{% if member.mutuals %}
|
{% if member.mutuals %}
|
||||||
<p class="help">
|
<p class="help">
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
No users found for {{ query }}
|
No potential members found for "{{ query }}"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
{% load interaction %}
|
{% load interaction %}
|
||||||
|
|
||||||
<div class="columns is-multiline">
|
<div class="columns is-multiline">
|
||||||
{% for group in groups %}
|
{% for membership in memberships %}
|
||||||
|
{% with group=membership.group %}
|
||||||
<div class="column is-one-quarter">
|
<div class="column is-one-quarter">
|
||||||
<div class="card is-stretchable">
|
<div class="card is-stretchable">
|
||||||
<header class="card-header">
|
<header class="card-header">
|
||||||
|
@ -31,5 +32,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endwith %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
<span class="icon icon-comment"></span>
|
<span class="icon icon-comment"></span>
|
||||||
{% elif notification.notification_type == 'REPLY' %}
|
{% elif notification.notification_type == 'REPLY' %}
|
||||||
<span class="icon icon-comments"></span>
|
<span class="icon icon-comments"></span>
|
||||||
{% elif notification.notification_type == 'FOLLOW' or notification.notification_type == 'FOLLOW_REQUEST' %}
|
{% elif notification.notification_type == 'FOLLOW' or notification.notification_type == 'FOLLOW_REQUEST' or notification.notification_type == 'INVITE' or notification.notification_type == 'ACCEPT' %}
|
||||||
<span class="icon icon-local"></span>
|
<span class="icon icon-local"></span>
|
||||||
{% elif notification.notification_type == 'BOOST' %}
|
{% elif notification.notification_type == 'BOOST' %}
|
||||||
<span class="icon icon-boost"></span>
|
<span class="icon icon-boost"></span>
|
||||||
|
@ -122,6 +122,17 @@
|
||||||
{% else %}
|
{% else %}
|
||||||
{% blocktrans with book_path=notification.related_list_item.book.local_path book_title=notification.related_list_item.book.title list_path=notification.related_list_item.book_list.local_path list_name=notification.related_list_item.book_list.name %} suggested adding <em><a href="{{ book_path }}">{{ book_title }}</a></em> to your list "<a href="{{ list_path }}/curate">{{ list_name }}</a>"{% endblocktrans %}
|
{% blocktrans with book_path=notification.related_list_item.book.local_path book_title=notification.related_list_item.book.title list_path=notification.related_list_item.book_list.local_path list_name=notification.related_list_item.book_list.name %} suggested adding <em><a href="{{ book_path }}">{{ book_title }}</a></em> to your list "<a href="{{ list_path }}/curate">{{ list_name }}</a>"{% endblocktrans %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% elif notification.notification_type == 'INVITE' %}
|
||||||
|
{% if notification.related_group %}
|
||||||
|
{% blocktrans with group_name=notification.related_group.name group_path=notification.related_group.local_path %} invited you to join the group <a href="{{ group_path }}">{{ group_name }}</a> {% endblocktrans %}
|
||||||
|
<div class="row shrink">
|
||||||
|
{% include 'snippets/join_invitation_buttons.html' with group=notification.related_group %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% elif notification.notification_type == 'ACCEPT' %}
|
||||||
|
{% if notification.related_group %}
|
||||||
|
{% blocktrans with group_name=notification.related_group.name group_path=notification.related_group.local_path %} accepted an invitation to join your group "<a href="{{ group_path }}">{{ group_name }}</a>"{% endblocktrans %}
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% elif notification.related_import %}
|
{% elif notification.related_import %}
|
||||||
{% url 'import-status' notification.related_import.id as url %}
|
{% url 'import-status' notification.related_import.id as url %}
|
||||||
|
|
|
@ -24,22 +24,22 @@
|
||||||
<span><em>Remote User</em></span>
|
<span><em>Remote User</em></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
<form action="{% url 'uninvite-group-member' %}" method="POST" class="interaction add_{{ user.id }} {% if not group|is_member:user or not group|is_invited:user %}is-hidden{% endif %}" data-id="add_{{ user.id }}">
|
<form action="{% url 'remove-group-member' %}" method="POST" class="interaction add_{{ user.id }} {% if not group|is_member:user and not group|is_invited:user %}is-hidden{% endif %}" data-id="add_{{ user.id }}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="group" value="{{ group.id }}">
|
<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 group|is_invited:user %}
|
{% if not group|is_member:user %}
|
||||||
<button class="button is-small is-danger is-light" type="submit">
|
<button class="button is-small is-danger is-light" type="submit">
|
||||||
{% trans "Undo Invitation" %}
|
{% trans "Undo Invitation" %}
|
||||||
</button>
|
</button>
|
||||||
{% 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 %}Remove @{{ username }}{% endblocktrans %}
|
{% blocktrans with username=user.localname %}Remove @{{ username }}{% endblocktrans %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% trans "Remove" %}
|
{% trans "Remove" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
16
bookwyrm/templates/snippets/join_invitation_buttons.html
Normal file
16
bookwyrm/templates/snippets/join_invitation_buttons.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{% load i18n %}
|
||||||
|
{% load bookwyrm_group_tags %}
|
||||||
|
{% if group|is_invited:request.user %}
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<form action="/accept-group-invitation/" method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="group" value="{{ group.id }}">
|
||||||
|
<button class="button is-link is-small" type="submit">{% trans "Accept" %}</button>
|
||||||
|
</form>
|
||||||
|
<form action="/reject-group-invitation/" method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="group" value="{{ group.id }}">
|
||||||
|
<button class="button is-danger is-light is-small" type="submit" class="warning">{% trans "Delete" %}</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
{% block panel %}
|
{% block panel %}
|
||||||
<section class="block">
|
<section class="block">
|
||||||
<form name="create-group" method="post" action="{% url 'user-groups' request.user.name %}" class="box is-hidden" id="create_group">
|
<form name="create-group" method="post" action="{% url 'user-groups' request.user.username %}" class="box is-hidden" id="create_group">
|
||||||
<header class="columns">
|
<header class="columns">
|
||||||
<h3 class="title column">{% trans "Create group" %}</h3>
|
<h3 class="title column">{% trans "Create group" %}</h3>
|
||||||
<div class="column is-narrow">
|
<div class="column is-narrow">
|
||||||
|
@ -34,9 +34,9 @@
|
||||||
{% include 'groups/form.html' %}
|
{% include 'groups/form.html' %}
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% include 'groups/user_groups.html' %}
|
{% include 'groups/user_groups.html' with memberships=memberships %}
|
||||||
</section>
|
</section>
|
||||||
<div>
|
<div>
|
||||||
{% include 'snippets/pagination.html' with page=user_groups path=path %}
|
{% include 'snippets/pagination.html' with page=user.memberships path=path %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
{% load utilities %}
|
{% load utilities %}
|
||||||
{% load markdown %}
|
{% load markdown %}
|
||||||
{% load layout %}
|
{% load layout %}
|
||||||
|
{% load bookwyrm_group_tags %}
|
||||||
|
|
||||||
{% block title %}{{ user.display_name }}{% endblock %}
|
{% block title %}{{ user.display_name }}{% endblock %}
|
||||||
|
|
||||||
|
@ -75,7 +76,7 @@
|
||||||
<a href="{{ url }}">{% trans "Lists" %}</a>
|
<a href="{{ url }}">{% trans "Lists" %}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if is_self or user.bookwyrm_groups %}
|
{% if is_self or user|has_groups %}
|
||||||
{% url 'user-groups' user|username as url %}
|
{% url 'user-groups' user|username as url %}
|
||||||
<li{% if url in request.path %} class="is-active"{% endif %}>
|
<li{% if url in request.path %} class="is-active"{% endif %}>
|
||||||
<a href="{{ url }}">{% trans "Groups" %}</a>
|
<a href="{{ url }}">{% trans "Groups" %}</a>
|
||||||
|
|
|
@ -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 BookwyrmGroup, UserGroups, FindUsers, invite_member, remove_member, uninvite_member, accept_membership, reject_membership
|
from .group import BookwyrmGroup, UserGroups, FindUsers, invite_member, remove_member, accept_membership, reject_membership
|
||||||
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
|
||||||
|
|
|
@ -57,11 +57,11 @@ class UserGroups(View):
|
||||||
def get(self, request, username):
|
def get(self, request, username):
|
||||||
"""display a group"""
|
"""display a group"""
|
||||||
user = get_user_from_username(request.user, username)
|
user = get_user_from_username(request.user, username)
|
||||||
groups = user.bookwyrmgroup_set.all() # follow the relationship backwards, nice
|
memberships = models.BookwyrmGroupMember.objects.filter(user=user).all()
|
||||||
paginated = Paginator(groups, 12)
|
paginated = Paginator(memberships, 12)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"groups": paginated.get_page(request.GET.get("page")),
|
"memberships": paginated.get_page(request.GET.get("page")),
|
||||||
"is_self": request.user.id == user.id,
|
"is_self": request.user.id == user.id,
|
||||||
"user": user,
|
"user": user,
|
||||||
"group_form": forms.GroupForm(),
|
"group_form": forms.GroupForm(),
|
||||||
|
@ -89,8 +89,10 @@ class FindUsers(View):
|
||||||
def get(self, request, group_id):
|
def get(self, request, group_id):
|
||||||
"""basic profile info"""
|
"""basic profile info"""
|
||||||
query = request.GET.get("query")
|
query = request.GET.get("query")
|
||||||
|
group = models.BookwyrmGroup.objects.get(id=group_id)
|
||||||
user_results = (
|
user_results = (
|
||||||
models.User.viewer_aware_objects(request.user)
|
models.User.viewer_aware_objects(request.user)
|
||||||
|
.exclude(memberships__in=group.memberships.all()) # don't suggest users who are already members
|
||||||
.annotate(
|
.annotate(
|
||||||
similarity=Greatest(
|
similarity=Greatest(
|
||||||
TrigramSimilarity("username", query),
|
TrigramSimilarity("username", query),
|
||||||
|
@ -149,7 +151,7 @@ def invite_member(request):
|
||||||
|
|
||||||
@require_POST
|
@require_POST
|
||||||
@login_required
|
@login_required
|
||||||
def uninvite_member(request):
|
def remove_member(request):
|
||||||
"""invite a member to the group"""
|
"""invite a member to the group"""
|
||||||
|
|
||||||
group = get_object_or_404(models.BookwyrmGroup, id=request.POST.get("group"))
|
group = get_object_or_404(models.BookwyrmGroup, id=request.POST.get("group"))
|
||||||
|
@ -160,51 +162,31 @@ def uninvite_member(request):
|
||||||
if not user:
|
if not user:
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
if not group.user == request.user:
|
is_member = models.BookwyrmGroupMember.objects.filter(group=group,user=user).exists()
|
||||||
return HttpResponseBadRequest()
|
is_invited = models.GroupMemberInvitation.objects.filter(group=group,user=user).exists()
|
||||||
|
|
||||||
try:
|
if is_invited:
|
||||||
invitation = models.GroupMemberInvitation.objects.get(
|
try:
|
||||||
user=user,
|
invitation = models.GroupMemberInvitation.objects.get(
|
||||||
group=group
|
user=user,
|
||||||
)
|
group=group
|
||||||
|
)
|
||||||
|
|
||||||
invitation.reject()
|
invitation.reject()
|
||||||
|
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return redirect(user.local_path)
|
if is_member:
|
||||||
|
|
||||||
@require_POST
|
try:
|
||||||
@login_required
|
membership = models.BookwyrmGroupMember.objects.get(group=group,user=user)
|
||||||
def remove_member(request):
|
membership.delete()
|
||||||
"""remove a member from the group"""
|
|
||||||
|
|
||||||
# TODO: send notification to user telling them they have been removed
|
except IntegrityError:
|
||||||
# TODO: remove yourself from a group!!!! (except owner)
|
pass
|
||||||
# FUTURE TODO: transfer ownership of group
|
|
||||||
# THIS LOGIC SHOULD BE IN MODEL
|
|
||||||
|
|
||||||
# TODO: if groups become AP values we need something like get_group_from_group_fullname
|
# TODO: should send notification to all members including the now ex-member that they have been removed.
|
||||||
# group = get_object_or_404(models.Group, id=request.POST.get("group")) # NOTE: does this not work?
|
|
||||||
group = models.BookwyrmGroup.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.user == request.user:
|
|
||||||
return HttpResponseBadRequest()
|
|
||||||
|
|
||||||
try:
|
|
||||||
membership = models.BookwyrmGroupMember.objects.get(group=group,user=user) # BUG: wrong
|
|
||||||
membership.delete()
|
|
||||||
|
|
||||||
except IntegrityError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return redirect(user.local_path)
|
return redirect(user.local_path)
|
||||||
|
|
||||||
|
@ -227,7 +209,7 @@ def accept_membership(request):
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return redirect(request.user.local_path)
|
return redirect(group.local_path)
|
||||||
|
|
||||||
@require_POST
|
@require_POST
|
||||||
@login_required
|
@login_required
|
||||||
|
|
Loading…
Reference in a new issue