From 0dbfbef594e520f039f7bc6c6e157d829ee67f99 Mon Sep 17 00:00:00 2001 From: Craig Williams Date: Tue, 22 Feb 2022 18:11:59 +0000 Subject: [PATCH] Added capability for organisation administrators to accept/reject join requests, remove members, edit their details and reset their passwords --- asset-bundles.json | 1 + assets/avsdev/js/pages/organisation.js | 2 +- assets/avsdev/js/widgets/members.js | 104 ++++++++ locale/en_US/messages.php | 24 ++ routes/organisation-members.php | 9 +- .../OrganisationMembersController.php | 227 +++++++++++++++++- src/Database/Models/Organisation.php | 13 +- .../Seeds/OrganisationPermissions.php | 15 ++ src/ServicesProvider/ServicesProvider.php | 21 ++ ...accept-organisation-join-request.html.twig | 20 ++ ...reject-organisation-join-request.html.twig | 20 ++ ...nfirm-remove-organisation-member.html.twig | 17 ++ templates/pages/organisation.html.twig | 2 +- .../tables/organisation-members.html.twig | 116 +++++++++ 14 files changed, 582 insertions(+), 9 deletions(-) create mode 100644 assets/avsdev/js/widgets/members.js create mode 100644 templates/modals/confirm-accept-organisation-join-request.html.twig create mode 100644 templates/modals/confirm-reject-organisation-join-request.html.twig create mode 100644 templates/modals/confirm-remove-organisation-member.html.twig create mode 100644 templates/tables/organisation-members.html.twig diff --git a/asset-bundles.json b/asset-bundles.json index fa94f8b..774b481 100644 --- a/asset-bundles.json +++ b/asset-bundles.json @@ -20,6 +20,7 @@ "userfrosting/js/widgets/users.js", "avsdev/js/widgets/users.js", "avsdev/js/widgets/organisations.js", + "avsdev/js/widgets/members.js", "avsdev/js/pages/organisation.js" ] }, diff --git a/assets/avsdev/js/pages/organisation.js b/assets/avsdev/js/pages/organisation.js index f365d09..ef708dd 100644 --- a/assets/avsdev/js/pages/organisation.js +++ b/assets/avsdev/js/pages/organisation.js @@ -19,7 +19,7 @@ $(document).ready(function() { // Bind user table buttons $("#widget-organisation-members").on("pagerComplete.ufTable", function () { + bindMemberButtons($(this)); bindUserButtons($(this)); - bindUserButtonsExtra($(this)); }); }); diff --git a/assets/avsdev/js/widgets/members.js b/assets/avsdev/js/widgets/members.js new file mode 100644 index 0000000..e81ae79 --- /dev/null +++ b/assets/avsdev/js/widgets/members.js @@ -0,0 +1,104 @@ +/** + * Members widget. Sets up dropdowns, modals, etc for a table of members. + * + * Depends on widgets/users.js + */ + +/** + * Link extra user action buttons in addition to the base ones. + * @param {module:jQuery} el jQuery wrapped element to target. + * @param {{delete_redirect: string}} options Options used to modify behaviour of button actions. + */ +function bindMemberButtons(el, options) { + if (!options) options = {}; + + // Remove user button + el.find('.js-member-remove').click(function(e) { + e.preventDefault(); + + $("body").ufModal({ + sourceUrl: site.uri.public + '/modals/organisations/o/' + $(this).data('slug') + '/members/confirm-remove', + ajaxParams: { + slug: $(this).data('slug'), + user_name: $(this).data('user_name') + }, + msgTarget: $("#alerts-page") + }); + + $("body").on('renderSuccess.ufModal', function() { + var modal = $(this).ufModal('getModal'); + var form = modal.find('.js-form'); + + form.ufForm() + .on("submitSuccess.ufForm", function() { + // Navigate or reload page on success + window.location.reload(); + }); + }); + }); + + // Accept member button + el.find('.js-member-accept').click(function(e) { + e.preventDefault(); + + $("body").ufModal({ + sourceUrl: site.uri.public + '/modals/organisations/o/' + $(this).data('slug') + '/members/confirm-accept', + ajaxParams: { + slug: $(this).data('slug'), + user_name: $(this).data('user_name') + }, + msgTarget: $("#alerts-page") + }); + + $("body").on('renderSuccess.ufModal', function() { + var modal = $(this).ufModal('getModal'); + var form = modal.find('.js-form'); + + form.ufForm() + .on("submitSuccess.ufForm", function() { + // Navigate or reload page on success + window.location.reload(); + }); + }); + }); + + + // Reject user button + el.find('.js-member-reject').click(function(e) { + e.preventDefault(); + + $("body").ufModal({ + sourceUrl: site.uri.public + '/modals/organisations/o/' + $(this).data('slug') + '/members/confirm-reject', + ajaxParams: { + slug: $(this).data('slug'), + user_name: $(this).data('user_name') + }, + msgTarget: $("#alerts-page") + }); + + $("body").on('renderSuccess.ufModal', function() { + var modal = $(this).ufModal('getModal'); + var form = modal.find('.js-form'); + + form.ufForm() + .on("submitSuccess.ufForm", function() { + // Navigate or reload page on success + window.location.reload(); + }); + }); + }); +} + +// function bindMemberCreationButton(el) { +// // Link create button +// el.find('.js-member-create').click(function(e) { +// e.preventDefault(); + +// $("body").ufModal({ +// sourceUrl: site.uri.public + "/modals/users/create", +// msgTarget: $("#alerts-page") +// }); + +// attachUserForm(); +// }); +// }; diff --git a/locale/en_US/messages.php b/locale/en_US/messages.php index 6926470..972e654 100644 --- a/locale/en_US/messages.php +++ b/locale/en_US/messages.php @@ -101,6 +101,18 @@ return [ 'CANCEL_YES' => 'Yes, cancel request', 'CANCEL_SUCCESSFUL' => 'Successfully cancelled request to join organisation {{name}}', + 'ACCEPT' => 'Accept join request', + 'ACCEPT_CONFIRM' => 'Are you sure you want to accept the request from user {{user_name}} to join organisation {{name}}?', + 'ACCEPT_CONFIRM_EXTRA' => 'This will allow them to submit noise records on the organisation\'s behalf, view other members and make agent requests.', + 'ACCEPT_YES' => 'Yes, accept request', + 'ACCEPT_SUCCESSFUL' => 'Successfully accepted the request made by user {{user_name}} to join organisation {{name}}', + + 'REJECT' => 'Reject join request', + 'REJECT_CONFIRM' => 'Are you sure you want to reject the request from user {{user_name}} to join organisation {{name}}?', + 'REJECT_CONFIRM_EXTRA' => 'The requester will be notified of your rejection.', + 'REJECT_YES' => 'Yes, reject request', + 'REJECT_SUCCESSFUL' => 'Successfully rejected the request made by user {{user_name}} to join organisation {{name}}', + 'ALREADY_MEMBER' => 'You are already a member of the organisation {{name}}.', 'REQUEST_PENDING' => 'You have already requested to join organisation {{name}}. Your request is awaiting approval.', 'NO_REQUEST' => 'You have no pending requests to join organisation {{name}}.', @@ -108,12 +120,24 @@ return [ 'TOKEN_NOT_FOUND' => 'User join request token does not exist / user join request has already been accepted/rejected.', 'ACCEPTED' => 'You have successfully accepted the request from user {{user_name}} to join organisation {{organisation_name}}.', 'REJECTED' => 'You have successfully rejected the request from user {{user_name}} to join organisation {{organisation_name}}.', + + 'ACCEPT' => 'Accept request to join', + 'REJECT' => 'Reject request to join', + ], + 'MEMBER' => [ + 'NOT_FOUND' => 'User {{user_name}} is not a member of organisation {{name}}', + 'REMOVE' => 'Remove member from organisation', + 'REMOVE_CONFIRM' => 'Are you sure you want to remove member {{user_name}} from the organisation {{name}}?', + 'REMOVE_YES' => 'Yes, remove member', + 'REMOVE_SUCCESSFUL' => 'Successfully removed member {{user_name}} from organisation {{name}}', ], ], 'MEMBER' => [ 1 => 'Member', 2 => 'Members', + + 'REMOVE' => 'Remove member', ], 'ADMIN' => [ diff --git a/routes/organisation-members.php b/routes/organisation-members.php index 80c27c8..784dc61 100644 --- a/routes/organisation-members.php +++ b/routes/organisation-members.php @@ -26,12 +26,17 @@ $app->group('/api/organisations/o/{slug}/members', function () { $this->delete('/cancel', 'UserFrosting\Sprinkle\Organisations\Controller\OrganisationMembersController:cancel'); - $this->put('/m/{user_name}/accept', 'UserFrosting\Sprinkle\Organisations\Controller\OrganisationMembersController:accept'); - $this->put('/m/{user_name}/reject', 'UserFrosting\Sprinkle\Organisations\Controller\OrganisationMembersController:reject'); + $this->delete('/m/{user_name}', 'UserFrosting\Sprinkle\Organisations\Controller\OrganisationMembersController:remove'); + + $this->post('/m/{user_name}/accept', 'UserFrosting\Sprinkle\Organisations\Controller\OrganisationMembersController:accept'); + $this->post('/m/{user_name}/reject', 'UserFrosting\Sprinkle\Organisations\Controller\OrganisationMembersController:reject'); })->add('authGuard')->add(new NoCache()); $app->group('/modals/organisations/o/{slug}/members', function () { $this->get('/confirm-leave', 'UserFrosting\Sprinkle\Organisations\Controller\OrganisationMembersController:getModalConfirmLeave'); $this->get('/confirm-cancel', 'UserFrosting\Sprinkle\Organisations\Controller\OrganisationMembersController:getModalConfirmCancel'); + $this->get('/confirm-remove', 'UserFrosting\Sprinkle\Organisations\Controller\OrganisationMembersController:getModalConfirmRemove'); + $this->get('/confirm-accept', 'UserFrosting\Sprinkle\Organisations\Controller\OrganisationMembersController:getModalConfirmAccept'); + $this->get('/confirm-reject', 'UserFrosting\Sprinkle\Organisations\Controller\OrganisationMembersController:getModalConfirmReject'); })->add('authGuard')->add(new NoCache()); \ No newline at end of file diff --git a/src/Controller/OrganisationMembersController.php b/src/Controller/OrganisationMembersController.php index 7a4736f..2575969 100644 --- a/src/Controller/OrganisationMembersController.php +++ b/src/Controller/OrganisationMembersController.php @@ -310,6 +310,83 @@ class OrganisationMembersController extends SimpleController return $response->withJson([], 200); } + /** + * Processes the request to remove a member from an organisation. + * + * Removes a member from the specified organisation. + * Before doing so, checks that: + * 1. The user has permission to remove the user from the organisation; + * 2. The submitted data is valid. + * This route requires authentication. + * + * Request type: DELETE + * + * @param Request $request + * @param Response $response + * @param array $args + * + * @throws NotFoundException If organisation is not found + * @throws ForbiddenException If user is not authorized to access page + * @throws BadRequestException + */ + public function remove(Request $request, Response $response, $args) + { + /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ + $authorizer = $this->ci->authorizer; + + /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ + $currentUser = $this->ci->currentUser; + + /** @var \UserFrosting\Sprinkle\Core\Alert\AlertStream $ms */ + $ms = $this->ci->alerts; + + + $organisation = $this->getOrganisationFromParams($args); + $user = $this->getUserFromParams($args); + + // If the organisation/user doesn't exist, return 404 + if (!$organisation || !$user) { + throw new NotFoundException(); + } + + // Access-controlled page + if (!$authorizer->checkAccess($currentUser, 'update_organisation_field', [ + 'organisation' => $organisation, + 'fields' => [ 'members' ], + ])) { + throw new ForbiddenException(); + } + + // Check if the user is a member of the organisation, pending or no relation at all + $memberCheck = $organisation->members()->where('user_id', $currentUser->id)->withPivot('flag_approved')->first(); + $adminCheck = $organisation->administrators()->where('user_id', $currentUser->id)->withPivot('flag_approved')->first(); + if (!($memberCheck && $memberCheck->pivot->flag_approved) && !($adminCheck && $adminCheck->pivot->flag_approved)) { + $ms->addMessageTranslated('danger', 'ORGANISATION.MEMBER.NOT_FOUND', [ + 'name' => $organisation->name, + 'user_name' => $user->user_name + ]); + return $response->withJson([], 400); + } + + // Begin transaction - DB will be rolled back if an exception occurs + Capsule::transaction(function () use ($organisation, $user, $currentUser) { + $user->organisations(true)->detach($organisation->id); + + // Create activity record + $this->ci->userActivityLogger->info("User {$currentUser->user_name} removed user {$user->user_name} from organisation {$organisation->name}.", [ + 'type' => 'organisation_member_remove', + 'user_id' => $currentUser->id, + ]); + }); + + $ms->addMessageTranslated('success', 'ORGANISATION.MEMBER.REMOVE_SUCCESSFUL', [ + 'name' => $organisation->ame, + 'user_name' => $user->user_name, + ]); + + return $response->withJson([], 200); + } + /** * Accepts a request to join organisation. @@ -642,7 +719,9 @@ class OrganisationMembersController extends SimpleController $sprunje = $classMapper->createInstance('user_sprunje', $classMapper, $params); $sprunje->extendQuery(function ($query) use ($classMapper, $organisation) { - return $query->where('organisation_id', $organisation->id); + return $query + ->where('organisation_id', $organisation->id) + ->addSelect('organisation_members.flag_approved AS membership_approved'); }); // Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content. @@ -740,6 +819,152 @@ class OrganisationMembersController extends SimpleController ]); } + /** + * Get remove member confirmation modal. + * + * @param Request $request + * @param Response $response + * @param array $args + * + * @throws NotFoundException If organisation/member is not found + * @throws ForbiddenException If user is not authorized to access page + * @throws BadRequestException + */ + public function getModalConfirmRemove(Request $request, Response $response, $args) + { + /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ + $authorizer = $this->ci->authorizer; + + /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ + $currentUser = $this->ci->currentUser; + + + // GET parameters + $params = $request->getQueryParams(); + + $organisation = $this->getOrganisationFromParams($params); + + $user = $this->getUserFromParams($params); + + // Check organisation & user exists + if (!$organisation || !$user) { + throw new NotFoundException(); + } + + + // Access-controlled page + if (!$authorizer->checkAccess($currentUser, 'update_organisation_field', [ + 'organisation' => $organisation, + 'fields' => [ 'members' ], + ])) { + throw new ForbiddenException(); + } + + return $this->ci->view->render($response, 'modals/confirm-remove-organisation-member.html.twig', [ + 'organisation' => $organisation, + 'user' => $user, + 'form' => [ + 'action' => "api/organisations/o/{$organisation->slug}/members/m/{$user->user_name}", + ], + ]); + } + + /** + * Get accept member confirmation modal. + * + * @param Request $request + * @param Response $response + * @param array $args + * + * @throws NotFoundException If organisation/member is not found + * @throws ForbiddenException If user is not authorized to access page + * @throws BadRequestException + */ + public function getModalConfirmAccept(Request $request, Response $response, $args) + { + /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ + $authorizer = $this->ci->authorizer; + + /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ + $currentUser = $this->ci->currentUser; + + + // GET parameters + $params = $request->getQueryParams(); + + $organisation = $this->getOrganisationFromParams($params); + + $user = $this->getUserFromParams($params); + + // Check organisation & user exists + if (!$organisation || !$user) { + throw new NotFoundException(); + } + + // Access-controlled page + if (!$authorizer->checkAccess($currentUser, 'accept_organisation_join_request', [ + 'organisation' => $organisation, + ])) { + throw new ForbiddenException(); + } + + return $this->ci->view->render($response, 'modals/confirm-accept-organisation-join-request.html.twig', [ + 'organisation' => $organisation, + 'user' => $user, + 'form' => [ + 'action' => "api/organisations/o/{$organisation->slug}/members/m/{$user->user_name}/accept", + ], + ]); + } + + /** + * Get reject member confirmation modal. + * + * @param Request $request + * @param Response $response + * @param array $args + * + * @throws NotFoundException If organisation/member is not found + * @throws ForbiddenException If user is not authorized to access page + * @throws BadRequestException + */ + public function getModalConfirmReject(Request $request, Response $response, $args) + { + /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ + $authorizer = $this->ci->authorizer; + + /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ + $currentUser = $this->ci->currentUser; + + + // GET parameters + $params = $request->getQueryParams(); + + $organisation = $this->getOrganisationFromParams($params); + + $user = $this->getUserFromParams($params); + + // Check organisation & user exists + if (!$organisation || !$user) { + throw new NotFoundException(); + } + + // Access-controlled page + if (!$authorizer->checkAccess($currentUser, 'accept_organisation_join_request', [ + 'organisation' => $organisation, + ])) { + throw new ForbiddenException(); + } + + return $this->ci->view->render($response, 'modals/confirm-reject-organisation-join-request.html.twig', [ + 'organisation' => $organisation, + 'user' => $user, + 'form' => [ + 'action' => "api/organisations/o/{$organisation->slug}/members/m/{$user->user_name}/reject", + ], + ]); + } + /** * Send approval email for specified organisation and confirmation to user. diff --git a/src/Database/Models/Organisation.php b/src/Database/Models/Organisation.php index 94680f5..ab6c345 100644 --- a/src/Database/Models/Organisation.php +++ b/src/Database/Models/Organisation.php @@ -107,14 +107,19 @@ class Organisation extends Model implements OrganisationInterface, TokenOwnerInt /** * Get a list of members within this organisation. */ - public function members() + public function members($withAdmins = false) { /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ $classMapper = static::$ci->classMapper; - return $this - ->belongsToMany($classMapper->getClassMapping('user'), 'organisation_members', 'organisation_id', 'user_id') - ->where('flag_admin', false); + $query = $this + ->belongsToMany($classMapper->getClassMapping('user'), 'organisation_members', 'organisation_id', 'user_id'); + + if ($withAdmins !== true) { + $query = $query->where('flag_admin', false); + } + + return $query; } /** diff --git a/src/Database/Seeds/OrganisationPermissions.php b/src/Database/Seeds/OrganisationPermissions.php index 57cbd0a..0c4e742 100644 --- a/src/Database/Seeds/OrganisationPermissions.php +++ b/src/Database/Seeds/OrganisationPermissions.php @@ -167,6 +167,19 @@ class OrganisationPermissions extends BaseSeed 'conditions' => 'always()', 'description' => 'View a page containing a list of deleted organisations.', ]), + + 'update_org_user_field' => new Permission([ + 'slug' => 'update_user_field', + 'name' => 'Edit organisation member', + 'conditions' => "can_admin_via_orgs(self.id, user.id) && subset(fields,['name','email','locale','password','phone_number'])", + 'description' => 'Edit users who are in an organisation they are a member of.', + ]), + 'view_org_user_field' => new Permission([ + 'slug' => 'view_user_field', + 'name' => 'View organisation member', + 'conditions' => "similar_orgs(self.id, user.id) && in(property,['user_name','name','locale','email','phone_number','activities'])", + 'description' => 'View certain properties of any user in their organisation.', + ]), ]; } @@ -245,6 +258,8 @@ class OrganisationPermissions extends BaseSeed $permissions['leave_organisation']->id, $permissions['register_organisation']->id, $permissions['accept_organisation_join_request']->id, + $permissions['update_org_user_field']->id, + $permissions['view_org_user_field']->id, ]); } } diff --git a/src/ServicesProvider/ServicesProvider.php b/src/ServicesProvider/ServicesProvider.php index 7741ab5..6dadf35 100644 --- a/src/ServicesProvider/ServicesProvider.php +++ b/src/ServicesProvider/ServicesProvider.php @@ -18,6 +18,7 @@ use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use UserFrosting\Sprinkle\Core\Log\MixedFormatter; use UserFrosting\Sprinkle\Organisations\Database\Models\Interfaces\OrganisationInterface; +use UserFrosting\Sprinkle\Organisations\Database\Models\User; use UserFrosting\Sprinkle\Organisations\Twig\OrganisationsExtension; use UserFrosting\Sprinkle\Organisations\Repository\OrganisationApprovalRepository; use UserFrosting\Sprinkle\Organisations\Repository\OrganisationMembershipApprovalRepository; @@ -96,6 +97,26 @@ class ServicesProvider ->count() > 0; }); + /* + * Check if $admin_id can modify $user_id via any of their joint organisations + * + * @param int $admin_id the id of the admin user (normally currentUser->id). + * @param int $user_id the id of the target user. + * @return bool true if $admin_id is an administrator of an organisation with $user_id in. + */ + $new_authorizer->addCallback('can_admin_via_orgs', function ($admin_id, $user_id) { + $admin = User::findInt($admin_id); + $user = User::findInt($user_id); + + foreach($admin->adminForOrganisations()->get() as $org) { + if ($org->members(true)->where('user_id', $user_id)->count() > 0) { + return true; + } + } + + return false; + }); + return $new_authorizer; }); diff --git a/templates/modals/confirm-accept-organisation-join-request.html.twig b/templates/modals/confirm-accept-organisation-join-request.html.twig new file mode 100644 index 0000000..72b75ea --- /dev/null +++ b/templates/modals/confirm-accept-organisation-join-request.html.twig @@ -0,0 +1,20 @@ +{% extends "modals/modal.html.twig" %} + +{% block modal_title %}{{translate("ORGANISATION.JOIN_REQUEST.ACCEPT")}}{% endblock %} + +{% block modal_body %} +
+ {% include "forms/csrf.html.twig" %} +
+
+

+ {{translate("ORGANISATION.JOIN_REQUEST.ACCEPT_CONFIRM", {name: organisation.name, agent_name: agent.name})}}
+ {{translate("ORGANISATION.JOIN_REQUEST.ACCEPT_CONFIRM_EXTRA", {name: organisation.name, agent_name: agent.name})}} +

+
+
+ + +
+
+{% endblock %} diff --git a/templates/modals/confirm-reject-organisation-join-request.html.twig b/templates/modals/confirm-reject-organisation-join-request.html.twig new file mode 100644 index 0000000..0cb7460 --- /dev/null +++ b/templates/modals/confirm-reject-organisation-join-request.html.twig @@ -0,0 +1,20 @@ +{% extends "modals/modal.html.twig" %} + +{% block modal_title %}{{translate("ORGANISATION.JOIN_REQUEST.REJECT")}}{% endblock %} + +{% block modal_body %} +
+ {% include "forms/csrf.html.twig" %} +
+
+

+ {{translate("ORGANISATION.JOIN_REQUEST.REJECT_CONFIRM", {name: organisation.name, agent_name: agent.name})}}
+ {{translate("ORGANISATION.JOIN_REQUEST.REJECT_CONFIRM_EXTRA", {name: organisation.name, agent_name: agent.name})}} +

+
+
+ + +
+
+{% endblock %} diff --git a/templates/modals/confirm-remove-organisation-member.html.twig b/templates/modals/confirm-remove-organisation-member.html.twig new file mode 100644 index 0000000..b66d265 --- /dev/null +++ b/templates/modals/confirm-remove-organisation-member.html.twig @@ -0,0 +1,17 @@ +{% extends "modals/modal.html.twig" %} + +{% block modal_title %}{{translate("ORGANISATION.MEMBER.REMOVE")}}{% endblock %} + +{% block modal_body %} +
+ {% include "forms/csrf.html.twig" %} +
+
+

{{translate("ORGANISATION.MEMBER.REMOVE_CONFIRM", {user_name: user.user_name, name: organisation.name})}}
{{translate("ACTION_CANNOT_UNDONE")}}

+
+
+ + +
+
+{% endblock %} diff --git a/templates/pages/organisation.html.twig b/templates/pages/organisation.html.twig index 9555b26..106b9b0 100644 --- a/templates/pages/organisation.html.twig +++ b/templates/pages/organisation.html.twig @@ -121,7 +121,7 @@ {% include "tables/table-tool-menu.html.twig" %}
- {% include "tables/users.html.twig" with { + {% include "tables/organisation-members.html.twig" with { "table" : { "id" : "table-organisation-members" } diff --git a/templates/tables/organisation-members.html.twig b/templates/tables/organisation-members.html.twig new file mode 100644 index 0000000..b625e71 --- /dev/null +++ b/templates/tables/organisation-members.html.twig @@ -0,0 +1,116 @@ +{% extends "@blockier-templates/tables/users.html.twig" %} + +{% block table %} + + + + + + {% if 'last_activity' in table.columns %} + + {% endif %} + + {% if hasRole('site-admin') or hasRole('organisations-admin') or (isOrganisationAdmin(organisation)) -%} + + {% endif %} + + + + +
{{translate('USER')}} {{translate("ORGANISATION", 2)}}{{translate("ACTIVITY.LAST")}} {{translate("STATUS")}} {{translate("ACTIONS")}}
+{% endblock %} + + +{% block table_cell_template_status %} + +{% endblock %} + +{% block table_cell_template_actions %} +{% if hasRole('site-admin') or hasRole('organisations-admin') or (isOrganisationAdmin(organisation)) %} + +{% endif %} +{% endblock %} + +{% block table_cell_template_extra %} + {% block table_cell_template_organisations %} + + {% endblock %} +{% endblock %} +