feat: group member removal can be achieved from the group edition page

This commit is contained in:
Éloi Rivard 2024-04-28 16:13:01 +02:00
parent 69b565e1ad
commit 69019763d4
No known key found for this signature in database
GPG key ID: 7EDA204EA57DD184
9 changed files with 410 additions and 77 deletions

View file

@ -1,3 +1,7 @@
Added
^^^^^
- Group member removal can be achieved from the group edition page :issue:`192`
Changed
^^^^^^^
- Model `identifier_attributes` are fixed.

View file

@ -251,12 +251,15 @@ def set_writable(field):
class IDToModel:
def __init__(self, model_name):
def __init__(self, model_name, raise_on_errors=True):
self.model_name = model_name
self.raise_on_errors = raise_on_errors
def __call__(self, data):
model = getattr(models, self.model_name)
instance = data if isinstance(data, model) else model.get(data)
if not instance:
if instance:
return instance
if self.raise_on_errors:
raise wtforms.ValidationError()
return instance

View file

@ -15,6 +15,7 @@ from canaille.app.forms import is_uri
from canaille.app.forms import phone_number
from canaille.app.forms import set_readonly
from canaille.app.forms import unique_values
from canaille.app.i18n import gettext
from canaille.app.i18n import lazy_gettext as _
from canaille.app.i18n import native_language_name_from_code
from canaille.backends import BaseBackend
@ -56,6 +57,20 @@ def existing_login(form, field):
)
def existing_group_member(form, field):
if field.data is None:
raise wtforms.ValidationError(
gettext("The user you are trying to remove does not exist.")
)
if field.data not in form.group.members:
raise wtforms.ValidationError(
gettext(
"The user '{user}' has already been removed from the group '{group}'"
).format(user=field.data.formatted_name, group=form.group.display_name)
)
def non_empty_groups(form, field):
"""LDAP groups cannot be empty because groupOfNames.member is a MUST
attribute.
@ -373,6 +388,13 @@ class EditGroupForm(Form):
)
class DeleteGroupMemberForm(Form):
member = wtforms.StringField(
filters=[IDToModel("User", raise_on_errors=False)],
validators=[existing_group_member],
)
class JoinForm(Form):
email = wtforms.EmailField(
_("Email address"),

View file

@ -13,6 +13,7 @@ from canaille.app.i18n import gettext as _
from canaille.app.themes import render_template
from .forms import CreateGroupForm
from .forms import DeleteGroupMemberForm
from .forms import EditGroupForm
bp = Blueprint("groups", __name__, url_prefix="/groups")
@ -72,6 +73,12 @@ def group(user, group):
if request.form.get("action") == "delete":
return delete_group(group)
if request.form.get("action") == "confirm-remove-member":
return delete_member(group)
if request.form.get("action") == "remove-member":
return delete_member(group)
abort(400, f"bad form action: {request.form.get('action')}")
@ -88,7 +95,11 @@ def edit_group(group):
},
)
if request.form and not request.form.get("page"):
if (
request.form
and request.form.get("action") == "edit"
and not request.form.get("page")
):
if form.validate():
group.description = form.description.data
group.save()
@ -105,7 +116,7 @@ def edit_group(group):
return render_htmx_template(
"group.html",
"partial/users.html",
"partial/group-members.html",
form=form,
menuitem="groups",
edited_group=group,
@ -113,6 +124,36 @@ def edit_group(group):
)
def delete_member(group):
form = DeleteGroupMemberForm(request.form or None)
form.group = group
if not form.validate():
flash(
"\n".join(form.errors.get("member")),
"error",
)
elif request.form.get("action") == "confirm-remove-member":
return render_template(
"modals/remove-group-member.html", group=group, form=form
)
else:
flash(
_(
f"{form.member.data.formatted_name} has been removed from the group {group.display_name}"
),
"success",
)
group.members = [
member for member in group.members if member != form.member.data
]
group.save()
return edit_group(group)
def delete_group(group):
flash(
_("The group %(group)s has been sucessfully deleted", group=group.display_name),

View file

@ -80,7 +80,7 @@
{{ table.search(table_form, "table.users") }}
</div>
{% include "partial/users.html" %}
{% include "partial/group-members.html" %}
{% endif %}
{% endblock %}

View file

@ -0,0 +1,31 @@
{% extends theme('base.html') %}
{% import 'macro/form.html' as fui %}
{% block content %}
<div class="ui warning message" id="modal-remove-member">
{% call fui.render_form(form) %}
<input type="hidden" name="member" value="{{ form.member.data }}">
<div class="ui icon header">
<i class="user minus alternate icon"></i>
{% trans %}Group member deletion{% endtrans %}
</div>
<div class="content">
<p>
{% trans group_name=group.display_name, user_name=form.member.data.formatted_name %}
Are you sure you want to remove {{ user_name }} from the group "{{ group_name }}"?
{% endtrans %}
</p>
</div>
<div class="ui center aligned container">
<div class="ui stackable buttons">
<a href="{{ request.url }}" class="ui cancel button">
{% trans %}Cancel{% endtrans %}
</a>
<button name="action" value="remove-member" id="remove-member" class="ui red approve button">
{% trans %}Remove{% endtrans %}
</button>
</div>
</div>
{% endcall %}
</div>
{% endblock %}

View file

@ -0,0 +1,104 @@
{% import "macro/table.html" as table %}
<table class="ui bottom attached table users">
<thead>
<tr>
{% if user.can_read("photo") %}
<th></th>
{% endif %}
{% if user.can_read("user_name") %}
<th>{% trans %}Login{% endtrans %}</th>
{% endif %}
{% if user.can_read("family_name") or user.can_read("given_name") %}
<th>{% trans %}Name{% endtrans %}</th>
{% endif %}
{% if user.can_manage_groups %}
<th>{% trans %}Groups{% endtrans %}</th>
<th>{% trans %}Actions{% endtrans %}</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for watched_user in table_form.items_slice %}
<tr>
{% if user.can_read("photo") %}
<td class="collapsing">
<a href="{{ url_for('core.account.profile_edition', edited_user=watched_user) }}">
{% if user.can_manage_users and watched_user.locked %}
<i class="lock circle big black icon" title="{% trans %}This account is locked{% endtrans %}"></i>
{% elif watched_user.photo and watched_user.photo %}
<img class="ui avatar image" src="{{ url_for("core.account.photo", user=watched_user, field="photo") }}" alt="User photo">
{% else %}
<i class="user circle big black icon"></i>
{% endif %}
</a>
</td>
{% endif %}
{% if user.can_read("user_name") %}
<td>
<a href="{{ url_for('core.account.profile_edition', edited_user=watched_user) }}">
{% if watched_user.user_name %}
{{ watched_user.user_name }}
{% else %}
{{ watched_user.identifier }}
{% endif %}
</a>
</td>
{% endif %}
{% if user.can_read("family_name") or user.can_read("given_name") %}
<td>{{ watched_user.formatted_name }}</td>
{% endif %}
{% if user.can_manage_groups %}
<td>
{% for group in watched_user.groups %}
<a class="ui label" href="{{ url_for('core.groups.group', group=group) }}"{% if group.description %} title="{{ group.description }}"{% endif %}>
{{ group.display_name }}
</a>
{% endfor %}
</td>
<td>
{% if edited_group.members|len > 1 %}
<form id="deletegroupmemberform-{{ watched_user.id }}" action="{{ url_for(request.url_rule.endpoint, **request.view_args) }}" method="POST">
{{ form.hidden_tag() if form.hidden_tag }}
<input type="hidden" name="member" value="{{ watched_user.id }}">
<button class="basic negative ui button" name="action" value="confirm-remove-member">
{% trans %}Remove{% endtrans %}
</button>
</form>
{% else %}
<button class="basic negative ui button" title="{% trans %}Groups must have at least one member{% endtrans %}" disabled>
{% trans %}Remove{% endtrans %}
</button>
{% endif %}
</td>
{% endif %}
</tr>
{% else %}
<tr>
<td colspan="5">
<div class="ui icon message">
<i class="exclamation icon"></i>
<div class="content">
{% if request.headers.get("Hx-Request") %}
<div class="header">
{% trans %}No item matches your request{% endtrans %}
</div>
<p>{% trans %}Maybe try with different criterias?{% endtrans %}</p>
{% else %}
<div class="header">
{% trans %}There is nothing here{% endtrans %}
</div>
{% endif %}
</div>
</div>
</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<th colspan="5">
{{ table.pagination(table_form) }}
</th>
</tr>
</tfoot>
</table>

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-04-27 14:07+0200\n"
"POT-Creation-Date: 2024-04-28 19:05+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -53,8 +53,8 @@ msgstr ""
msgid "John Doe"
msgstr ""
#: canaille/backends/ldap/backend.py:176 canaille/core/endpoints/forms.py:149
#: canaille/core/endpoints/forms.py:398
#: canaille/backends/ldap/backend.py:176 canaille/core/endpoints/forms.py:164
#: canaille/core/endpoints/forms.py:420
msgid "jdoe"
msgstr ""
@ -128,9 +128,10 @@ msgstr ""
msgid "You are already logged in, you cannot create an account."
msgstr ""
#: canaille/core/endpoints/account.py:297 canaille/core/endpoints/forms.py:298
#: canaille/core/endpoints/forms.py:416 canaille/core/templates/groups.html:5
#: canaille/core/endpoints/account.py:297 canaille/core/endpoints/forms.py:313
#: canaille/core/endpoints/forms.py:438 canaille/core/templates/groups.html:5
#: canaille/core/templates/groups.html:23
#: canaille/core/templates/partial/group-members.html:15
#: canaille/core/templates/partial/users.html:18
#: canaille/templates/base.html:58
msgid "Groups"
@ -253,10 +254,10 @@ msgstr ""
msgid "Email"
msgstr ""
#: canaille/core/endpoints/admin.py:29 canaille/core/endpoints/forms.py:82
#: canaille/core/endpoints/forms.py:105 canaille/core/endpoints/forms.py:194
#: canaille/core/endpoints/forms.py:384 canaille/core/endpoints/forms.py:410
#: canaille/core/endpoints/forms.py:431 canaille/core/endpoints/forms.py:447
#: canaille/core/endpoints/admin.py:29 canaille/core/endpoints/forms.py:97
#: canaille/core/endpoints/forms.py:120 canaille/core/endpoints/forms.py:209
#: canaille/core/endpoints/forms.py:406 canaille/core/endpoints/forms.py:432
#: canaille/core/endpoints/forms.py:453 canaille/core/endpoints/forms.py:469
msgid "jane@doe.com"
msgstr ""
@ -325,57 +326,67 @@ msgstr ""
msgid "Your password has been updated successfully"
msgstr ""
#: canaille/core/endpoints/forms.py:30
#: canaille/core/endpoints/forms.py:31
msgid "The user name '{user_name}' already exists"
msgstr ""
#: canaille/core/endpoints/forms.py:39
#: canaille/core/endpoints/forms.py:40
msgid "The email '{email}' is already used"
msgstr ""
#: canaille/core/endpoints/forms.py:46
#: canaille/core/endpoints/forms.py:47
msgid "The group '{group}' already exists"
msgstr ""
#: canaille/core/endpoints/forms.py:55
#: canaille/core/endpoints/forms.py:56
msgid "The login '{login}' does not exist"
msgstr ""
#: canaille/core/endpoints/forms.py:71
#: canaille/core/endpoints/forms.py:63
msgid "The user you are trying to remove does not exist."
msgstr ""
#: canaille/core/endpoints/forms.py:68
msgid "The user '{user}' has already been removed from the group '{group}'"
msgstr ""
#: canaille/core/endpoints/forms.py:86
msgid ""
"The group '{group}' cannot be removed, because it must have at least one "
"user left."
msgstr ""
#: canaille/core/endpoints/forms.py:79 canaille/core/endpoints/forms.py:102
#: canaille/core/endpoints/forms.py:94 canaille/core/endpoints/forms.py:117
#: canaille/core/templates/partial/group-members.html:9
#: canaille/core/templates/partial/users.html:9
msgid "Login"
msgstr ""
#: canaille/core/endpoints/forms.py:92 canaille/core/endpoints/forms.py:114
#: canaille/core/endpoints/forms.py:248
#: canaille/core/endpoints/forms.py:107 canaille/core/endpoints/forms.py:129
#: canaille/core/endpoints/forms.py:263
#: canaille/core/templates/profile_settings.html:63
msgid "Password"
msgstr ""
#: canaille/core/endpoints/forms.py:121 canaille/core/endpoints/forms.py:258
#: canaille/core/endpoints/forms.py:136 canaille/core/endpoints/forms.py:273
msgid "Password confirmation"
msgstr ""
#: canaille/core/endpoints/forms.py:124 canaille/core/endpoints/forms.py:261
#: canaille/core/endpoints/forms.py:139 canaille/core/endpoints/forms.py:276
msgid "Password and confirmation do not match."
msgstr ""
#: canaille/core/endpoints/forms.py:143
#: canaille/core/endpoints/forms.py:158
msgid "Automatic"
msgstr ""
#: canaille/core/endpoints/forms.py:148
#: canaille/core/endpoints/forms.py:163
msgid "Username"
msgstr ""
#: canaille/core/endpoints/forms.py:152 canaille/core/endpoints/forms.py:347
#: canaille/core/endpoints/forms.py:361
#: canaille/core/endpoints/forms.py:167 canaille/core/endpoints/forms.py:362
#: canaille/core/endpoints/forms.py:376
#: canaille/core/templates/partial/group-members.html:12
#: canaille/core/templates/partial/groups.html:6
#: canaille/core/templates/partial/users.html:12
#: canaille/oidc/endpoints/forms.py:26
@ -383,187 +394,187 @@ msgstr ""
msgid "Name"
msgstr ""
#: canaille/core/endpoints/forms.py:154
#: canaille/core/endpoints/forms.py:169
msgid "Title"
msgstr ""
#: canaille/core/endpoints/forms.py:154
#: canaille/core/endpoints/forms.py:169
msgid "Vice president"
msgstr ""
#: canaille/core/endpoints/forms.py:157
#: canaille/core/endpoints/forms.py:172
msgid "Given name"
msgstr ""
#: canaille/core/endpoints/forms.py:159
#: canaille/core/endpoints/forms.py:174
msgid "John"
msgstr ""
#: canaille/core/endpoints/forms.py:165
#: canaille/core/endpoints/forms.py:180
msgid "Family Name"
msgstr ""
#: canaille/core/endpoints/forms.py:168
#: canaille/core/endpoints/forms.py:183
msgid "Doe"
msgstr ""
#: canaille/core/endpoints/forms.py:174
#: canaille/core/endpoints/forms.py:189
msgid "Display Name"
msgstr ""
#: canaille/core/endpoints/forms.py:177
#: canaille/core/endpoints/forms.py:192
msgid "Johnny"
msgstr ""
#: canaille/core/endpoints/forms.py:184 canaille/core/endpoints/forms.py:437
#: canaille/core/endpoints/forms.py:199 canaille/core/endpoints/forms.py:459
#: canaille/core/templates/profile_edit.html:176
msgid "Email addresses"
msgstr ""
#: canaille/core/endpoints/forms.py:190 canaille/core/endpoints/forms.py:427
#: canaille/core/endpoints/forms.py:205 canaille/core/endpoints/forms.py:449
msgid ""
"This email will be used as a recovery address to reset the password if "
"needed"
msgstr ""
#: canaille/core/endpoints/forms.py:204
#: canaille/core/endpoints/forms.py:219
msgid "Phone numbers"
msgstr ""
#: canaille/core/endpoints/forms.py:205
#: canaille/core/endpoints/forms.py:220
msgid "555-000-555"
msgstr ""
#: canaille/core/endpoints/forms.py:212
#: canaille/core/endpoints/forms.py:227
msgid "Address"
msgstr ""
#: canaille/core/endpoints/forms.py:214
#: canaille/core/endpoints/forms.py:229
msgid "132, Foobar Street, Gotham City 12401, XX"
msgstr ""
#: canaille/core/endpoints/forms.py:218
#: canaille/core/endpoints/forms.py:233
msgid "Street"
msgstr ""
#: canaille/core/endpoints/forms.py:220
#: canaille/core/endpoints/forms.py:235
msgid "132, Foobar Street"
msgstr ""
#: canaille/core/endpoints/forms.py:224
#: canaille/core/endpoints/forms.py:239
msgid "Postal Code"
msgstr ""
#: canaille/core/endpoints/forms.py:230
#: canaille/core/endpoints/forms.py:245
msgid "Locality"
msgstr ""
#: canaille/core/endpoints/forms.py:232
#: canaille/core/endpoints/forms.py:247
msgid "Gotham City"
msgstr ""
#: canaille/core/endpoints/forms.py:236
#: canaille/core/endpoints/forms.py:251
msgid "Region"
msgstr ""
#: canaille/core/endpoints/forms.py:238
#: canaille/core/endpoints/forms.py:253
msgid "North Pole"
msgstr ""
#: canaille/core/endpoints/forms.py:242
#: canaille/core/endpoints/forms.py:257
msgid "Photo"
msgstr ""
#: canaille/core/endpoints/forms.py:246
#: canaille/core/endpoints/forms.py:261
#: canaille/core/templates/profile_add.html:56
#: canaille/core/templates/profile_edit.html:64
msgid "Delete the photo"
msgstr ""
#: canaille/core/endpoints/forms.py:269
#: canaille/core/endpoints/forms.py:284
msgid "User number"
msgstr ""
#: canaille/core/endpoints/forms.py:271 canaille/core/endpoints/forms.py:277
#: canaille/core/endpoints/forms.py:286 canaille/core/endpoints/forms.py:292
msgid "1234"
msgstr ""
#: canaille/core/endpoints/forms.py:275
#: canaille/core/endpoints/forms.py:290
msgid "Department"
msgstr ""
#: canaille/core/endpoints/forms.py:281
#: canaille/core/endpoints/forms.py:296
msgid "Organization"
msgstr ""
#: canaille/core/endpoints/forms.py:283
#: canaille/core/endpoints/forms.py:298
msgid "Cogip LTD."
msgstr ""
#: canaille/core/endpoints/forms.py:287
#: canaille/core/endpoints/forms.py:302
msgid "Website"
msgstr ""
#: canaille/core/endpoints/forms.py:289
#: canaille/core/endpoints/forms.py:304
msgid "https://mywebsite.tld"
msgstr ""
#: canaille/core/endpoints/forms.py:294
#: canaille/core/endpoints/forms.py:309
msgid "Preferred language"
msgstr ""
#: canaille/core/endpoints/forms.py:301
#: canaille/core/endpoints/forms.py:316
msgid "users, admins …"
msgstr ""
#: canaille/core/endpoints/forms.py:326
#: canaille/core/endpoints/forms.py:341
msgid "Account expiration"
msgstr ""
#: canaille/core/endpoints/forms.py:350
#: canaille/core/endpoints/forms.py:365
msgid "group"
msgstr ""
#: canaille/core/endpoints/forms.py:354 canaille/core/endpoints/forms.py:371
#: canaille/core/endpoints/forms.py:369 canaille/core/endpoints/forms.py:386
#: canaille/core/templates/partial/groups.html:7
msgid "Description"
msgstr ""
#: canaille/core/endpoints/forms.py:378 canaille/core/endpoints/forms.py:403
#: canaille/core/endpoints/forms.py:400 canaille/core/endpoints/forms.py:425
msgid "Email address"
msgstr ""
#: canaille/core/endpoints/forms.py:397
#: canaille/core/endpoints/forms.py:419
msgid "User name"
msgstr ""
#: canaille/core/endpoints/forms.py:401
#: canaille/core/endpoints/forms.py:423
msgid "Username editable by the invitee"
msgstr ""
#: canaille/core/endpoints/forms.py:440
#: canaille/core/endpoints/forms.py:462
msgid "New email address"
msgstr ""
#: canaille/core/endpoints/groups.py:38
#: canaille/core/endpoints/groups.py:39
msgid "Group creation failed."
msgstr ""
#: canaille/core/endpoints/groups.py:46
#: canaille/core/endpoints/groups.py:47
#, python-format
msgid "The group %(group)s has been sucessfully created"
msgstr ""
#: canaille/core/endpoints/groups.py:96
#: canaille/core/endpoints/groups.py:107
#, python-format
msgid "The group %(group)s has been sucessfully edited."
msgstr ""
#: canaille/core/endpoints/groups.py:104
#: canaille/core/endpoints/groups.py:115
msgid "Group edition failed."
msgstr ""
#: canaille/core/endpoints/groups.py:118
#: canaille/core/endpoints/groups.py:159
#, python-format
msgid "The group %(group)s has been sucessfully deleted"
msgstr ""
@ -1262,6 +1273,7 @@ msgstr ""
#: canaille/core/templates/modals/delete-account.html:26
#: canaille/core/templates/modals/delete-group.html:22
#: canaille/core/templates/modals/lock-account.html:26
#: canaille/core/templates/modals/remove-group-member.html:22
#: canaille/oidc/templates/modals/delete-client.html:20
#: canaille/oidc/templates/modals/revoke-token.html:20
msgid "Cancel"
@ -1305,10 +1317,32 @@ msgstr ""
msgid "Lock"
msgstr ""
#: canaille/core/templates/partial/groups.html:8
msgid "Number of members"
#: canaille/core/templates/modals/remove-group-member.html:10
msgid "Group member deletion"
msgstr ""
#: canaille/core/templates/modals/remove-group-member.html:14
#, python-format
msgid ""
"Are you sure you want to remove %(user_name)s from the group "
"\"%(group_name)s\"?"
msgstr ""
#: canaille/core/templates/modals/remove-group-member.html:25
#: canaille/core/templates/partial/group-members.html:64
msgid "Remove"
msgstr ""
#: canaille/core/templates/partial/group-members.html:16
msgid "Actions"
msgstr ""
#: canaille/core/templates/partial/group-members.html:27
#: canaille/core/templates/partial/users.html:29
msgid "This account is locked"
msgstr ""
#: canaille/core/templates/partial/group-members.html:79
#: canaille/core/templates/partial/groups.html:31
#: canaille/core/templates/partial/users.html:73
#: canaille/oidc/templates/partial/authorization_list.html:31
@ -1317,6 +1351,7 @@ msgstr ""
msgid "No item matches your request"
msgstr ""
#: canaille/core/templates/partial/group-members.html:81
#: canaille/core/templates/partial/groups.html:33
#: canaille/core/templates/partial/users.html:75
#: canaille/oidc/templates/partial/authorization_list.html:33
@ -1325,6 +1360,7 @@ msgstr ""
msgid "Maybe try with different criterias?"
msgstr ""
#: canaille/core/templates/partial/group-members.html:84
#: canaille/core/templates/partial/groups.html:36
#: canaille/core/templates/partial/users.html:78
#: canaille/oidc/templates/partial/authorization_list.html:36
@ -1334,8 +1370,8 @@ msgstr ""
msgid "There is nothing here"
msgstr ""
#: canaille/core/templates/partial/users.html:29
msgid "This account is locked"
#: canaille/core/templates/partial/groups.html:8
msgid "Number of members"
msgstr ""
#: canaille/oidc/utils.py:6

View file

@ -292,3 +292,95 @@ def test_user_list_search(testclient, logged_admin, foo_group, user, moderator):
res.mustcontain(user.formatted_name)
res.mustcontain(no=logged_admin.formatted_name)
res.mustcontain(no=moderator.formatted_name)
def test_remove_member(testclient, logged_admin, foo_group, user, moderator):
foo_group.members = [user, moderator]
foo_group.save()
res = testclient.get("/groups/foo")
form = res.forms[f"deletegroupmemberform-{user.id}"]
res = form.submit(name="action", value="confirm-remove-member")
res = res.form.submit(name="action", value="remove-member")
assert (
"success",
"John (johnny) Doe has been removed from the group foo",
) in res.flashes
foo_group.reload()
assert user not in foo_group.members
def test_remove_member_already_remove_from_group(
testclient, logged_admin, foo_group, user, moderator
):
foo_group.members = [user, moderator]
foo_group.save()
res = testclient.get("/groups/foo")
form = res.forms[f"deletegroupmemberform-{user.id}"]
foo_group.members = [moderator]
foo_group.save()
res = form.submit(name="action", value="confirm-remove-member")
assert (
"error",
"The user 'John (johnny) Doe' has already been removed from the group 'foo'",
) in res.flashes
def test_confirm_remove_member_already_removed_from_group(
testclient, logged_admin, foo_group, user, moderator
):
foo_group.members = [user, moderator]
foo_group.save()
res = testclient.get("/groups/foo")
form = res.forms[f"deletegroupmemberform-{user.id}"]
res = form.submit(name="action", value="confirm-remove-member")
foo_group.members = [moderator]
foo_group.save()
res = res.form.submit(name="action", value="remove-member")
assert (
"error",
"The user 'John (johnny) Doe' has already been removed from the group 'foo'",
) in res.flashes
def test_remove_member_already_deleted(
testclient, logged_admin, foo_group, user, moderator
):
foo_group.members = [user, moderator]
foo_group.save()
res = testclient.get("/groups/foo")
form = res.forms[f"deletegroupmemberform-{user.id}"]
user.delete()
res = form.submit(name="action", value="confirm-remove-member")
assert (
"error",
"The user you are trying to remove does not exist.",
) in res.flashes
def test_confirm_remove_member_already_deleted(
testclient, logged_admin, foo_group, user, moderator
):
foo_group.members = [user, moderator]
foo_group.save()
res = testclient.get("/groups/foo")
form = res.forms[f"deletegroupmemberform-{user.id}"]
res = form.submit(name="action", value="confirm-remove-member")
user.delete()
res = res.form.submit(name="action", value="remove-member")
assert (
"error",
"The user you are trying to remove does not exist.",
) in res.flashes