Skip to content

Commit

Permalink
Allow change of ali (#4552)
Browse files Browse the repository at this point in the history
* Allow change of ali

* update tests

* fix typo

* chain user updates

* fix test

* precommit

* update tests

---------

Co-authored-by: Ross Perry <[email protected]>
Co-authored-by: Katherine Fleming <[email protected]>
  • Loading branch information
3 people authored Mar 6, 2024
1 parent 6305fbb commit 1348d7c
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 23 deletions.
102 changes: 87 additions & 15 deletions seed/static/seed/js/controllers/members_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,101 @@ angular.module('BE.seed.controller.members', []).controller('members_controller'
'user_profile_payload',
'access_level_tree',
'urls',
'Notification',
// eslint-disable-next-line func-names
function ($scope, $uibModal, users_payload, organization_payload, auth_payload, auth_service, organization_service, user_profile_payload, access_level_tree, urls) {
function ($scope, $uibModal, users_payload, organization_payload, auth_payload, auth_service, organization_service, user_profile_payload, access_level_tree, urls, Notification) {
$scope.ownerRoles = ['owner', 'member', 'viewer'];
$scope.memberRoles = ['member', 'viewer'];
$scope.users = users_payload.users;
$scope.org = organization_payload.organization;
$scope.filter_params = {};
$scope.auth = auth_payload.auth;
$scope.user_profile = user_profile_payload;
$scope.access_level_names = access_level_tree.access_level_names;
$scope.user_id_being_edited = null;
$scope.user_edits = {};

/* Build out access_level_instances_by_depth recursively */
const access_level_instances_by_depth = {};
const calculate_access_level_instances_by_depth = (tree, depth = 1) => {
if (tree === undefined) return;
if (access_level_instances_by_depth[depth] === undefined) access_level_instances_by_depth[depth] = [];
for (const ali of tree) {
access_level_instances_by_depth[depth].push({ id: ali.id, name: ali.data.name });
calculate_access_level_instances_by_depth(ali.children, depth + 1);
}
};
calculate_access_level_instances_by_depth(access_level_tree.access_level_tree, 0);

// Result of clicking "edit"
$scope.begin_user_edits = (user) => {
// set edited user and default edits
$scope.user_id_being_edited = user.user_id;
$scope.user_edits = {
"access_level_instance": {
"id": user.access_level_instance_id,
"name": user.access_level_instance_name
},
"access_level": user.access_level,
"role": user.role,
};

// the the possible access_level_instances at that level
access_level_idx = $scope.access_level_names.findIndex(x => x == $scope.user_edits["access_level"]);
$scope.access_level_instances = access_level_instances_by_depth[access_level_idx];
}

// Result of pick a new al. sets ali options.
$scope.change_access_level_instance_options = () => {
access_level_idx = $scope.access_level_names.findIndex(x => x == $scope.user_edits["access_level"]);
$scope.access_level_instances = access_level_instances_by_depth[access_level_idx];
$scope.user_edits["access_level_instance"] = null;
}

// user user edits
$scope.save_user_edits = () => {
if ($scope.user_edits["access_level_instance"] === null){
Notification.error('Must select an ali.');
return;
}
if($scope.user_edits.role == "owner" && $scope.user_edits.access_level != $scope.access_level_names[0]){
Notification.error('Owners must be in the root.');
return;
}

// update user
user = $scope.users.find(x => x.user_id == $scope.user_id_being_edited);
$scope.update_user(user, $scope.user_edits)

user.role = $scope.user_edits.role
user.access_level_instance_id = $scope.user_edits.access_level_instance.id
user.access_level_instance_name = $scope.user_edits.access_level_instance.name
user.access_level = $scope.user_edits.access_level

// reset user edits
$scope.user_id_being_edited = null;
$scope.user_edits = {};
}
/**
* update_user: updates a users role and access level instance
*/
$scope.update_user = (user, user_edits) => {
// 1. update role
// 2. update ali
organization_service.update_role(user.user_id, $scope.org.id, user_edits.role)
.then(() => {
refreshRoleStatus();
return organization_service.update_ali(user.user_id, $scope.org.id, user_edits.access_level_instance.id)
})
.catch((data) => {
$scope.$emit('app_error', data);
})
}

$scope.cancel_user_edits = () => {
$scope.user_id_being_edited = null;
$scope.user_edits = {};
}

/**
* remove_member: removes a user from the org
Expand All @@ -42,20 +128,6 @@ angular.module('BE.seed.controller.members', []).controller('members_controller'
});
};

/**
* saves the changed role for the user
* @param {obj} user
*/
$scope.update_role = (user) => {
organization_service
.update_role(user.user_id, $scope.org.id, user.role)
.then((data) => {
refreshRoleStatus();
})
.catch((data) => {
$scope.$emit('app_error', data);
});
};

/**
* new_member_modal open an AngularUI modal to add/invite a new member
Expand Down
12 changes: 12 additions & 0 deletions seed/static/seed/js/services/organization_service.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@ angular.module('BE.seed.service.organization', []).factory('organization_service
)
.then((response) => response.data);

organization_factory.update_ali = (user_id, org_id, ali_id) => $http
.put(
`/api/v3/users/${user_id}/access_level_instance/`,
{
"access_level_instance_id": ali_id,
},
{
params: { organization_id: org_id }
}
)
.then((response) => response.data);

/**
* saves the organization settings
* @param {obj} org an organization with fields to share between sub-orgs
Expand Down
59 changes: 52 additions & 7 deletions seed/static/seed/partials/members.html
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,66 @@ <h2><i class="fa-solid fa-user"></i> {$:: 'Members' | translate $}</h2>
<tr ng-repeat="u in users | filter:filter_params:strict">
<td>{$:: u.name $}</td>
<td>{$:: u.email $}</td>
<td>{$:: u.access_level $}</td>
<td>{$:: u.access_level_instance_name $}</td>
<td>
<div class="row">
<div ng-if="user_id_being_edited != u.user_id">{$:: u.access_level $}</div>
<select
ng-if="user_id_being_edited == u.user_id"
class="form-control"
ng-model="user_edits['access_level']"
ng-options="potential_level_name for potential_level_name in access_level_names"
ng-change="change_access_level_instance_options()">
</select>
</td>
<td>
<div ng-if="user_id_being_edited != u.user_id">{$:: u.access_level_instance_name $}</div>
<select
ng-if="user_id_being_edited == u.user_id"
class="form-control"
ng-model="user_edits['access_level_instance']"
ng-options="potential_access_level_instance as potential_access_level_instance.name for potential_access_level_instance in access_level_instances track by potential_access_level_instance.id">
</select>
<td>
<div ng-if="user_id_being_edited != u.user_id">{$:: u.role | translate $}</div>
<div class="row" ng-if="user_id_being_edited == u.user_id">
<div class="form-group">
<div class="col-sm-12" ng-switch="can_edit(u)">
<select ng-switch-when="owner" class="form-control input-sm hide_transition" ng-model="u.role" ng-options="role for role in ::ownerRoles" ng-change="update_role(u)"></select>
<select ng-switch-when="member" class="form-control input-sm hide_transition" ng-model="u.role" ng-options="role for role in ::memberRoles" ng-change="update_role(u)"></select>
<span ng-switch-default style="padding-left: 15px">{$:: u.role | translate $}</span>
<select ng-switch-when="owner" class="form-control input-sm hide_transition" ng-model="user_edits.role" ng-options="role for role in ::ownerRoles"></select>
<select ng-switch-when="member" class="form-control input-sm hide_transition" ng-model="user_edits.role" ng-options="role for role in ::memberRoles"></select>
<span ng-switch-default style="padding-left: 15px">{$:: u.role | translate $}</span>
</div>
</div>
</div>
</td>
<td ng-if="auth.can_remove_member">
<a ng-if="!only_one_owner || u.email !== user_profile.email" ng-click="remove_member(u)" translate>Remove</a>
<button
class="btn btn-default"
ng-click="begin_user_edits(u)"
ng-hide="user_id_being_edited == u.user_id"
ng-disabled="user_id_being_edited !== null"
translate>
Edit
</button>
<button
class="btn btn-primary"
ng-click="save_user_edits()"
ng-if="user_id_being_edited == u.user_id"
translate>
Save
</button>
<button
class="btn btn-default"
ng-click="cancel_user_edits()"
ng-if="user_id_being_edited == u.user_id"
translate>
Cancel
</button>
<button
class="btn btn-danger"
ng-if="(!only_one_owner || u.email !== user_profile.email) && user_id_being_edited == null"
ng-click="remove_member(u)"
translate>
Remove
</button>
</td>
</tr>
</tbody>
Expand Down
2 changes: 1 addition & 1 deletion seed/static/seed/tests/members_controller.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ describe('controller: members_controller', () => {

// act
ctrl_scope.$digest();
ctrl_scope.update_role({ user_id: 2, role: 'viewer' });
ctrl_scope.update_user({ user_id: 2 }, { role: 'viewer' });

// assertions
expect(mock_organization_service.update_role).toHaveBeenCalledWith(2, 4, 'viewer');
Expand Down
26 changes: 26 additions & 0 deletions seed/tests/test_account_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,32 @@ def test_update_role(self):
})
self.assertEqual(ou.role_level, ROLE_MEMBER)

def test_update_access_level_instance(self):
# Setup
u = User.objects.create(username='[email protected]', email='[email protected]')
self.org.add_member(u, role=ROLE_VIEWER, access_level_instance_id=self.org.root.id)
org_user = OrganizationUser.objects.get(user=u)

self.org.access_level_names = ["root", "child"]
self.org.save()
child_ali = self.org.add_new_access_level_instance(self.org.root.id, "child")

# Action
resp = self.client.put(
reverse_lazy("api:v3:user-access-level-instance", args=[u.id]) + '?organization_id=' + str(
self.org.id),
data=json.dumps(
{
'access_level_instance_id': child_ali.id
}
),
content_type='application/json',
)

# Assertion
assert resp.status_code == 200
assert OrganizationUser.objects.get(pk=org_user.pk).access_level_instance_id == child_ali.id

def test_allowed_to_update_role_if_not_last_owner(self):
u = User.objects.create(username='[email protected]', email='[email protected]')
self.org.add_member(u, role=ROLE_OWNER, access_level_instance_id=self.org.root.id)
Expand Down
1 change: 1 addition & 0 deletions seed/views/v3/organization_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def list(self, request, organization_pk):
'number_of_orgs': user_orgs,
'user_id': user.pk,
'role': get_js_role(u.role_level),
'access_level_instance_id': u.access_level_instance.id,
'access_level_instance_name': u.access_level_instance.name,
'access_level': org.access_level_names[u.access_level_instance.depth - 1],
})
Expand Down
38 changes: 38 additions & 0 deletions seed/views/v3/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,44 @@ def role(self, request, pk=None):

return JsonResponse({'status': 'success'})

@api_endpoint_class
@ajax_request_class
@has_perm_class('requires_owner')
@action(detail=True, methods=['PUT'])
def access_level_instance(self, request, pk=None):
user_id = int(pk)
organization_id = self.get_organization(request)

# get user
try:
user = OrganizationUser.objects.get(user_id=user_id, organization_id=organization_id)
except OrganizationUser.DoesNotExist:
return JsonResponse({
'status': 'error',
'message': 'No such user'
}, status=status.HTTP_400_BAD_REQUEST)

# get ali
access_level_instance_id = request.data.get("access_level_instance_id")
if access_level_instance_id is None:
return JsonResponse({
'status': 'error',
'message': 'Must be an `access_level_instance_id`'
}, status=status.HTTP_400_BAD_REQUEST)
try:
access_level_instance = AccessLevelInstance.objects.get(organization_id=organization_id, id=access_level_instance_id)
except AccessLevelInstance.DoesNotExist:
return JsonResponse({
'status': 'error',
'message': 'No such access_level_instance'
}, status=status.HTTP_400_BAD_REQUEST)

# set user ali
user.access_level_instance = access_level_instance
user.save()

return JsonResponse({'status': 'success'})

@swagger_auto_schema(
responses={
200: user_response_schema,
Expand Down

0 comments on commit 1348d7c

Please sign in to comment.