Merge pull request #1561 from hughrun/group-privacy

Fix Group privacy
This commit is contained in:
Mouse Reeve 2021-10-22 10:38:43 -07:00 committed by GitHub
commit c59abdc89a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 176 additions and 16 deletions

View file

@ -0,0 +1,93 @@
# Generated by Django 3.2.5 on 2021-10-22 08:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0111_merge_0107_auto_20211016_0639_0110_auto_20211015_1734"),
]
operations = [
migrations.RemoveConstraint(
model_name="notification",
name="notification_type_valid",
),
migrations.AlterField(
model_name="notification",
name="notification_type",
field=models.CharField(
choices=[
("FAVORITE", "Favorite"),
("REPLY", "Reply"),
("MENTION", "Mention"),
("TAG", "Tag"),
("FOLLOW", "Follow"),
("FOLLOW_REQUEST", "Follow Request"),
("BOOST", "Boost"),
("IMPORT", "Import"),
("ADD", "Add"),
("REPORT", "Report"),
("INVITE", "Invite"),
("ACCEPT", "Accept"),
("JOIN", "Join"),
("LEAVE", "Leave"),
("REMOVE", "Remove"),
("GROUP_PRIVACY", "Group Privacy"),
("GROUP_NAME", "Group Name"),
("GROUP_DESCRIPTION", "Group Description"),
],
max_length=255,
),
),
migrations.AlterField(
model_name="user",
name="preferred_language",
field=models.CharField(
blank=True,
choices=[
("en-us", "English"),
("de-de", "Deutsch (German)"),
("es-es", "Español (Spanish)"),
("fr-fr", "Français (French)"),
("pt-br", "Português - Brasil (Brazilian Portuguese)"),
("zh-hans", "简体中文 (Simplified Chinese)"),
("zh-hant", "繁體中文 (Traditional Chinese)"),
],
max_length=255,
null=True,
),
),
migrations.AddConstraint(
model_name="notification",
constraint=models.CheckConstraint(
check=models.Q(
(
"notification_type__in",
[
"FAVORITE",
"REPLY",
"MENTION",
"TAG",
"FOLLOW",
"FOLLOW_REQUEST",
"BOOST",
"IMPORT",
"ADD",
"REPORT",
"INVITE",
"ACCEPT",
"JOIN",
"LEAVE",
"REMOVE",
"GROUP_PRIVACY",
"GROUP_NAME",
"GROUP_DESCRIPTION",
],
)
),
name="notification_type_valid",
),
),
]

View file

