blob: aac88698875ce911fe254c5c9f9060d981931110 [file] [log] [blame]
<template>
<div class="share-button btn-container">
<b-button
:variant="'outline-primary'"
:title="title"
:disabled="!shareButtonEnabled"
@click="openSharingSettingsModal"
>
Share
<b-badge>{{ totalCount }}</b-badge>
</b-button>
<b-modal
class="modal-share-settings"
title="Sharing Settings"
ref="sharingSettingsModal"
ok-title="Save"
@ok="saveSharedEntity"
@cancel="cancelEditSharedEntity"
no-close-on-esc
no-close-on-backdrop
hide-header-close
@show="showSharingSettingsModal"
>
<shared-entity-editor
v-if="localSharedEntity && users && groups"
v-model="localSharedEntity"
:users="users"
:groups="groups"
:disallow-editing-admin-groups="disallowEditingAdminGroups"
/>
<!-- Only show parent entity permissions for new entities -->
<template v-if="hasParentSharedEntityPermissions">
<shared-entity-editor
v-if="parentSharedEntity && users && groups"
v-model="parentSharedEntity"
:users="users"
:groups="groups"
:readonly="true"
class="mt-4"
>
<span slot="permissions-header"
>Inherited {{ parentEntityLabel }} Permissions
<!-- <small class="text-muted" v-if="parentEntityOwner">Owned by {{parentEntityOwner.firstName}} {{parentEntityOwner.lastName}} ({{parentEntityOwner.email}})</small> -->
</span>
</shared-entity-editor>
</template>
</b-modal>
</div>
</template>
<script>
import { models, services } from "django-airavata-api";
import SharedEntityEditor from "./SharedEntityEditor.vue";
export default {
name: "share-button",
props: {
entityId: String,
parentEntityId: String,
parentEntityLabel: {
type: String,
default: "Parent",
},
sharedEntity: models.SharedEntity,
autoAddDefaultGatewayUsersGroup: {
type: Boolean,
default: true,
},
autoAddAdminGroups: {
type: Boolean,
default: true,
},
disallowEditingAdminGroups: {
type: Boolean,
default: true,
},
},
components: {
SharedEntityEditor,
},
data: function () {
return {
localSharedEntity: null,
parentSharedEntity: null,
sharedEntityCopy: null,
defaultGatewayUsersGroup: null,
adminsGroup: null,
readOnlyAdminsGroup: null,
users: null,
groups: null,
};
},
computed: {
title: function () {
return (
"Shared with " +
this.groupsCount +
" groups" +
(this.groupsCount > 0 ? " (" + this.groupNames.join(", ") + ")" : "") +
" and " +
this.usersCount +
" users" +
(this.usersCount > 0 ? " (" + this.userNames.join(", ") + ")" : "")
);
},
usersCount: function () {
return this.combinedUsers.length;
},
userNames: function () {
return this.combinedUsers.map((u) => u.firstName + " " + u.lastName);
},
combinedUsers() {
const users = [];
if (this.localSharedEntity && this.localSharedEntity.userPermissions) {
users.push(
...this.localSharedEntity.userPermissions.map((up) => up.user)
);
}
if (this.parentSharedEntity && this.parentSharedEntity.userPermissions) {
users.push(
...this.parentSharedEntity.userPermissions.map((up) => up.user)
);
if (this.parentEntityOwner) {
users.push(this.parentEntityOwner);
}
}
return users;
},
filteredGroupPermissions: function () {
if (this.localSharedEntity && this.localSharedEntity.groupPermissions) {
return this.localSharedEntity.groupPermissions;
} else {
return [];
}
},
combinedGroups() {
const groups = [];
groups.push(...this.filteredGroupPermissions.map((gp) => gp.group));
if (this.parentSharedEntity && this.parentSharedEntity.groupPermissions) {
groups.push(
...this.parentSharedEntity.groupPermissions.map((gp) => gp.group)
);
}
return groups;
},
groupNames: function () {
return this.combinedGroups.map((g) => g.name);
},
groupsCount: function () {
return this.combinedGroups.length;
},
totalCount: function () {
return this.usersCount + this.groupsCount;
},
shareButtonEnabled: function () {
// Enable share button if new entity or user is the entity's owner
return (
this.localSharedEntity &&
(!this.localSharedEntity.entityId ||
this.localSharedEntity.isOwner ||
this.localSharedEntity.hasSharingPermission)
);
},
hasParentSharedEntityPermissions() {
return (
this.parentSharedEntity &&
(this.parentSharedEntity.userPermissions.length > 0 ||
this.parentSharedEntity.groupPermissions.length > 0)
);
},
parentEntityOwner() {
// Only show the parent entity owner when not the same as current user
if (this.parentSharedEntity && !this.parentSharedEntity.isOwner) {
return this.parentSharedEntity.owner;
} else {
return null;
}
},
},
methods: {
initialize: function () {
// First loaded needed data and then process it. This is to prevent one
// call to initialize clobbering a later call to initialize. That is, do
// all of the async stuff first and then make decisions based on the
// values of the props.
const promises = [];
let loadedSharedEntity = null;
if (this.entityId) {
promises.push(
this.loadSharedEntity(this.entityId).then(
(sharedEntity) => (loadedSharedEntity = sharedEntity)
)
);
}
if (
!this.entityId &&
(!this.sharedEntity || !this.sharedEntity.entityId) &&
(!this.defaultGatewayUsersGroup ||
!this.adminsGroup ||
!this.readOnlyAdminsGroup)
) {
promises.push(
services.GroupService.list({ limit: -1 }).then((groups) => {
this.groups = groups;
this.defaultGatewayUsersGroup = groups.find(
(g) => g.isDefaultGatewayUsersGroup
);
this.adminsGroup = groups.find((g) => g.isGatewayAdminsGroup);
this.readOnlyAdminsGroup = groups.find(
(g) => g.isReadOnlyGatewayAdminsGroup
);
})
);
}
if (this.parentEntityId) {
promises.push(
this.loadSharedEntity(this.parentEntityId).then(
(sharedEntity) => (this.parentSharedEntity = sharedEntity)
)
);
}
Promise.all(promises).then(() => {
if (this.sharedEntity) {
this.localSharedEntity = this.sharedEntity.clone();
} else if (this.entityId) {
this.localSharedEntity = loadedSharedEntity;
} else {
this.localSharedEntity = new models.SharedEntity();
}
if (
!this.localSharedEntity.entityId &&
this.autoAddDefaultGatewayUsersGroup
) {
this.localSharedEntity.addGroup({
group: this.defaultGatewayUsersGroup,
});
this.emitUnsavedEvent();
}
if (!this.localSharedEntity.entityId && this.autoAddAdminGroups) {
this.localSharedEntity.addGroup({
group: this.adminsGroup,
permissionType: models.ResourcePermissionType.MANAGE_SHARING,
});
this.localSharedEntity.addGroup({ group: this.readOnlyAdminsGroup });
this.emitUnsavedEvent();
}
if (
this.localSharedEntity.entityId &&
this.autoAddAdminGroups &&
this.localSharedEntity.isOwner
) {
// AIRAVATA-3297 Admins group used to get WRITE permission, but the
// new default is MANAGE_SHARING so update if necessary
// Since autoAddAdminGroups is true, there should already be an adminsGroupPermission
const adminsGroupPermission = this.localSharedEntity.groupPermissions.find(
(gp) => gp.group.isGatewayAdminsGroup
);
if (
adminsGroupPermission &&
adminsGroupPermission.permissionType !==
models.ResourcePermissionType.MANAGE_SHARING
) {
adminsGroupPermission.permissionType =
models.ResourcePermissionType.MANAGE_SHARING;
this.emitUnsavedEvent();
}
}
});
},
loadSharedEntity(entityId) {
return services.SharedEntityService.retrieve({ lookup: entityId });
},
/**
* Merge the persisted SharedEntity with the local SharedEntity
* instance and save it, returning a Promise.
*/
mergeAndSave: function (entityId) {
return services.SharedEntityService.merge({
lookup: entityId,
data: this.localSharedEntity,
}).then((sharedEntity) => {
this.localSharedEntity = sharedEntity;
this.emitSavedEvent();
});
},
saveSharedEntity: function () {
// If we don't have an entityId we can't create a SharedEntity. Instead,
// we'll just emit 'unsaved' to let parent know that sharing has changed.
// It will be up to parent to call `mergeAndSave(entityId)` once there is
// an entityId or merge the sharedEntity itself.
if (this.localSharedEntity.entityId) {
services.SharedEntityService.update({
data: this.localSharedEntity,
lookup: this.localSharedEntity.entityId,
}).then((sharedEntity) => {
this.localSharedEntity = sharedEntity;
this.emitSavedEvent();
});
} else {
this.emitUnsavedEvent();
}
},
emitSavedEvent() {
this.$emit("saved", this.localSharedEntity);
},
emitUnsavedEvent() {
this.$emit("unsaved", this.localSharedEntity);
},
cancelEditSharedEntity: function () {
this.localSharedEntity = this.sharedEntityCopy;
},
openSharingSettingsModal: function () {
this.$refs.sharingSettingsModal.show();
},
showSharingSettingsModal: function () {
this.sharedEntityCopy = this.localSharedEntity.clone();
if (!this.users) {
services.ServiceFactory.service("UserProfiles")
.list()
.then((users) => (this.users = users));
}
if (!this.groups) {
services.GroupService.list({ limit: -1 }).then((groups) => {
this.groups = groups;
});
}
},
},
mounted: function () {
// Only run initialize when mounted since it may add the default gateways
// group automatically (autoAddDefaultGatewayUsersGroup)
this.initialize();
},
watch: {
sharedEntity(newSharedEntity) {
this.localSharedEntity = newSharedEntity
? newSharedEntity.clone()
: new models.SharedEntity();
},
entityId(newEntityId, oldEntityId) {
if (newEntityId && newEntityId !== oldEntityId) {
this.loadSharedEntity(newEntityId).then(
(sharedEntity) => (this.localSharedEntity = sharedEntity)
);
}
},
parentEntityId(newParentEntityId) {
this.loadSharedEntity(newParentEntityId).then((sharedEntity) => {
this.parentSharedEntity = sharedEntity;
});
},
},
};
</script>
<style scoped>
button {
background-color: white;
white-space: nowrap;
}
.share-button {
display: inline-block;
}
.share-button >>> .modal-share-settings .modal-body {
max-height: 50vh;
min-height: 300px;
overflow: auto;
}
.share-button >>> .modal-dialog {
max-width: 800px;
width: 60vw;
}
</style>