Skip to content

Commit 6c94783

Browse files
committed
Finally fixes #649
1 parent 0d74ab4 commit 6c94783

12 files changed

Lines changed: 106 additions & 15 deletions

File tree

client/build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ source $NVM_DIR/nvm.sh
77
nvm use
88
yarn install --force
99
CI=true yarn test
10-
yarn lint ./src
10+
yarn lint
1111
yarn build

client/src/api/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ export function institutionAdminsbyRole(roleId) {
9999
return fetchJson(`/api/v1/users/institution-admins/${roleId}`, {}, {}, false);
100100
}
101101

102+
export function institutionAdmins() {
103+
return fetchJson("/api/v1/users/institutionAdmins", {}, {}, false);
104+
}
105+
102106
export function searchUsers(pagination = {}) {
103107
const queryPart = paginationQueryParams(pagination, {})
104108
return fetchJson(`/api/v1/users/search?${queryPart}`);

client/src/components/User.jsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {AUTHORITIES, highestAuthority} from "../utils/UserRole";
66
import I18n from "../locale/I18n";
77
import Logo from "./Logo";
88
import {Button, ButtonType, Card, CardType} from "@surfnet/sds";
9-
import {isEmpty} from "../utils/Utils";
9+
import {isEmpty, splitListSemantically} from "../utils/Utils";
1010
import {deriveRemoteApplicationAttributes, reduceApplicationFromUserRoles} from "../utils/Manage";
1111
import SearchIcon from "@surfnet/sds/icons/functional-icons/search.svg";
1212
import {MoreLessText} from "./MoreLessText";
@@ -17,7 +17,7 @@ import {deleteUser} from "../api";
1717
import {useNavigate} from "react-router-dom";
1818
import {useAppStore} from "../stores/AppStore";
1919

20-
export const User = ({user, other, config, currentUser}) => {
20+
export const User = ({user, other, config, currentUser, otherInstitutionAdmins}) => {
2121
const navigate = useNavigate();
2222
const {setFlash} = useAppStore(state => state);
2323
const searchRef = useRef();
@@ -155,6 +155,15 @@ export const User = ({user, other, config, currentUser}) => {
155155
onClick={() => doDeleteUser(true)}/>
156156
</div>
157157
}
158+
{user.institutionAdmin && <div>
159+
<p className="label">{isEmpty(otherInstitutionAdmins) ? I18n.t("users.onlyInstitutionAdmins") :
160+
I18n.t("users.institutionAdmins", )}</p>
161+
{!isEmpty(otherInstitutionAdmins) && <ul className="admins">
162+
{otherInstitutionAdmins.map((admin, index) => <li key={index}>
163+
<span>{`${admin.name} - ${admin.email}`}</span>
164+
</li>)}
165+
</ul>}
166+
</div>}
158167
<h3 className={"title span-row "}>{I18n.t("users.roles")}</h3>
159168
{(highestAuthority(user, false) === AUTHORITIES.GUEST && !other) &&
160169
<p className={"span-row"}

client/src/components/User.scss

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@ section.user {
1515

1616
}
1717

18+
p.label {
19+
font-weight: 600;
20+
margin: 20px 0 15px 0;
21+
}
22+
23+
ul.admins {
24+
list-style: circle;
25+
26+
li {
27+
margin-left: 20px;
28+
}
29+
30+
}
31+
1832
p.span-row {
1933
margin-bottom: 25px;
2034
}

client/src/locale/en.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ const en = {
9494
name_email: "Name / email",
9595
name: "Name",
9696
email: "Email",
97-
highestAuthority: "Role",
97+
highestAuthority: "Function",
9898
createdAt: "Created",
9999
schacHomeOrganization: "Institution",
100100
lastActivity: "Last activity",
@@ -129,7 +129,9 @@ const en = {
129129
expiryDays: "Expiry days",
130130
roleExpiryTooltip: "Sort on roles to see which roles will expire the soonest",
131131
deleteFlash: "User {{name}} has been deleted",
132-
deleteConfirmation: "Are you absolutely sure you want to delete user {{name}}? There is no undo button."
132+
deleteConfirmation: "Are you absolutely sure you want to delete user {{name}}? There is no undo button.",
133+
institutionAdmins: "Your fellow Institution administrators are:",
134+
onlyInstitutionAdmins: "You are the only Institution admin within your organisation",
133135
},
134136
role: {
135137
copyUrn: "Copy urn",

client/src/locale/nl.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ const nl = {
9494
name_email: "Naam / e-mail",
9595
name: "Naam",
9696
email: "E-mail",
97-
highestAuthority: "Rol",
97+
highestAuthority: "Functie",
9898
createdAt: "Aangemaakt op",
9999
schacHomeOrganization: "Instelling",
100100
lastActivity: "Laatst actief",
@@ -129,8 +129,9 @@ const nl = {
129129
expiryDays: "Verloopdagen",
130130
roleExpiryTooltip: "Sorteer op rollen, om te zien welke rol het eerst zal verlopen",
131131
deleteFlash: "Gebruiker {{name}} is verwijderd",
132-
deleteConfirmation: "Weet je heel zeker dat je gebruiker {{name}} wilt verwijderen? Er is geen undo functionaliteit."
133-
},
132+
deleteConfirmation: "Weet je heel zeker dat je gebruiker {{name}} wilt verwijderen? Er is geen undo functionaliteit.",
133+
institutionAdmins: "Je collega-instellingsbeheerders zijn:",
134+
onlyInstitutionAdmins: "Je bent de enige instellingsbeheerder binnen je organisatie", },
134135
role: {
135136
copyUrn: "Copy urn",
136137
userInfo: "{{nbr}} leden & verloopt {{period}}",

client/src/locale/pt.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,9 @@ const pt = {
128128
expiryDays: "Expiry days",
129129
roleExpiryTooltip: "Sort on roles to see which roles will expire the soonest",
130130
deleteFlash: "User {{name}} has been deleted",
131-
deleteConfirmation: "Are you absolutely sure you want to delete user {{name}}? There is no undo button."
131+
deleteConfirmation: "Are you absolutely sure you want to delete user {{name}}? There is no undo button.",
132+
institutionAdmins: "Your fellow Institution administrators are:",
133+
onlyInstitutionAdmins: "You are the only Institution admin within your organisation",
132134
},
133135
role: {
134136
copyUrn: "Copy urn",

client/src/pages/Profile.jsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, {useEffect, useState} from "react";
2-
import {other} from "../api";
2+
import {institutionAdmins, other} from "../api";
33
import I18n from "../locale/I18n";
44
import "./Profile.scss";
55
import {Loader} from "@surfnet/sds";
@@ -10,12 +10,16 @@ import {UnitHeader} from "../components/UnitHeader";
1010
import Logo from "@surfnet/sds/icons/functional-icons/id-1.svg";
1111
import {dateFromEpoch} from "../utils/Date";
1212
import {isEmpty} from "../utils/Utils";
13+
import {AUTHORITIES, highestAuthority} from "../utils/UserRole";
1314

1415
export const Profile = () => {
1516
const {id} = useParams();
1617
const {user: currentUser, config} = useAppStore(state => state);
1718
const [user, setUser] = useState(currentUser);
18-
const [loading, setLoading] = useState(!isEmpty(id));
19+
const [otherInstitutionAdmins,setOtherInstitutionAdmins] = useState([]);
20+
21+
const [loading, setLoading] = useState(!isEmpty(id) || user.institutionAdmin);
22+
1923
const navigate = useNavigate();
2024

2125
useEffect(() => {
@@ -26,6 +30,12 @@ export const Profile = () => {
2630
setLoading(false);
2731
})
2832
.catch(() => navigate("/404"))
33+
} else if (user.institutionAdmin) {
34+
institutionAdmins()
35+
.then(res => {
36+
setOtherInstitutionAdmins(res);
37+
setLoading(false);
38+
})
2939
}
3040

3141
}, [id, currentUser]);// eslint-disable-line react-hooks/exhaustive-deps
@@ -62,7 +72,11 @@ export const Profile = () => {
6272
})}</p>
6373
</UnitHeader>
6474
<div className="profile-container">
65-
<User user={user} other={!isEmpty(id)} config={config} currentUser={currentUser}/>
75+
<User user={user}
76+
other={!isEmpty(id)}
77+
otherInstitutionAdmins={otherInstitutionAdmins}
78+
config={config}
79+
currentUser={currentUser}/>
6680
</div>
6781
</div>);
6882
};

server/src/main/java/invite/api/UserController.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,17 @@ public ResponseEntity<User> me(@Parameter(hidden = true) User user) {
118118
return ResponseEntity.ok(user);
119119
}
120120

121+
@GetMapping("institutionAdmins")
122+
@Transactional(readOnly = true)
123+
public ResponseEntity<List<Map<String, String>>> institutionAdmins(@Parameter(hidden = true) User user) {
124+
LOG.debug(String.format("/institutionAdmins for user %s", user.getEduPersonPrincipalName()));
125+
126+
UserPermissions.assertAuthority(user, Authority.INSTITUTION_ADMIN);
127+
List<Map<String, String>> users = userRepository.findInstitutionAdminsPerOrganizationGUID(user.getId(), user.getOrganizationGUID());
128+
129+
return ResponseEntity.ok(users);
130+
}
131+
121132
@GetMapping("other/{id}")
122133
@Transactional(readOnly = true)
123134
public ResponseEntity<User> details(@PathVariable("id") Long id, @Parameter(hidden = true) User user) {

server/src/main/java/invite/repository/UserRepository.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,13 @@ SELECT distinct(u.id), u.email, u.name, u.schac_home_organization, u.created_at,
134134
nativeQuery = true)
135135
List<User> findInstitutionAdminsPerRole(Long roleId);
136136

137+
@Query(value = """
138+
SELECT u.name, u.email FROM users u
139+
WHERE u.id <> ?1 AND u.organization_guid = ?2 AND u.institution_admin = 1
140+
""",
141+
nativeQuery = true)
142+
List<Map<String, String>> findInstitutionAdminsPerOrganizationGUID(Long id, String organizationGUID);
143+
137144
@Override
138145
default String rewrite(String query, Sort sort) {
139146
Sort.Order authoritySort = sort.getOrderFor("authority");

0 commit comments

Comments
 (0)