@ -23,7 +23,7 @@ class Group(BookWyrmModel):
@classmethod @classmethod
def followers_filter(cls, queryset, viewer): def followers_filter(cls, queryset, viewer):
"""Override filter for "followers" privacy level to allow non-following """Override filter for "followers" privacy level to allow non-following
group members to see the existence of groups and group lists""" group members to see the existence of group-curated lists"""
return queryset.exclude( return queryset.exclude(
~Q( # user is not a group member ~Q( # user is not a group member

View file

@ -7,7 +7,7 @@ from . import Boost, Favorite, ImportJob, Report, Status, User
# pylint: disable=line-too-long # pylint: disable=line-too-long
NotificationType = models.TextChoices( NotificationType = models.TextChoices(
"NotificationType", "NotificationType",
"FAVORITE REPLY MENTION TAG FOLLOW FOLLOW_REQUEST BOOST IMPORT ADD REPORT INVITE ACCEPT JOIN LEAVE REMOVE", "FAVORITE REPLY MENTION TAG FOLLOW FOLLOW_REQUEST BOOST IMPORT ADD REPORT INVITE ACCEPT JOIN LEAVE REMOVE GROUP_PRIVACY GROUP_NAME GROUP_DESCRIPTION",
) )

View file

@ -4,7 +4,6 @@
<div class="columns"> <div class="columns">
<div class="column is-two-thirds"> <div class="column is-two-thirds">
<input type="hidden" name="user" value="{{ request.user.id }}" /> <input type="hidden" name="user" value="{{ request.user.id }}" />
<input type="hidden" name="privacy" value="public" />
<div class="field"> <div class="field">
<label class="label" for="id_name">{% trans "Group Name:" %}</label> <label class="label" for="id_name">{% trans "Group Name:" %}</label>
{{ group_form.name }} {{ group_form.name }}
@ -19,7 +18,7 @@
<div class="column"> <div class="column">
<div class="field has-addons"> <div class="field has-addons">
<div class="control"> <div class="control">
{% include 'snippets/privacy_select.html' with current=group.privacy %} {% include 'snippets/privacy_select_no_followers.html' with current=group.privacy %}
</div> </div>
<div class="control"> <div class="control">
<button type="submit" class="button is-primary">{% trans "Save" %}</button> <button type="submit" class="button is-primary">{% trans "Save" %}</button>

View file

@ -27,4 +27,10 @@
{% include 'notifications/items/leave.html' %} {% include 'notifications/items/leave.html' %}
{% elif notification.notification_type == 'REMOVE' %} {% elif notification.notification_type == 'REMOVE' %}
{% include 'notifications/items/remove.html' %} {% include 'notifications/items/remove.html' %}
{% elif notification.notification_type == 'GROUP_PRIVACY' %}
{% include 'notifications/items/update.html' %}
{% elif notification.notification_type == 'GROUP_NAME' %}
{% include 'notifications/items/update.html' %}
{% elif notification.notification_type == 'GROUP_DESCRIPTION' %}
{% include 'notifications/items/update.html' %}
{% endif %} {% endif %}

View file

@ -18,6 +18,3 @@ has joined your group "<a href="{{ group_path }}">{{ group_name }}</a>"
{% endblocktrans %} {% endblocktrans %}
{% endblock %} {% endblock %}

View file

@ -0,0 +1,28 @@
{% extends 'notifications/items/item_layout.html' %}
{% load i18n %}
{% load utilities %}
{% block primary_link %}{% spaceless %}
{{ notification.related_group.local_path }}
{% endspaceless %}{% endblock %}
{% block icon %}
<span class="icon icon-local"></span>
{% endblock %}
{% block description %}
{% if notification.notification_type == 'GROUP_PRIVACY' %}
{% blocktrans with group_name=notification.related_group.name group_path=notification.related_group.local_path %}
has changed the privacy level for <a href="{{ group_path }}">{{ group_name }}</a>
{% endblocktrans %}
{% elif notification.notification_type == 'GROUP_NAME' %}
{% blocktrans with group_name=notification.related_group.name group_path=notification.related_group.local_path %}
has changed the name of <a href="{{ group_path }}">{{ group_name }}</a>
{% endblocktrans %}
{% else %}
{% blocktrans with group_name=notification.related_group.name group_path=notification.related_group.local_path %}
has changed the description of <a href="{{ group_path }}">{{ group_name }}</a>
{% endblocktrans %}
{% endif %}
{% endblock %}

View file

@ -0,0 +1,21 @@
{% load i18n %}
{% load utilities %}
<div class="select {{ class }}">
{% firstof privacy_uuid 0|uuid as uuid %}
{% if not no_label %}
<label class="is-sr-only" for="privacy_{{ uuid }}">{% trans "Post privacy" %}</label>
{% endif %}
{% firstof current user.default_post_privacy "public" as privacy %}
<select name="privacy" id="privacy_{{ uuid }}">
<option value="public" {% if privacy == 'public' %}selected{% endif %}>
{% trans "Public" %}
</option>
<option value="unlisted" {% if privacy == 'unlisted' %}selected{% endif %}>
{% trans "Unlisted" %}
</option>
<option value="direct" {% if privacy == 'direct' %}selected{% endif %}>
{% trans "Private" %}
</option>
</select>
</div>

View file

@ -75,15 +75,6 @@ class Group(TestCase):
) )
models.GroupMember.objects.create(group=self.public_group, user=self.capybara) models.GroupMember.objects.create(group=self.public_group, user=self.capybara)
def test_group_members_can_see_followers_only_groups(self, _):
"""follower-only group should not be excluded from group listings for group members viewing"""
rat_groups = models.Group.privacy_filter(self.rat).all()
badger_groups = models.Group.privacy_filter(self.badger).all()
self.assertFalse(self.followers_only_group in rat_groups)
self.assertTrue(self.followers_only_group in badger_groups)
def test_group_members_can_see_private_groups(self, _): def test_group_members_can_see_private_groups(self, _):
"""direct privacy group should not be excluded from group listings for group members viewing""" """direct privacy group should not be excluded from group listings for group members viewing"""

View file

@ -47,6 +47,31 @@ class Group(View):
if not form.is_valid(): if not form.is_valid():
return redirect("group", user_group.id) return redirect("group", user_group.id)
user_group = form.save() user_group = form.save()
# 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,
)
return redirect("group", user_group.id) return redirect("group", user_group.id)