django_airavata/static/common/js/components/SharedEntityEditor.vue (346 lines of code) (raw):
<template>
<div>
<b-form-group
v-if="!readonly"
label="Search for users/groups"
labelFor="user-groups-autocomplete"
>
<autocomplete-text-input
id="user-groups-autocomplete"
:suggestions="usersAndGroupsSuggestions"
@selected="suggestionSelected"
>
<template slot="suggestion" slot-scope="slotProps">
<span v-if="slotProps.suggestion.type == 'group'">
<i class="fa fa-users"></i> {{ slotProps.suggestion.name }}
</span>
<span v-if="slotProps.suggestion.type == 'user'">
<i class="fa fa-user"></i>
{{ slotProps.suggestion.user.firstName }}
{{ slotProps.suggestion.user.lastName }} ({{
slotProps.suggestion.user.userId
}}) - {{ slotProps.suggestion.user.email }}
</span>
</template>
</autocomplete-text-input>
</b-form-group>
<h5 v-if="totalCount > 0">
<slot name="permissions-header">Currently Shared With</slot>
</h5>
<b-table
v-if="usersCount > 0"
id="modal-user-table"
hover
:items="sortedUserPermissions"
:fields="userFields"
>
<template slot="cell(name)" slot-scope="data">
<span
:title="data.item.user.userId"
:class="userDataClasses"
v-if="!isPermissionReadOnly(data.item.permissionType)"
>{{ data.item.user.firstName }} {{ data.item.user.lastName }}</span
>
<span v-else class="text-muted font-italic"
>{{ data.item.user.firstName }} {{ data.item.user.lastName }}</span
>
</template>
<template slot="cell(email)" slot-scope="data">
<span
:class="userDataClasses"
v-if="!isPermissionReadOnly(data.item.permissionType)"
>{{ data.item.user.email }}</span
>
<span v-else class="text-muted font-italic">{{
data.item.user.email
}}</span>
</template>
<template slot="cell(permission)" slot-scope="data">
<b-form-select
v-if="!isPermissionReadOnly(data.item.permissionType)"
v-model="data.item.permissionType"
:options="permissionOptions"
/>
<span
v-else
class="text-uppercase text-muted font-italic"
:class="userDataClasses"
>{{ data.item.permissionType.name }}</span
>
</template>
<template slot="cell(remove)" slot-scope="data">
<b-link
v-if="!isPermissionReadOnly(data.item.permissionType)"
@click="removeUser(data.item.user)"
>
<span class="fa fa-trash"></span>
</b-link>
</template>
</b-table>
<b-table
v-if="groupsCount > 0"
id="modal-group-table"
hover
:items="sortedGroupPermissions"
:fields="groupFields"
>
<template slot="cell(name)" slot-scope="data">
<span
v-if="editingAllowed(data.item.group, data.item.permissionType)"
>{{ data.item.group.name }}</span
>
<span v-else class="text-muted font-italic">{{
data.item.group.name
}}</span>
</template>
<template slot="cell(permission)" slot-scope="data">
<b-form-select
v-if="editingAllowed(data.item.group, data.item.permissionType)"
v-model="data.item.permissionType"
:options="permissionOptions"
/>
<span v-else class="text-muted font-italic">{{
data.item.permissionType.name
}}</span>
</template>
<template slot="cell(remove)" slot-scope="data">
<b-link
v-if="editingAllowed(data.item.group, data.item.permissionType)"
@click="removeGroup(data.item.group)"
>
<span class="fa fa-trash"></span>
</b-link>
</template>
</b-table>
</div>
</template>
<script>
import { models, utils, session } from "django-airavata-api";
import AutocompleteTextInput from "./AutocompleteTextInput.vue";
import VModelMixin from "../mixins/VModelMixin";
export default {
name: "shared-entity-editor",
mixins: [VModelMixin],
props: {
value: {
type: models.SharedEntity,
},
users: {
type: Array,
required: true,
},
groups: {
type: Array,
required: true,
},
disallowEditingAdminGroups: {
type: Boolean,
default: true,
},
readonly: {
type: Boolean,
default: false,
},
},
components: {
AutocompleteTextInput,
},
computed: {
userFields: function () {
return [
{ key: "name", label: "User Name", class: "text-truncate" },
{ key: "email", label: "Email", class: "text-truncate" },
{ key: "permission", label: "Permission" },
{ key: "remove", label: "Remove" },
];
},
groupFields: function () {
return [
{ key: "name", label: "Group Name" },
{ key: "permission", label: "Permission" },
{ key: "remove", label: "Remove" },
];
},
usersCount: function () {
return this.data && this.data.userPermissions
? this.data.userPermissions.length
: 0;
},
sortedUserPermissions: function () {
const userPermsCopy = this.data.userPermissions
? this.data.userPermissions.slice()
: [];
const sortedUserPerms = utils.StringUtils.sortIgnoreCase(
userPermsCopy,
(userPerm) => userPerm.user.lastName + ", " + userPerm.user.firstName
);
// When in readonly mode, if the current owner isn't the owner, display
// the user with the OWNER permission
if (this.readonly && !this.data.isOwner) {
sortedUserPerms.push(
new models.UserPermission({
user: this.data.owner,
permissionType: models.ResourcePermissionType.OWNER,
})
);
}
return sortedUserPerms;
},
userDataClasses() {
return {
"text-muted": this.readonly,
"font-italic": this.readonly,
};
},
filteredGroupPermissions: function () {
return this.data && this.data.groupPermissions
? this.data.groupPermissions
: [];
},
sortedGroupPermissions: function () {
const groupPermsCopy = this.filteredGroupPermissions.slice();
// Sort by name, then admin groups should come last if editing is disallowed
utils.StringUtils.sortIgnoreCase(groupPermsCopy, (g) => g.group.name);
if (this.disallowEditingAdminGroups) {
groupPermsCopy.sort((a, b) => {
if (a.group.isAdminGroup && !b.group.isAdminGroup) {
return 1;
}
});
}
return groupPermsCopy;
},
groupsCount: function () {
return this.filteredGroupPermissions.length;
},
totalCount: function () {
return this.usersCount + this.groupsCount;
},
permissionOptions: function () {
var options = [
models.ResourcePermissionType.READ,
models.ResourcePermissionType.WRITE,
];
// manage_sharing permission is visible only if the user is the owner or it is a new entity and owner is not defined
if (this.data.isOwner || this.data.isOwner === null) {
options.push(models.ResourcePermissionType.MANAGE_SHARING);
}
return options.map((perm) => {
return {
value: perm,
text: perm.name,
};
});
},
groupSuggestions: function () {
// filter out already selected groups
const currentGroupIds = this.filteredGroupPermissions.map(
(groupPerm) => groupPerm.group.id
);
return this.groups
.filter((group) => currentGroupIds.indexOf(group.id) < 0)
.filter((group) => {
// Filter out admin groups from options
if (this.disallowEditingAdminGroups) {
return !group.isAdminGroup;
} else {
return true;
}
})
.map((group) => {
return {
id: group.id,
name: group.name,
type: "group",
};
});
},
userSuggestions: function () {
// filter out already selected users
const currentUserIds = this.data.userPermissions
? this.data.userPermissions.map(
(userPerm) => userPerm.user.airavataInternalUserId
)
: [];
return this.users
.filter(
(user) => currentUserIds.indexOf(user.airavataInternalUserId) < 0
)
.filter(
(user) =>
user.airavataInternalUserId !==
session.Session.airavataInternalUserId
)
.map((user) => {
return {
id: user.airavataInternalUserId,
name:
user.firstName +
" " +
user.lastName +
" (" +
user.userId +
") " +
user.email,
user: user,
type: "user",
};
});
},
usersAndGroupsSuggestions: function () {
return this.userSuggestions.concat(this.groupSuggestions);
},
},
methods: {
removeUser: function (user) {
this.data.removeUser(user);
},
removeGroup: function (group) {
this.data.removeGroup(group);
},
suggestionSelected: function (suggestion) {
if (suggestion.type === "group") {
const group = this.groups.find((group) => group.id === suggestion.id);
this.data.addGroup({ group });
} else if (suggestion.type === "user") {
const user = this.users.find(
(user) => user.airavataInternalUserId === suggestion.id
);
this.data.addUser(user);
}
},
/**
* For some entity types the backend automatically shares the entity with
* admin users and doesn't allow editing or removing those admin groups.
* For that reason the disallowEditingAdminGroups property was added and
* when it is true editing of the "Admins" and "Read Only Admins" groups
* should not be allowed.
*/
editingAllowed(group, permission) {
return (
!this.readonly &&
(!this.disallowEditingAdminGroups || !group.isAdminGroup) &&
!(
!this.data.isOwner &&
permission === models.ResourcePermissionType.MANAGE_SHARING
)
);
},
isPermissionReadOnly: function (permission) {
// if it is a new entity, it will not be readonly
if (this.data.isOwner == null) {
return false;
}
return (
!this.data.isOwner &&
permission === models.ResourcePermissionType.MANAGE_SHARING
);
},
},
};
</script>
<style scoped>
#modal-user-table {
table-layout: fixed;
}
</style>