diff --git a/h/security/policy/_api_cookie.py b/h/security/policy/_api_cookie.py index a6beb4d6fe1..5bc3c3eab5a 100644 --- a/h/security/policy/_api_cookie.py +++ b/h/security/policy/_api_cookie.py @@ -8,6 +8,7 @@ COOKIE_AUTHENTICATABLE_API_REQUESTS = [ ("api.groups", "POST"), # Create a new group. ("api.group", "PATCH"), # Edit an existing group. + ("api.group_members", "GET"), # Get a list of a group's members. ] diff --git a/h/static/scripts/group-forms/components/CreateEditGroupForm.tsx b/h/static/scripts/group-forms/components/CreateEditGroupForm.tsx index 61ceb811b4e..839823a2e17 100644 --- a/h/static/scripts/group-forms/components/CreateEditGroupForm.tsx +++ b/h/static/scripts/group-forms/components/CreateEditGroupForm.tsx @@ -3,9 +3,16 @@ import { useEffect, useId, useMemo, useState } from 'preact/hooks'; import { Button, CancelIcon, + TrashIcon, Input, RadioGroup, + Table, + TableHead, + TableRow, + TableCell, + TableBody, Textarea, + Select, ModalDialog, useWarnOnPageUnload, } from '@hypothesis/frontend-shared'; @@ -219,6 +226,8 @@ export default function CreateEditGroupForm() { 'unmodified' | 'unsaved' | 'saving' | 'saved' >('unmodified'); + const [members, setMembers] = useState([]); + // Warn when leaving page if there are unsaved changes. We only do this when // editing a group because this hook lacks a way to disable the handler before // calling `setLocation` after a successful group creation. @@ -333,6 +342,20 @@ export default function CreateEditGroupForm() { } }; + const getMembers = async () => { + let newMembers = (await callAPI(config.api.getGroupMembers!.url, { + method: config.api.getGroupMembers!.method, + headers: config.api.getGroupMembers!.headers, + })); + setMembers(newMembers); + }; + + useEffect(()=> { + if (config.api.getGroupMembers !== null) { + getMembers(); + } + } ,[]) + return (

@@ -393,6 +416,40 @@ export default function CreateEditGroupForm() { )} + + + + Username + Role + + + + + {members.map((member) => ( + + {member.username} + + + + + + ))} + +
+
{errorMessage && ( diff --git a/h/static/scripts/group-forms/config.ts b/h/static/scripts/group-forms/config.ts index 102883388fa..22dc3b5aef7 100644 --- a/h/static/scripts/group-forms/config.ts +++ b/h/static/scripts/group-forms/config.ts @@ -12,6 +12,7 @@ export type ConfigObject = { api: { createGroup: APIConfig; updateGroup: APIConfig | null; + getGroupMembers: APIConfig | null; }; context: { group: { diff --git a/h/views/api/groups.py b/h/views/api/groups.py index d0714f91c4e..db81145373d 100644 --- a/h/views/api/groups.py +++ b/h/views/api/groups.py @@ -1,6 +1,9 @@ from pyramid.httpexceptions import HTTPConflict, HTTPNoContent, HTTPNotFound +from sqlalchemy import select +from sqlalchemy.orm import selectinload from h.i18n import TranslationString as _ +from h.models import GroupMembership from h.presenters import GroupJSONPresenter, GroupsJSONPresenter, UserJSONPresenter from h.schemas.api.group import ( CreateGroupAPISchema, @@ -11,11 +14,6 @@ from h.traversal import EditGroupMembershipContext, GroupContext, GroupMembershipContext from h.views.api.config import api_config from h.views.api.exceptions import PayloadError -from h.models import GroupMembership - -from sqlalchemy import select -from sqlalchemy.orm import selectinload - DEFAULT_GROUP_TYPE = "private" @@ -245,11 +243,7 @@ def edit_member(request): ) context = EditGroupMembershipContext( - request.db, - request.context.group, - request.context.user, - membership, - appstruct["role"], + request.context.group, request.context.user, membership, appstruct["role"] ) if not request.has_permission(Permission.Group.MEMBER_EDIT, context): diff --git a/h/views/groups.py b/h/views/groups.py index 7af98e57c31..b33070d5491 100644 --- a/h/views/groups.py +++ b/h/views/groups.py @@ -77,6 +77,11 @@ def _js_config(self): "url": self.request.route_url("api.group", id=group.pubid), "headers": {"X-CSRF-Token": csrf_token}, } + js_config["api"]["getGroupMembers"] = { + "method": "GET", + "url": self.request.route_url("api.group_members", pubid=group.pubid), + "headers": {"X-CSRF-Token": csrf_token}, + } return js_config