mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
projects: Enabling Role based Users in Projects (#382)
Enables creating role based users in projects UI for feature: apache/cloudstack#4128 Also addresses issue: #485 Co-authored-by: Pearl Dsilva <pearl.dsilva@shapeblue.com> Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
2b7624e45c
commit
fa934769d6
@ -91,6 +91,7 @@ export default {
|
|||||||
},
|
},
|
||||||
changeProject (index) {
|
changeProject (index) {
|
||||||
const project = this.projects[index]
|
const project = this.projects[index]
|
||||||
|
this.$store.dispatch('ProjectView', project.id)
|
||||||
this.$store.dispatch('SetProject', project)
|
this.$store.dispatch('SetProject', project)
|
||||||
this.$store.dispatch('ToggleTheme', project.id === undefined ? 'light' : 'dark')
|
this.$store.dispatch('ToggleTheme', project.id === undefined ? 'light' : 'dark')
|
||||||
this.$message.success(`Switched to "${project.displaytext}"`)
|
this.$message.success(`Switched to "${project.displaytext}"`)
|
||||||
|
|||||||
@ -93,6 +93,20 @@ export default {
|
|||||||
},
|
},
|
||||||
$route () {
|
$route () {
|
||||||
this.dedicatedSectionActive = this.dedicatedRoutes.includes(this.$route.meta.name)
|
this.dedicatedSectionActive = this.dedicatedRoutes.includes(this.$route.meta.name)
|
||||||
|
this.fetchProjectAdmins()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchProjectAdmins () {
|
||||||
|
if (!this.resource.owner) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var owners = this.resource.owner
|
||||||
|
var projectAdmins = []
|
||||||
|
for (var owner of owners) {
|
||||||
|
projectAdmins.push(Object.keys(owner).includes('user') ? owner.account + '(' + owner.user + ')' : owner.account)
|
||||||
|
}
|
||||||
|
this.resource.account = projectAdmins.join()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -459,6 +459,21 @@
|
|||||||
<span v-else>{{ resource.zone || resource.zonename || resource.zoneid }}</span>
|
<span v-else>{{ resource.zone || resource.zonename || resource.zoneid }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="resource-detail-item" v-if="resource.owner">
|
||||||
|
<div class="resource-detail-item__label">{{ $t('label.owners') }}</div>
|
||||||
|
<div class="resource-detail-item__details">
|
||||||
|
<a-icon type="user" />
|
||||||
|
<template v-for="(item,idx) in resource.owner">
|
||||||
|
<span style="margin-right:5px" :key="idx">
|
||||||
|
<span v-if="$store.getters.userInfo.roletype !== 'User'">
|
||||||
|
<router-link v-if="'user' in item" :to="{ path: '/accountuser', query: { username: item.user, domainid: resource.domainid }}">{{ item.account + '(' + item.user + ')' }}</router-link>
|
||||||
|
<router-link v-else :to="{ path: '/account', query: { name: item.account, domainid: resource.domainid } }">{{ item.account }}</router-link>
|
||||||
|
</span>
|
||||||
|
<span v-else>{{ item.user ? item.account + '(' + item.user + ')' : item.account }}</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="resource-detail-item" v-if="resource.account && !resource.account.startsWith('PrjAcct-')">
|
<div class="resource-detail-item" v-if="resource.account && !resource.account.startsWith('PrjAcct-')">
|
||||||
<div class="resource-detail-item__label">{{ $t('label.account') }}</div>
|
<div class="resource-detail-item__label">{{ $t('label.account') }}</div>
|
||||||
<div class="resource-detail-item__details">
|
<div class="resource-detail-item__details">
|
||||||
|
|||||||
@ -169,6 +169,17 @@
|
|||||||
<router-link :to="{ path: '/pod/' + record.podid }">{{ text }}</router-link>
|
<router-link :to="{ path: '/pod/' + record.podid }">{{ text }}</router-link>
|
||||||
</a>
|
</a>
|
||||||
<span slot="account" slot-scope="text, record">
|
<span slot="account" slot-scope="text, record">
|
||||||
|
<template v-if="record.owner">
|
||||||
|
<template v-for="(item,idx) in record.owner">
|
||||||
|
<span style="margin-right:5px" :key="idx">
|
||||||
|
<span v-if="$store.getters.userInfo.roletype !== 'User'">
|
||||||
|
<router-link v-if="'user' in item" :to="{ path: '/accountuser', query: { username: item.user, domainid: record.domainid }}">{{ item.account + '(' + item.user + ')' }}</router-link>
|
||||||
|
<router-link v-else :to="{ path: '/account', query: { name: item.account, domainid: record.domainid } }">{{ item.account }}</router-link>
|
||||||
|
</span>
|
||||||
|
<span v-else>{{ item.user ? item.account + '(' + item.user + ')' : item.account }}</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
<template v-if="text && !text.startsWith('PrjAcct-')">
|
<template v-if="text && !text.startsWith('PrjAcct-')">
|
||||||
<router-link
|
<router-link
|
||||||
v-if="'quota' in record && $router.resolve(`${$route.path}/${record.account}`) !== '404'"
|
v-if="'quota' in record && $router.resolve(`${$route.path}/${record.account}`) !== '404'"
|
||||||
|
|||||||
@ -88,7 +88,8 @@ export default {
|
|||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
activeTab: '',
|
activeTab: '',
|
||||||
networkService: null
|
networkService: null,
|
||||||
|
projectAccount: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
// KIND, either express or implied. See the License for the
|
// KIND, either express or implied. See the License for the
|
||||||
// specific language governing permissions and limitations
|
// specific language governing permissions and limitations
|
||||||
// under the License.
|
// under the License.
|
||||||
|
import store from '@/store'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'project',
|
name: 'project',
|
||||||
@ -28,7 +29,20 @@ export default {
|
|||||||
tabs: [
|
tabs: [
|
||||||
{
|
{
|
||||||
name: 'details',
|
name: 'details',
|
||||||
component: () => import('@/components/view/DetailsTab.vue')
|
component: () => import('@/views/project/ProjectDetailsTab.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'accounts',
|
||||||
|
component: () => import('@/views/project/AccountsTab.vue'),
|
||||||
|
show: (record, route, user) => { return ['Admin', 'DomainAdmin'].includes(user.roletype) || record.isCurrentUserProjectAdmin }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'project.roles',
|
||||||
|
component: () => import('@/views/project/iam/ProjectRoleTab.vue'),
|
||||||
|
show: (record, route, user) => {
|
||||||
|
return (['Admin', 'DomainAdmin'].includes(user.roletype) || record.isCurrentUserProjectAdmin) &&
|
||||||
|
'listProjectRoles' in store.getters.apis
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'resources',
|
name: 'resources',
|
||||||
@ -38,11 +52,6 @@ export default {
|
|||||||
name: 'limits',
|
name: 'limits',
|
||||||
show: (record, route, user) => { return ['Admin'].includes(user.roletype) },
|
show: (record, route, user) => { return ['Admin'].includes(user.roletype) },
|
||||||
component: () => import('@/components/view/ResourceLimitTab.vue')
|
component: () => import('@/components/view/ResourceLimitTab.vue')
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'accounts',
|
|
||||||
show: (record, route, user) => { return record.account === user.account || ['Admin', 'DomainAdmin'].includes(user.roletype) },
|
|
||||||
component: () => import('@/views/project/AccountsTab.vue')
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
actions: [
|
actions: [
|
||||||
@ -84,7 +93,7 @@ export default {
|
|||||||
dataView: true,
|
dataView: true,
|
||||||
args: ['displaytext'],
|
args: ['displaytext'],
|
||||||
show: (record, store) => {
|
show: (record, store) => {
|
||||||
return record.account === store.userInfo.account || ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)
|
return (['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) || record.isCurrentUserProjectAdmin
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -94,7 +103,7 @@ export default {
|
|||||||
message: 'message.activate.project',
|
message: 'message.activate.project',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record, store) => {
|
show: (record, store) => {
|
||||||
return (record.account === store.userInfo.account || ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) && record.state === 'Suspended'
|
return ((['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) || record.isCurrentUserProjectAdmin) && record.state === 'Suspended'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -105,7 +114,8 @@ export default {
|
|||||||
docHelp: 'adminguide/projects.html#sending-project-membership-invitations',
|
docHelp: 'adminguide/projects.html#sending-project-membership-invitations',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record, store) => {
|
show: (record, store) => {
|
||||||
return (record.account === store.userInfo.account || ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) && record.state !== 'Suspended'
|
return ((['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) ||
|
||||||
|
record.isCurrentUserProjectAdmin) && record.state !== 'Suspended'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -114,13 +124,11 @@ export default {
|
|||||||
label: 'label.action.project.add.account',
|
label: 'label.action.project.add.account',
|
||||||
docHelp: 'adminguide/projects.html#adding-project-members-from-the-ui',
|
docHelp: 'adminguide/projects.html#adding-project-members-from-the-ui',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
args: ['projectid', 'account', 'email'],
|
popup: true,
|
||||||
show: (record, store) => { return record.account === store.userInfo.account || ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) },
|
show: (record, store) => {
|
||||||
mapping: {
|
return (['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) || record.isCurrentUserProjectAdmin
|
||||||
projectid: {
|
},
|
||||||
value: (record) => { return record.id }
|
component: () => import('@/views/project/AddAccountOrUserToProject.vue')
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
api: 'deleteProject',
|
api: 'deleteProject',
|
||||||
@ -129,7 +137,9 @@ export default {
|
|||||||
message: 'message.delete.project',
|
message: 'message.delete.project',
|
||||||
docHelp: 'adminguide/projects.html#suspending-or-deleting-a-project',
|
docHelp: 'adminguide/projects.html#suspending-or-deleting-a-project',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record, store) => { return record.account === store.userInfo.account || ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) }
|
show: (record, store) => {
|
||||||
|
return (['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) || record.isCurrentUserProjectAdmin
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -213,6 +213,7 @@
|
|||||||
"label.action.migrate.systemvm": "Migrate System VM",
|
"label.action.migrate.systemvm": "Migrate System VM",
|
||||||
"label.action.migrate.systemvm.processing": "Migrating System VM....",
|
"label.action.migrate.systemvm.processing": "Migrating System VM....",
|
||||||
"label.action.project.add.account": "Add Account to Project",
|
"label.action.project.add.account": "Add Account to Project",
|
||||||
|
"label.action.project.add.user": "Add User to Project",
|
||||||
"label.action.reboot.instance": "Reboot Instance",
|
"label.action.reboot.instance": "Reboot Instance",
|
||||||
"label.action.reboot.instance.processing": "Rebooting Instance....",
|
"label.action.reboot.instance.processing": "Rebooting Instance....",
|
||||||
"label.action.reboot.router": "Reboot Router",
|
"label.action.reboot.router": "Reboot Router",
|
||||||
@ -339,6 +340,7 @@
|
|||||||
"label.add.portable.ip.range": "Add Portable IP Range",
|
"label.add.portable.ip.range": "Add Portable IP Range",
|
||||||
"label.add.primary.storage": "Add Primary Storage",
|
"label.add.primary.storage": "Add Primary Storage",
|
||||||
"label.add.private.gateway": "Add Private Gateway",
|
"label.add.private.gateway": "Add Private Gateway",
|
||||||
|
"label.add.project.role": "Add Project Role",
|
||||||
"label.add.region": "Add Region",
|
"label.add.region": "Add Region",
|
||||||
"label.add.resources": "Add Resources",
|
"label.add.resources": "Add Resources",
|
||||||
"label.add.role": "Add Role",
|
"label.add.role": "Add Role",
|
||||||
@ -608,6 +610,7 @@
|
|||||||
"label.create.nfs.secondary.staging.storage": "Create NFS Secondary Staging Store",
|
"label.create.nfs.secondary.staging.storage": "Create NFS Secondary Staging Store",
|
||||||
"label.create.nfs.secondary.staging.store": "Create NFS secondary staging store",
|
"label.create.nfs.secondary.staging.store": "Create NFS secondary staging store",
|
||||||
"label.create.project": "Create project",
|
"label.create.project": "Create project",
|
||||||
|
"label.create.project.role": "Create Project Role",
|
||||||
"label.create.user": "Create user",
|
"label.create.user": "Create user",
|
||||||
"label.create.site.vpn.connection": "Create Site-to-Site VPN Connection",
|
"label.create.site.vpn.connection": "Create Site-to-Site VPN Connection",
|
||||||
"label.create.site.vpn.gateway": "Create Site-to-Site VPN Gateway",
|
"label.create.site.vpn.gateway": "Create Site-to-Site VPN Gateway",
|
||||||
@ -675,6 +678,7 @@
|
|||||||
"label.delete.pa": "Delete Palo Alto",
|
"label.delete.pa": "Delete Palo Alto",
|
||||||
"label.delete.portable.ip.range": "Delete Portable IP Range",
|
"label.delete.portable.ip.range": "Delete Portable IP Range",
|
||||||
"label.delete.project": "Delete project",
|
"label.delete.project": "Delete project",
|
||||||
|
"label.delete.project.role": "Delete Project Role",
|
||||||
"label.delete.role": "Delete Role",
|
"label.delete.role": "Delete Role",
|
||||||
"label.delete.rule": "Delete rule",
|
"label.delete.rule": "Delete rule",
|
||||||
"label.delete.secondary.staging.store": "Delete Secondary Staging Store",
|
"label.delete.secondary.staging.store": "Delete Secondary Staging Store",
|
||||||
@ -694,6 +698,8 @@
|
|||||||
"label.deleting.failed": "Deleting Failed",
|
"label.deleting.failed": "Deleting Failed",
|
||||||
"label.deleting.iso": "Deleting ISO",
|
"label.deleting.iso": "Deleting ISO",
|
||||||
"label.deleting.processing": "Deleting....",
|
"label.deleting.processing": "Deleting....",
|
||||||
|
"label.demote.project.owner": "Demote account to Regular role",
|
||||||
|
"label.demote.project.owner.user": "Demote user to Regular role",
|
||||||
"label.deleting.template": "Deleting template",
|
"label.deleting.template": "Deleting template",
|
||||||
"label.deny": "Deny",
|
"label.deny": "Deny",
|
||||||
"label.deploymentplanner": "Deployment planner",
|
"label.deploymentplanner": "Deployment planner",
|
||||||
@ -793,6 +799,7 @@
|
|||||||
"label.edit.lb.rule": "Edit LB rule",
|
"label.edit.lb.rule": "Edit LB rule",
|
||||||
"label.edit.network.details": "Edit network details",
|
"label.edit.network.details": "Edit network details",
|
||||||
"label.edit.project.details": "Edit project details",
|
"label.edit.project.details": "Edit project details",
|
||||||
|
"label.edit.project.role": "Edit Project Role",
|
||||||
"label.edit.region": "Edit Region",
|
"label.edit.region": "Edit Region",
|
||||||
"label.edit.role": "Edit Role",
|
"label.edit.role": "Edit Role",
|
||||||
"label.edit.rule": "Edit rule",
|
"label.edit.rule": "Edit rule",
|
||||||
@ -1239,6 +1246,7 @@
|
|||||||
"label.macaddresschanges": "MAC Address Changes",
|
"label.macaddresschanges": "MAC Address Changes",
|
||||||
"label.macos": "MacOS",
|
"label.macos": "MacOS",
|
||||||
"label.make.project.owner": "Make account project owner",
|
"label.make.project.owner": "Make account project owner",
|
||||||
|
"label.make.user.project.owner": "Make user project owner",
|
||||||
"label.makeredundant": "Make redundant",
|
"label.makeredundant": "Make redundant",
|
||||||
"label.manage": "Manage",
|
"label.manage": "Manage",
|
||||||
"label.manage.resources": "Manage Resources",
|
"label.manage.resources": "Manage Resources",
|
||||||
@ -1325,6 +1333,7 @@
|
|||||||
"label.metrics.network.usage": "Network Usage",
|
"label.metrics.network.usage": "Network Usage",
|
||||||
"label.metrics.network.write": "Write",
|
"label.metrics.network.write": "Write",
|
||||||
"label.metrics.num.cpu.cores": "Cores",
|
"label.metrics.num.cpu.cores": "Cores",
|
||||||
|
|
||||||
"label.migrate.instance.to": "Migrate instance to",
|
"label.migrate.instance.to": "Migrate instance to",
|
||||||
"label.migrate.instance.to.host": "Migrate instance to another host",
|
"label.migrate.instance.to.host": "Migrate instance to another host",
|
||||||
"label.migrate.instance.to.ps": "Migrate instance to another primary storage",
|
"label.migrate.instance.to.ps": "Migrate instance to another primary storage",
|
||||||
@ -1503,6 +1512,7 @@
|
|||||||
"label.owned.public.ips": "Owned Public IP Addresses",
|
"label.owned.public.ips": "Owned Public IP Addresses",
|
||||||
"label.owner.account": "Owner Account",
|
"label.owner.account": "Owner Account",
|
||||||
"label.owner.domain": "Owner Domain",
|
"label.owner.domain": "Owner Domain",
|
||||||
|
"label.owners": "Owners",
|
||||||
"label.pa": "Palo Alto",
|
"label.pa": "Palo Alto",
|
||||||
"label.page": "page",
|
"label.page": "page",
|
||||||
"label.palo.alto.details": "Palo Alto details",
|
"label.palo.alto.details": "Palo Alto details",
|
||||||
@ -1593,6 +1603,10 @@
|
|||||||
"label.project.invitation": "Project Invitations",
|
"label.project.invitation": "Project Invitations",
|
||||||
"label.project.invite": "Invite to project",
|
"label.project.invite": "Invite to project",
|
||||||
"label.project.name": "Project name",
|
"label.project.name": "Project name",
|
||||||
|
"label.project.owner": "Project Owner(s)",
|
||||||
|
"label.project.role": "Project Role",
|
||||||
|
"label.project.roles": "Project Roles",
|
||||||
|
"label.project.role.permissions": "Project Role Permissions",
|
||||||
"label.project.view": "Project View",
|
"label.project.view": "Project View",
|
||||||
"label.projectaccountname": "Project Account Name",
|
"label.projectaccountname": "Project Account Name",
|
||||||
"label.projectid": "Project ID",
|
"label.projectid": "Project ID",
|
||||||
@ -1712,6 +1726,8 @@
|
|||||||
"label.remove.network.offering": "Remove network offering",
|
"label.remove.network.offering": "Remove network offering",
|
||||||
"label.remove.pf": "Remove port forwarding rule",
|
"label.remove.pf": "Remove port forwarding rule",
|
||||||
"label.remove.project.account": "Remove account from project",
|
"label.remove.project.account": "Remove account from project",
|
||||||
|
"label.remove.project.role": "Remove project role",
|
||||||
|
"label.remove.project.user": "Remove user from project",
|
||||||
"label.remove.region": "Remove Region",
|
"label.remove.region": "Remove Region",
|
||||||
"label.remove.rule": "Remove rule",
|
"label.remove.rule": "Remove rule",
|
||||||
"label.remove.ssh.key.pair": "Remove SSH Key Pair",
|
"label.remove.ssh.key.pair": "Remove SSH Key Pair",
|
||||||
@ -2094,6 +2110,7 @@
|
|||||||
"label.untagged": "Untagged",
|
"label.untagged": "Untagged",
|
||||||
"label.update.instance.group": "Update Instance Group",
|
"label.update.instance.group": "Update Instance Group",
|
||||||
"label.update.project.resources": "Update project resources",
|
"label.update.project.resources": "Update project resources",
|
||||||
|
"label.update.project.role": "Update project role",
|
||||||
"label.update.ssl": " SSL Certificate",
|
"label.update.ssl": " SSL Certificate",
|
||||||
"label.update.ssl.cert": " SSL Certificate",
|
"label.update.ssl.cert": " SSL Certificate",
|
||||||
"label.update.to": "updated to",
|
"label.update.to": "updated to",
|
||||||
@ -2120,6 +2137,7 @@
|
|||||||
"label.usehttps": "Use HTTPS",
|
"label.usehttps": "Use HTTPS",
|
||||||
"label.usenewdiskoffering": "Replace disk offering?",
|
"label.usenewdiskoffering": "Replace disk offering?",
|
||||||
"label.user": "User",
|
"label.user": "User",
|
||||||
|
"label.user.as.admin": "Make User the Project Admin",
|
||||||
"label.user.conflict": "Conflict",
|
"label.user.conflict": "Conflict",
|
||||||
"label.user.details": "User details",
|
"label.user.details": "User details",
|
||||||
"label.user.source": "source",
|
"label.user.source": "source",
|
||||||
@ -2440,6 +2458,7 @@
|
|||||||
"message.add.tag.for.networkacl": "Add tag for NetworkACL",
|
"message.add.tag.for.networkacl": "Add tag for NetworkACL",
|
||||||
"message.add.tag.processing": "Adding new tag...",
|
"message.add.tag.processing": "Adding new tag...",
|
||||||
"message.add.template": "Please enter the following data to create your new template",
|
"message.add.template": "Please enter the following data to create your new template",
|
||||||
|
"message.add.user.to.project": "This form is to enable adding specific users of an account to a project.<br>Furthermore, a ProjectRole may be added to the added user/account to allow/disallow API access at project level.<br> We can also specify the role with which the user should be added to a project - Admin/Regular; if not specified, it defaults to 'Regular'",
|
||||||
"message.add.volume": "Please fill in the following data to add a new volume.",
|
"message.add.volume": "Please fill in the following data to add a new volume.",
|
||||||
"message.add.vpn.connection.failed": "Adding VPN Connection failed",
|
"message.add.vpn.connection.failed": "Adding VPN Connection failed",
|
||||||
"message.add.vpn.connection.processing": "Adding VPN Connection...",
|
"message.add.vpn.connection.processing": "Adding VPN Connection...",
|
||||||
@ -3183,6 +3202,9 @@
|
|||||||
"message.zone.step.3.desc": "Please enter the following info to add a new pod",
|
"message.zone.step.3.desc": "Please enter the following info to add a new pod",
|
||||||
"message.zonewizard.enable.local.storage": "WARNING: If you enable local storage for this zone, you must do the following, depending on where you would like your system VMs to launch:<br/><br/>1. If system VMs need to be launched in shared primary storage, shared primary storage needs to be added to the zone after creation. You must also start the zone in a disabled state.<br/><br/>2. If system VMs need to be launched in local primary storage, system.vm.use.local.storage needs to be set to true before you enable the zone.<br/><br/><br/>Would you like to continue?",
|
"message.zonewizard.enable.local.storage": "WARNING: If you enable local storage for this zone, you must do the following, depending on where you would like your system VMs to launch:<br/><br/>1. If system VMs need to be launched in shared primary storage, shared primary storage needs to be added to the zone after creation. You must also start the zone in a disabled state.<br/><br/>2. If system VMs need to be launched in local primary storage, system.vm.use.local.storage needs to be set to true before you enable the zone.<br/><br/><br/>Would you like to continue?",
|
||||||
"messgae.validate.min": "Please enter a value greater than or equal to {0}.",
|
"messgae.validate.min": "Please enter a value greater than or equal to {0}.",
|
||||||
|
"migrate.from": "Migrate From",
|
||||||
|
"migrate.to": "Migrate To",
|
||||||
|
"migrationPolicy": "Migration Policy",
|
||||||
"network.rate": "Network Rate",
|
"network.rate": "Network Rate",
|
||||||
"router.health.checks": "Health Check",
|
"router.health.checks": "Health Check",
|
||||||
"side.by.side": "Side by Side",
|
"side.by.side": "Side by Side",
|
||||||
|
|||||||
@ -91,7 +91,6 @@ const user = {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
login(userInfo).then(response => {
|
login(userInfo).then(response => {
|
||||||
const result = response.loginresponse || {}
|
const result = response.loginresponse || {}
|
||||||
|
|
||||||
Cookies.set('account', result.account, { expires: 1 })
|
Cookies.set('account', result.account, { expires: 1 })
|
||||||
Cookies.set('domainid', result.domainid, { expires: 1 })
|
Cookies.set('domainid', result.domainid, { expires: 1 })
|
||||||
Cookies.set('role', result.type, { expires: 1 })
|
Cookies.set('role', result.type, { expires: 1 })
|
||||||
@ -100,7 +99,6 @@ const user = {
|
|||||||
Cookies.set('userfullname', result.firstname + ' ' + result.lastname, { expires: 1 })
|
Cookies.set('userfullname', result.firstname + ' ' + result.lastname, { expires: 1 })
|
||||||
Cookies.set('userid', result.userid, { expires: 1 })
|
Cookies.set('userid', result.userid, { expires: 1 })
|
||||||
Cookies.set('username', result.username, { expires: 1 })
|
Cookies.set('username', result.username, { expires: 1 })
|
||||||
|
|
||||||
Vue.ls.set(ACCESS_TOKEN, result.sessionkey, 24 * 60 * 60 * 1000)
|
Vue.ls.set(ACCESS_TOKEN, result.sessionkey, 24 * 60 * 60 * 1000)
|
||||||
commit('SET_TOKEN', result.sessionkey)
|
commit('SET_TOKEN', result.sessionkey)
|
||||||
|
|
||||||
@ -174,7 +172,7 @@ const user = {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
api('listUsers', { username: Cookies.get('username'), listall: true }).then(response => {
|
api('listUsers', { username: Cookies.get('username') }).then(response => {
|
||||||
const result = response.listusersresponse.user[0]
|
const result = response.listusersresponse.user[0]
|
||||||
commit('SET_INFO', result)
|
commit('SET_INFO', result)
|
||||||
commit('SET_NAME', result.firstname + ' ' + result.lastname)
|
commit('SET_NAME', result.firstname + ' ' + result.lastname)
|
||||||
@ -248,6 +246,29 @@ const user = {
|
|||||||
var jobsArray = Vue.ls.get(ASYNC_JOB_IDS, [])
|
var jobsArray = Vue.ls.get(ASYNC_JOB_IDS, [])
|
||||||
jobsArray.push(jobJson)
|
jobsArray.push(jobJson)
|
||||||
commit('SET_ASYNC_JOB_IDS', jobsArray)
|
commit('SET_ASYNC_JOB_IDS', jobsArray)
|
||||||
|
},
|
||||||
|
ProjectView ({ commit }, projectid) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
api('listApis', { projectid: projectid }).then(response => {
|
||||||
|
const apis = {}
|
||||||
|
const apiList = response.listapisresponse.api
|
||||||
|
for (var idx = 0; idx < apiList.length; idx++) {
|
||||||
|
const api = apiList[idx]
|
||||||
|
const apiName = api.name
|
||||||
|
apis[apiName] = {
|
||||||
|
params: api.params,
|
||||||
|
response: api.response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commit('SET_APIS', apis)
|
||||||
|
resolve(apis)
|
||||||
|
store.dispatch('GenerateRoutes', { apis }).then(() => {
|
||||||
|
router.addRoutes(store.getters.addRouters)
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -576,6 +576,18 @@ export default {
|
|||||||
this.items = []
|
this.items = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (['listTemplates', 'listIsos'].includes(this.apiName) && this.items.length > 1) {
|
||||||
|
this.items = [...new Map(this.items.map(x => [x.id, x])).values()]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.apiName === 'listProjects' && this.items.length > 0) {
|
||||||
|
this.columns.map(col => {
|
||||||
|
if (col.title === 'Account') {
|
||||||
|
col.title = this.$t('label.project.owner')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
for (let idx = 0; idx < this.items.length; idx++) {
|
for (let idx = 0; idx < this.items.length; idx++) {
|
||||||
this.items[idx].key = idx
|
this.items[idx].key = idx
|
||||||
for (const key in customRender) {
|
for (const key in customRender) {
|
||||||
@ -818,7 +830,6 @@ export default {
|
|||||||
execSubmit (e) {
|
execSubmit (e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.form.validateFields((err, values) => {
|
this.form.validateFields((err, values) => {
|
||||||
console.log(values)
|
|
||||||
if (!err) {
|
if (!err) {
|
||||||
const params = {}
|
const params = {}
|
||||||
if ('id' in this.resource && this.currentAction.params.map(i => { return i.name }).includes('id')) {
|
if ('id' in this.resource && this.currentAction.params.map(i => { return i.name }).includes('id')) {
|
||||||
@ -868,10 +879,6 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(this.currentAction)
|
|
||||||
console.log(this.resource)
|
|
||||||
console.log(params)
|
|
||||||
|
|
||||||
const resourceName = params.displayname || params.displaytext || params.name || params.hostname || params.username || params.ipaddress || params.virtualmachinename || this.resource.name
|
const resourceName = params.displayname || params.displaytext || params.name || params.hostname || params.username || params.ipaddress || params.virtualmachinename || this.resource.name
|
||||||
|
|
||||||
var hasJobId = false
|
var hasJobId = false
|
||||||
|
|||||||
@ -21,29 +21,47 @@
|
|||||||
<a-col :md="24" :lg="24">
|
<a-col :md="24" :lg="24">
|
||||||
<a-table
|
<a-table
|
||||||
size="small"
|
size="small"
|
||||||
:loading="loading"
|
:loading="loading.table"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:dataSource="dataSource"
|
:dataSource="dataSource"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
:rowKey="record => record.accountid || record.account"
|
:rowKey="record => record.userid ? record.userid : (record.accountid || record.account)">
|
||||||
>
|
<span slot="user" slot-scope="text, record" v-if="record.userid">
|
||||||
<span slot="action" v-if="record.role!==owner" slot-scope="text, record" class="account-button-action">
|
{{ record.username }}
|
||||||
<a-tooltip placement="top">
|
</span>
|
||||||
<template slot="title">
|
<span slot="projectrole" slot-scope="text, record" v-if="record.projectroleid">
|
||||||
{{ $t('label.make.project.owner') }}
|
{{ getProjectRole(record) }}
|
||||||
</template>
|
</span>
|
||||||
|
<span v-if="imProjectAdmin && dataSource.length > 1" slot="action" slot-scope="text, record" class="account-button-action">
|
||||||
|
<a-tooltip
|
||||||
|
slot="title"
|
||||||
|
placement="top"
|
||||||
|
:title="record.userid ? $t('label.make.user.project.owner') : $t('label.make.project.owner')">
|
||||||
<a-button
|
<a-button
|
||||||
|
v-if="record.role !== owner"
|
||||||
type="default"
|
type="default"
|
||||||
shape="circle"
|
shape="circle"
|
||||||
icon="user"
|
icon="arrow-up"
|
||||||
size="small"
|
size="small"
|
||||||
:disabled="!('updateProject' in $store.getters.apis)"
|
@click="promoteAccount(record)" />
|
||||||
@click="onMakeProjectOwner(record)" />
|
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip placement="top">
|
<a-tooltip
|
||||||
<template slot="title">
|
slot="title"
|
||||||
{{ $t('label.remove.project.account') }}
|
placement="top"
|
||||||
</template>
|
:title="record.userid ? $t('label.demote.project.owner.user') : $t('label.demote.project.owner')"
|
||||||
|
v-if="updateProjectApi.params.filter(x => x.name === 'swapowner').length > 0">
|
||||||
|
<a-button
|
||||||
|
v-if="record.role === owner"
|
||||||
|
type="default"
|
||||||
|
shape="circle"
|
||||||
|
icon="arrow-down"
|
||||||
|
size="small"
|
||||||
|
@click="demoteAccount(record)" />
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip
|
||||||
|
slot="title"
|
||||||
|
placement="top"
|
||||||
|
:title="record.userid ? $t('label.remove.project.user') : $t('label.remove.project.account')">
|
||||||
<a-button
|
<a-button
|
||||||
type="danger"
|
type="danger"
|
||||||
shape="circle"
|
shape="circle"
|
||||||
@ -89,11 +107,20 @@ export default {
|
|||||||
return {
|
return {
|
||||||
columns: [],
|
columns: [],
|
||||||
dataSource: [],
|
dataSource: [],
|
||||||
loading: false,
|
imProjectAdmin: false,
|
||||||
|
loading: {
|
||||||
|
user: false,
|
||||||
|
projectAccount: false,
|
||||||
|
roles: false,
|
||||||
|
table: false
|
||||||
|
},
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
itemCount: 0,
|
itemCount: 0,
|
||||||
owner: 'Admin'
|
users: [],
|
||||||
|
projectRoles: [],
|
||||||
|
owner: 'Admin',
|
||||||
|
role: 'Regular'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
@ -101,27 +128,36 @@ export default {
|
|||||||
{
|
{
|
||||||
title: this.$t('label.account'),
|
title: this.$t('label.account'),
|
||||||
dataIndex: 'account',
|
dataIndex: 'account',
|
||||||
width: '35%',
|
|
||||||
scopedSlots: { customRender: 'account' }
|
scopedSlots: { customRender: 'account' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: this.$t('label.role'),
|
title: this.$t('label.roletype'),
|
||||||
dataIndex: 'role',
|
dataIndex: 'role',
|
||||||
scopedSlots: { customRender: 'role' }
|
scopedSlots: { customRender: 'role' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: this.$t('label.action'),
|
title: this.$t('label.action'),
|
||||||
dataIndex: 'action',
|
dataIndex: 'action',
|
||||||
fixed: 'right',
|
|
||||||
width: 100,
|
|
||||||
scopedSlots: { customRender: 'action' }
|
scopedSlots: { customRender: 'action' }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
if (this.isProjectRolesSupported()) {
|
||||||
|
this.columns.splice(1, 0, {
|
||||||
|
title: this.$t('label.user'),
|
||||||
|
dataIndex: 'userid',
|
||||||
|
scopedSlots: { customRender: 'user' }
|
||||||
|
})
|
||||||
|
this.columns.splice(this.columns.length - 1, 0, {
|
||||||
|
title: this.$t('label.project.role'),
|
||||||
|
dataIndex: 'projectroleid',
|
||||||
|
scopedSlots: { customRender: 'projectrole' }
|
||||||
|
})
|
||||||
|
}
|
||||||
this.page = 1
|
this.page = 1
|
||||||
this.pageSize = 10
|
this.pageSize = 10
|
||||||
this.itemCount = 0
|
this.itemCount = 0
|
||||||
},
|
},
|
||||||
|
inject: ['parentFetchData'],
|
||||||
mounted () {
|
mounted () {
|
||||||
this.fetchData()
|
this.fetchData()
|
||||||
},
|
},
|
||||||
@ -140,25 +176,12 @@ export default {
|
|||||||
params.projectId = this.resource.id
|
params.projectId = this.resource.id
|
||||||
params.page = this.page
|
params.page = this.page
|
||||||
params.pageSize = this.pageSize
|
params.pageSize = this.pageSize
|
||||||
|
this.updateProjectApi = this.$store.getters.apis.updateProject
|
||||||
this.loading = true
|
this.fetchUsers()
|
||||||
|
this.fetchProjectAccounts(params)
|
||||||
api('listProjectAccounts', params).then(json => {
|
if (this.isProjectRolesSupported()) {
|
||||||
const listProjectAccount = json.listprojectaccountsresponse.projectaccount
|
this.fetchProjectRoles()
|
||||||
const itemCount = json.listprojectaccountsresponse.count
|
}
|
||||||
|
|
||||||
if (!listProjectAccount || listProjectAccount.length === 0) {
|
|
||||||
this.dataSource = []
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.itemCount = itemCount
|
|
||||||
this.dataSource = listProjectAccount
|
|
||||||
}).catch(error => {
|
|
||||||
this.$notifyError(error)
|
|
||||||
}).finally(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
changePage (page, pageSize) {
|
changePage (page, pageSize) {
|
||||||
this.page = page
|
this.page = page
|
||||||
@ -166,29 +189,126 @@ export default {
|
|||||||
this.fetchData()
|
this.fetchData()
|
||||||
},
|
},
|
||||||
changePageSize (currentPage, pageSize) {
|
changePageSize (currentPage, pageSize) {
|
||||||
this.page = currentPage
|
this.page = 0
|
||||||
this.pageSize = pageSize
|
this.pageSize = pageSize
|
||||||
this.fetchData()
|
this.fetchData()
|
||||||
},
|
},
|
||||||
|
isLoggedInUserProjectAdmin (user) {
|
||||||
|
if (['Admin', 'DomainAdmin'].includes(this.$store.getters.userInfo.roletype)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// If I'm the logged in user Or if I'm the logged in account And I'm the owner
|
||||||
|
if (((user.userid && user.userid === this.$store.getters.userInfo.id) ||
|
||||||
|
user.account === this.$store.getters.userInfo.account) &&
|
||||||
|
user.role === this.owner) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
isProjectRolesSupported () {
|
||||||
|
return ('listProjectRoles' in this.$store.getters.apis)
|
||||||
|
},
|
||||||
|
getProjectRole (record) {
|
||||||
|
const projectRole = this.projectRoles.filter(role => role.id === record.projectroleid)
|
||||||
|
return projectRole[0].name || projectRole[0].id || null
|
||||||
|
},
|
||||||
|
fetchUsers () {
|
||||||
|
this.loading.user = true
|
||||||
|
api('listUsers', { listall: true }).then(response => {
|
||||||
|
this.users = response.listusersresponse.user || []
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading.user = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchProjectRoles () {
|
||||||
|
this.loading.roles = true
|
||||||
|
api('listProjectRoles', { projectId: this.resource.id }).then(response => {
|
||||||
|
this.projectRoles = response.listprojectrolesresponse.projectrole || []
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading.roles = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchProjectAccounts (params) {
|
||||||
|
this.loading.projectAccount = true
|
||||||
|
api('listProjectAccounts', params).then(json => {
|
||||||
|
const listProjectAccount = json.listprojectaccountsresponse.projectaccount
|
||||||
|
const itemCount = json.listprojectaccountsresponse.count
|
||||||
|
if (!listProjectAccount || listProjectAccount.length === 0) {
|
||||||
|
this.dataSource = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for (const projectAccount of listProjectAccount) {
|
||||||
|
this.imProjectAdmin = this.isLoggedInUserProjectAdmin(projectAccount)
|
||||||
|
if (this.imProjectAdmin) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.itemCount = itemCount
|
||||||
|
this.dataSource = listProjectAccount
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading.projectAccount = false
|
||||||
|
})
|
||||||
|
},
|
||||||
onMakeProjectOwner (record) {
|
onMakeProjectOwner (record) {
|
||||||
const title = this.$t('label.make.project.owner')
|
const title = this.$t('label.make.project.owner')
|
||||||
const loading = this.$message.loading(title + `${this.$t('label.in.progress.for')} ` + record.account, 0)
|
const loading = this.$message.loading(title + `${this.$t('label.in.progress.for')} ` + record.account, 0)
|
||||||
const params = {}
|
const params = {}
|
||||||
|
|
||||||
params.id = this.resource.id
|
params.id = this.resource.id
|
||||||
params.account = record.account
|
params.account = record.account
|
||||||
|
this.updateProject(record, params, title, loading)
|
||||||
|
},
|
||||||
|
promoteAccount (record) {
|
||||||
|
var title = this.$t('label.make.project.owner')
|
||||||
|
const loading = this.$message.loading(title + `${this.$t('label.in.progress.for')} ` + record.account, 0)
|
||||||
|
const params = {}
|
||||||
|
|
||||||
|
params.id = this.resource.id
|
||||||
|
if (record.userid) {
|
||||||
|
params.userid = record.userid
|
||||||
|
// params.accountid = (record.user && record.user[0].accountid) || record.accountid
|
||||||
|
title = this.$t('label.make.user.project.owner')
|
||||||
|
} else {
|
||||||
|
params.account = record.account
|
||||||
|
}
|
||||||
|
params.roletype = this.owner
|
||||||
|
params.swapowner = false
|
||||||
|
this.updateProject(record, params, title, loading)
|
||||||
|
},
|
||||||
|
demoteAccount (record) {
|
||||||
|
var title = this.$t('label.demote.project.owner')
|
||||||
|
const loading = this.$message.loading(title + `${this.$t('label.in.progress.for')} ` + record.account, 0)
|
||||||
|
const params = {}
|
||||||
|
if (record.userid) {
|
||||||
|
params.userid = record.userid
|
||||||
|
// params.accountid = (record.user && record.user[0].accountid) || record.accountid
|
||||||
|
title = this.$t('label.demote.project.owner.user')
|
||||||
|
} else {
|
||||||
|
params.account = record.account
|
||||||
|
}
|
||||||
|
|
||||||
|
params.id = this.resource.id
|
||||||
|
params.roletype = 'Regular'
|
||||||
|
params.swapowner = false
|
||||||
|
this.updateProject(record, params, title, loading)
|
||||||
|
},
|
||||||
|
updateProject (record, params, title, loading) {
|
||||||
api('updateProject', params).then(json => {
|
api('updateProject', params).then(json => {
|
||||||
const hasJobId = this.checkForAddAsyncJob(json, title, record.account)
|
const hasJobId = this.checkForAddAsyncJob(json, title, record.account)
|
||||||
|
|
||||||
if (hasJobId) {
|
if (hasJobId) {
|
||||||
this.fetchData()
|
this.fetchData()
|
||||||
}
|
}
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
// show error
|
|
||||||
this.$notifyError(error)
|
this.$notifyError(error)
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
setTimeout(loading, 1000)
|
setTimeout(loading, 1000)
|
||||||
|
this.parentFetchData()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onShowConfirmDelete (record) {
|
onShowConfirmDelete (record) {
|
||||||
@ -209,18 +329,22 @@ export default {
|
|||||||
const title = this.$t('label.remove.project.account')
|
const title = this.$t('label.remove.project.account')
|
||||||
const loading = this.$message.loading(title + `${this.$t('label.in.progress.for')} ` + record.account, 0)
|
const loading = this.$message.loading(title + `${this.$t('label.in.progress.for')} ` + record.account, 0)
|
||||||
const params = {}
|
const params = {}
|
||||||
|
|
||||||
params.account = record.account
|
|
||||||
params.projectid = this.resource.id
|
params.projectid = this.resource.id
|
||||||
|
if (record.userid) {
|
||||||
api('deleteAccountFromProject', params).then(json => {
|
params.userid = record.userid
|
||||||
|
this.deleteOperation('deleteUserFromProject', params, record, title, loading)
|
||||||
|
} else {
|
||||||
|
params.account = record.account
|
||||||
|
this.deleteOperation('deleteAccountFromProject', params, record, title, loading)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deleteOperation (apiName, params, record, title, loading) {
|
||||||
|
api(apiName, params).then(json => {
|
||||||
const hasJobId = this.checkForAddAsyncJob(json, title, record.account)
|
const hasJobId = this.checkForAddAsyncJob(json, title, record.account)
|
||||||
|
|
||||||
if (hasJobId) {
|
if (hasJobId) {
|
||||||
this.fetchData()
|
this.fetchData()
|
||||||
}
|
}
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
// show error
|
|
||||||
this.$notifyError(error)
|
this.$notifyError(error)
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
setTimeout(loading, 1000)
|
setTimeout(loading, 1000)
|
||||||
|
|||||||
332
ui/src/views/project/AddAccountOrUserToProject.vue
Normal file
332
ui/src/views/project/AddAccountOrUserToProject.vue
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-tabs class="form-layout">
|
||||||
|
<a-tab-pane key="1" :tab="$t('label.action.project.add.account')">
|
||||||
|
<a-form
|
||||||
|
:form="form"
|
||||||
|
@submit="addAccountToProject"
|
||||||
|
layout="vertical">
|
||||||
|
<a-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
{{ $t('label.account') }}
|
||||||
|
<a-tooltip :title="apiParams.addAccountToProject.account.description">
|
||||||
|
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
<a-input
|
||||||
|
v-decorator="['account']"
|
||||||
|
:placeholder="apiParams.addAccountToProject.account.description"/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
{{ $t('label.email') }}
|
||||||
|
<a-tooltip :title="apiParams.addAccountToProject.email.description">
|
||||||
|
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
<a-input v-decorator="[ 'email']"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item v-if="apiParams.addAccountToProject.projectroleid">
|
||||||
|
<span slot="label">
|
||||||
|
{{ $t('label.project.role') }}
|
||||||
|
<a-tooltip :title="apiParams.addAccountToProject.projectroleid.description">
|
||||||
|
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
<a-select
|
||||||
|
showSearch
|
||||||
|
v-decorator="['projectroleid']"
|
||||||
|
:loading="loading"
|
||||||
|
:placeholder="$t('label.project.role')"
|
||||||
|
>
|
||||||
|
<a-select-option v-for="role in projectRoles" :key="role.id">
|
||||||
|
{{ role.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item v-if="apiParams.addAccountToProject.roletype">
|
||||||
|
<span slot="label">
|
||||||
|
{{ $t('label.roletype') }}
|
||||||
|
<a-tooltip :title="apiParams.addAccountToProject.roletype.description">
|
||||||
|
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
<a-select
|
||||||
|
showSearch
|
||||||
|
v-decorator="['roletype']"
|
||||||
|
:placeholder="$t('label.roletype')">
|
||||||
|
<a-select-option value="Admin">Admin</a-select-option>
|
||||||
|
<a-select-option value="Regular">Regular</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<div :span="24" class="action-button">
|
||||||
|
<a-button @click="closeAction">{{ this.$t('label.cancel') }}</a-button>
|
||||||
|
<a-button type="primary" @click="addAccountToProject" :loading="loading">{{ $t('label.ok') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</a-form>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="2" :tab="$t('label.action.project.add.user')" v-if="apiParams.addUserToProject">
|
||||||
|
<a-form
|
||||||
|
:form="form"
|
||||||
|
@submit="addUserToProject"
|
||||||
|
layout="vertical">
|
||||||
|
<p v-html="$t('message.add.user.to.project')"></p>
|
||||||
|
<a-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
{{ $t('label.user') }}
|
||||||
|
<a-tooltip :title="apiParams.addUserToProject.username.description">
|
||||||
|
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
<a-input
|
||||||
|
v-decorator="['username']"
|
||||||
|
:placeholder="apiParams.addUserToProject.username.description"/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
{{ $t('label.email') }}
|
||||||
|
<a-tooltip :title="apiParams.addUserToProject.email.description">
|
||||||
|
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
<a-input v-decorator="[ 'email']"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
{{ $t('label.project.role') }}
|
||||||
|
<a-tooltip :title="apiParams.addUserToProject.roletype.description">
|
||||||
|
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
<a-select
|
||||||
|
showSearch
|
||||||
|
v-decorator="['projectroleid']"
|
||||||
|
:loading="loading"
|
||||||
|
:placeholder="$t('label.project.role')"
|
||||||
|
>
|
||||||
|
<a-select-option v-for="role in projectRoles" :key="role.id">
|
||||||
|
{{ role.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
{{ $t('label.roletype') }}
|
||||||
|
<a-tooltip :title="apiParams.addUserToProject.roletype.description">
|
||||||
|
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
<a-select
|
||||||
|
showSearch
|
||||||
|
v-decorator="['roletype']"
|
||||||
|
:placeholder="$t('label.roletype')">
|
||||||
|
<a-select-option value="Admin">Admin</a-select-option>
|
||||||
|
<a-select-option value="Regular">Regular</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<div :span="24" class="action-button">
|
||||||
|
<a-button @click="closeAction">{{ this.$t('label.cancel') }}</a-button>
|
||||||
|
<a-button type="primary" @click="addUserToProject" :loading="loading">{{ $t('label.ok') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</a-form>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { api } from '@/api'
|
||||||
|
export default {
|
||||||
|
name: 'AddAccountOrUserToProject',
|
||||||
|
props: {
|
||||||
|
resource: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
users: [],
|
||||||
|
accounts: [],
|
||||||
|
projectRoles: [],
|
||||||
|
selectedUser: null,
|
||||||
|
selectedAccount: null,
|
||||||
|
loading: false,
|
||||||
|
load: {
|
||||||
|
users: false,
|
||||||
|
accounts: false,
|
||||||
|
projectRoles: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
beforeCreate () {
|
||||||
|
this.form = this.$form.createForm(this)
|
||||||
|
const apis = ['addAccountToProject']
|
||||||
|
if ('addUserToProject' in this.$store.getters.apis) {
|
||||||
|
apis.push('addUserToProject')
|
||||||
|
}
|
||||||
|
this.apiParams = {}
|
||||||
|
for (var api of apis) {
|
||||||
|
const details = {}
|
||||||
|
const apiConfig = this.$store.getters.apis[api]
|
||||||
|
apiConfig.params.forEach(param => {
|
||||||
|
details[param.name] = param
|
||||||
|
})
|
||||||
|
this.apiParams[api] = details
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchData () {
|
||||||
|
this.fetchUsers()
|
||||||
|
this.fetchAccounts()
|
||||||
|
if (this.isProjectRolesSupported()) {
|
||||||
|
this.fetchProjectRoles()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fetchUsers () {
|
||||||
|
this.load.users = true
|
||||||
|
api('listUsers', { listall: true }).then(response => {
|
||||||
|
this.users = response.listusersresponse.user ? response.listusersresponse.user : []
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.load.users = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchAccounts () {
|
||||||
|
this.load.accounts = true
|
||||||
|
api('listAccounts', {
|
||||||
|
domainid: this.resource.domainid
|
||||||
|
}).then(response => {
|
||||||
|
this.accounts = response.listaccountsresponse.account || []
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.load.accounts = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchProjectRoles () {
|
||||||
|
this.load.projectRoles = true
|
||||||
|
api('listProjectRoles', {
|
||||||
|
projectid: this.resource.id
|
||||||
|
}).then(response => {
|
||||||
|
this.projectRoles = response.listprojectrolesresponse.projectrole || []
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.load.projectRoles = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
isProjectRolesSupported () {
|
||||||
|
return ('listProjectRoles' in this.$store.getters.apis)
|
||||||
|
},
|
||||||
|
addAccountToProject (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
this.form.validateFields((err, values) => {
|
||||||
|
if (err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
var params = {
|
||||||
|
projectid: this.resource.id
|
||||||
|
}
|
||||||
|
for (const key in values) {
|
||||||
|
const input = values[key]
|
||||||
|
if (input === undefined) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
params[key] = input
|
||||||
|
}
|
||||||
|
api('addAccountToProject', params).then(response => {
|
||||||
|
this.$pollJob({
|
||||||
|
jobId: response.addaccounttoprojectresponse.jobid,
|
||||||
|
successMessage: `Successfully added account ${params.account} to project`,
|
||||||
|
errorMessage: `Failed to add account: ${params.account} to project`,
|
||||||
|
loadingMessage: `Adding Account: ${params.account} to project...`,
|
||||||
|
catchMessage: 'Error encountered while fetching async job result'
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.$emit('refresh-data')
|
||||||
|
this.loading = false
|
||||||
|
this.closeAction()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
addUserToProject (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
this.form.validateFields((err, values) => {
|
||||||
|
if (err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = true
|
||||||
|
var params = {
|
||||||
|
projectid: this.resource.id
|
||||||
|
}
|
||||||
|
for (const key in values) {
|
||||||
|
const input = values[key]
|
||||||
|
if (input === undefined) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
params[key] = input
|
||||||
|
}
|
||||||
|
api('addUserToProject', params).then(response => {
|
||||||
|
this.$pollJob({
|
||||||
|
jobId: response.addusertoprojectresponse.jobid,
|
||||||
|
successMessage: `Successfully added user ${params.username} to project`,
|
||||||
|
errorMessage: `Failed to add user: ${params.username} to project`,
|
||||||
|
loadingMessage: `Adding User ${params.username} to project...`,
|
||||||
|
catchMessage: 'Error encountered while fetching async job result'
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
console.log('catch')
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.$emit('refresh-data')
|
||||||
|
this.loading = false
|
||||||
|
this.closeAction()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
closeAction () {
|
||||||
|
this.$emit('close-action')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.form-layout {
|
||||||
|
width: 80vw;
|
||||||
|
|
||||||
|
@media (min-width: 600px) {
|
||||||
|
width: 450px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.action-button {
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -115,6 +115,11 @@ export default {
|
|||||||
dataIndex: 'project',
|
dataIndex: 'project',
|
||||||
scopedSlots: { customRender: 'project' }
|
scopedSlots: { customRender: 'project' }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: this.$t('label.account'),
|
||||||
|
dataIndex: 'account',
|
||||||
|
scopedSlots: { customRender: 'account' }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: this.$t('label.domain'),
|
title: this.$t('label.domain'),
|
||||||
dataIndex: 'domain',
|
dataIndex: 'domain',
|
||||||
@ -152,6 +157,18 @@ export default {
|
|||||||
this.page = 1
|
this.page = 1
|
||||||
this.pageSize = 10
|
this.pageSize = 10
|
||||||
this.itemCount = 0
|
this.itemCount = 0
|
||||||
|
this.apiConfig = this.$store.getters.apis.listProjectInvitations || {}
|
||||||
|
this.apiParams = {}
|
||||||
|
this.apiConfig.params.forEach(param => {
|
||||||
|
this.apiParams[param.name] = param
|
||||||
|
})
|
||||||
|
if (this.apiParams.userid) {
|
||||||
|
this.columns.splice(2, 0, {
|
||||||
|
title: this.$t('label.user'),
|
||||||
|
dataIndex: 'userid',
|
||||||
|
scopedSlots: { customRender: 'user' }
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.fetchData()
|
this.fetchData()
|
||||||
@ -225,7 +242,11 @@ export default {
|
|||||||
const params = {}
|
const params = {}
|
||||||
|
|
||||||
params.projectid = record.projectid
|
params.projectid = record.projectid
|
||||||
params.account = record.account
|
if (record.userid && record.userid !== null) {
|
||||||
|
params.userid = record.userid
|
||||||
|
} else {
|
||||||
|
params.account = record.account
|
||||||
|
}
|
||||||
params.domainid = record.domainid
|
params.domainid = record.domainid
|
||||||
params.accept = state
|
params.accept = state
|
||||||
|
|
||||||
|
|||||||
57
ui/src/views/project/ProjectDetailsTab.vue
Normal file
57
ui/src/views/project/ProjectDetailsTab.vue
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
<template>
|
||||||
|
<DetailsTab :resource="resource" />
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import DetailsTab from '@/components/view/DetailsTab'
|
||||||
|
export default {
|
||||||
|
name: 'ProjectDetailsTab',
|
||||||
|
components: {
|
||||||
|
DetailsTab
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
resource: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
resource (newItem, oldItem) {
|
||||||
|
if (!newItem || !newItem.id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.resource = newItem
|
||||||
|
this.fetchProjectAccounts()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchProjectAccounts () {
|
||||||
|
var owner = this.resource.owner
|
||||||
|
owner = owner.filter(projectaccount => {
|
||||||
|
return (projectaccount.userid && projectaccount.userid === this.$store.getters.userInfo.id) ||
|
||||||
|
projectaccount.account === this.$store.getters.userInfo.account
|
||||||
|
})
|
||||||
|
var isCurrentUserProjectAdmin = false
|
||||||
|
if (owner.length > 0) {
|
||||||
|
isCurrentUserProjectAdmin = true
|
||||||
|
}
|
||||||
|
this.$set(this.resource, 'isCurrentUserProjectAdmin', isCurrentUserProjectAdmin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
442
ui/src/views/project/iam/ProjectRolePermissionTab.vue
Normal file
442
ui/src/views/project/iam/ProjectRolePermissionTab.vue
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<a-icon v-if="loadingTable" type="loading" class="main-loading-spinner"></a-icon>
|
||||||
|
<div v-else>
|
||||||
|
<div v-if="updateTable" class="loading-overlay">
|
||||||
|
<a-icon type="loading" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="rules-list ant-list ant-list-bordered"
|
||||||
|
:class="{'rules-list--overflow-hidden' : updateTable}" >
|
||||||
|
|
||||||
|
<div class="rules-table-item ant-list-item">
|
||||||
|
<div class="rules-table__col rules-table__col--grab"></div>
|
||||||
|
<div class="rules-table__col rules-table__col--rule rules-table__col--new">
|
||||||
|
<a-auto-complete
|
||||||
|
:autoFocus="true"
|
||||||
|
:filterOption="filterOption"
|
||||||
|
:dataSource="apis"
|
||||||
|
:value="newRule"
|
||||||
|
@change="val => newRule = val"
|
||||||
|
placeholder="Rule"
|
||||||
|
:class="{'rule-dropdown-error' : newRuleSelectError}" />
|
||||||
|
</div>
|
||||||
|
<div class="rules-table__col rules-table__col--permission">
|
||||||
|
<permission-editable
|
||||||
|
:defaultValue="newRulePermission"
|
||||||
|
@change="onPermissionChange(null, $event)" />
|
||||||
|
</div>
|
||||||
|
<div class="rules-table__col rules-table__col--description">
|
||||||
|
<a-input v-model="newRuleDescription" placeholder="Description"></a-input>
|
||||||
|
</div>
|
||||||
|
<div class="rules-table__col rules-table__col--actions">
|
||||||
|
<a-tooltip
|
||||||
|
placement="bottom">
|
||||||
|
<template slot="title">
|
||||||
|
Save new Rule
|
||||||
|
</template>
|
||||||
|
<a-button
|
||||||
|
icon="plus"
|
||||||
|
type="primary"
|
||||||
|
shape="circle"
|
||||||
|
@click="onRuleSave"
|
||||||
|
>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<draggable
|
||||||
|
v-model="rules"
|
||||||
|
@change="changeOrder"
|
||||||
|
handle=".drag-handle"
|
||||||
|
animation="200"
|
||||||
|
ghostClass="drag-ghost">
|
||||||
|
<transition-group type="transition">
|
||||||
|
<div
|
||||||
|
v-for="(record, index) in rules"
|
||||||
|
:key="`item-${index}`"
|
||||||
|
class="rules-table-item ant-list-item">
|
||||||
|
<div class="rules-table__col rules-table__col--grab drag-handle">
|
||||||
|
<a-icon type="drag"></a-icon>
|
||||||
|
</div>
|
||||||
|
<div class="rules-table__col rules-table__col--rule">
|
||||||
|
{{ record.rule }}
|
||||||
|
</div>
|
||||||
|
<div class="rules-table__col rules-table__col--permission">
|
||||||
|
<permission-editable
|
||||||
|
:defaultValue="record.permission"
|
||||||
|
@change="onPermissionChange(record, $event)" />
|
||||||
|
</div>
|
||||||
|
<div class="rules-table__col rules-table__col--description">
|
||||||
|
<template v-if="record.description">
|
||||||
|
{{ record.description }}
|
||||||
|
</template>
|
||||||
|
<div v-else class="no-description">
|
||||||
|
No description entered.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="rules-table__col rules-table__col--actions">
|
||||||
|
<rule-delete
|
||||||
|
:record="record"
|
||||||
|
@delete="onRuleDelete(record.id)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition-group>
|
||||||
|
</draggable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { api } from '@/api'
|
||||||
|
import draggable from 'vuedraggable'
|
||||||
|
import PermissionEditable from '@/views/iam/PermissionEditable'
|
||||||
|
import RuleDelete from '@/views/iam/RuleDelete'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ProjectRolePermissionTab',
|
||||||
|
components: {
|
||||||
|
RuleDelete,
|
||||||
|
PermissionEditable,
|
||||||
|
draggable
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
resource: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
role: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loadingTable: true,
|
||||||
|
updateTable: false,
|
||||||
|
rules: null,
|
||||||
|
newRule: '',
|
||||||
|
newRulePermission: 'allow',
|
||||||
|
newRuleDescription: '',
|
||||||
|
newRuleSelectError: false,
|
||||||
|
drag: false,
|
||||||
|
apis: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.apis = Object.keys(this.$store.getters.apis).sort((a, b) => a.localeCompare(b))
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
resource: function () {
|
||||||
|
this.fetchData(() => {
|
||||||
|
this.resetNewFields()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
filterOption (input, option) {
|
||||||
|
return (
|
||||||
|
option.componentOptions.children[0].text.toUpperCase().indexOf(input.toUpperCase()) >= 0
|
||||||
|
)
|
||||||
|
},
|
||||||
|
resetNewFields () {
|
||||||
|
this.newRule = ''
|
||||||
|
this.newRulePermission = 'allow'
|
||||||
|
this.newRuleDescription = ''
|
||||||
|
this.newRuleSelectError = false
|
||||||
|
},
|
||||||
|
fetchData (callback = null) {
|
||||||
|
if (!this.resource.id) return
|
||||||
|
api('listProjectRolePermissions', {
|
||||||
|
projectid: this.resource.id,
|
||||||
|
projectroleid: this.role.id
|
||||||
|
}).then(response => {
|
||||||
|
this.rules = response.listprojectrolepermissionsresponse.projectrolepermission
|
||||||
|
}).catch(error => {
|
||||||
|
console.error(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loadingTable = false
|
||||||
|
this.updateTable = false
|
||||||
|
if (callback) callback()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
changeOrder () {
|
||||||
|
api('updateProjectRolePermission', {}, 'POST', {
|
||||||
|
projectid: this.resource.id,
|
||||||
|
projectroleid: this.role.id,
|
||||||
|
ruleorder: this.rules.map(rule => rule.id)
|
||||||
|
}).catch(error => {
|
||||||
|
console.error(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.fetchData()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onRuleDelete (key) {
|
||||||
|
this.updateTable = true
|
||||||
|
api('deleteProjectRolePermission', {
|
||||||
|
id: key,
|
||||||
|
projectid: this.resource.id
|
||||||
|
}).catch(error => {
|
||||||
|
console.error(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.fetchData()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onPermissionChange (record, value) {
|
||||||
|
this.newRulePermission = value
|
||||||
|
if (!record) return
|
||||||
|
this.updateTable = true
|
||||||
|
api('updateProjectRolePermission', {
|
||||||
|
projectid: this.resource.id,
|
||||||
|
projectroleid: this.role.id,
|
||||||
|
projectrolepermissionid: record.id,
|
||||||
|
permission: value
|
||||||
|
}).then(() => {
|
||||||
|
this.fetchData()
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onRuleSelect (value) {
|
||||||
|
this.newRule = value
|
||||||
|
},
|
||||||
|
onRuleSave () {
|
||||||
|
if (!this.newRule) {
|
||||||
|
this.newRuleSelectError = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.updateTable = true
|
||||||
|
api('createProjectRolePermission', {
|
||||||
|
rule: this.newRule,
|
||||||
|
permission: this.newRulePermission,
|
||||||
|
description: this.newRuleDescription,
|
||||||
|
projectroleid: this.role.id,
|
||||||
|
projectid: this.resource.id
|
||||||
|
}).then(() => {
|
||||||
|
}).catch(error => {
|
||||||
|
console.error(error)
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.resetNewFields()
|
||||||
|
this.fetchData()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.main-loading-spinner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
.role-add-btn {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.new-role-controls {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
button {
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-list {
|
||||||
|
max-height: 600px;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
&--overflow-hidden {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-table {
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
padding: 0;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
@media (min-width: 760px) {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
padding-right: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&__col {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 15px;
|
||||||
|
|
||||||
|
@media (min-width: 760px) {
|
||||||
|
padding: 15px 0;
|
||||||
|
|
||||||
|
&:not(:first-child) {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-right: 1px solid #e8e8e8;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--grab {
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
@media (min-width: 760px) {
|
||||||
|
position: relative;
|
||||||
|
top: auto;
|
||||||
|
width: 35px;
|
||||||
|
padding-left: 25px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&--rule,
|
||||||
|
&--description {
|
||||||
|
word-break: break-all;
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
@media (min-width: 760px) {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&--rule {
|
||||||
|
padding-left: 60px;
|
||||||
|
background-color: rgba(#e6f7ff, 0.7);
|
||||||
|
|
||||||
|
@media (min-width: 760px) {
|
||||||
|
padding-left: 0;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&--permission {
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.ant-select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 760px) {
|
||||||
|
width: auto;
|
||||||
|
|
||||||
|
.ant-select {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&--actions {
|
||||||
|
max-width: 60px;
|
||||||
|
width: 100%;
|
||||||
|
padding-right: 0;
|
||||||
|
|
||||||
|
@media (min-width: 760px) {
|
||||||
|
width: auto;
|
||||||
|
max-width: 70px;
|
||||||
|
padding-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&--new {
|
||||||
|
padding-left: 15px;
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
div {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-description {
|
||||||
|
opacity: 0.4;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
|
||||||
|
@media (min-width: 760px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-handle {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-ghost {
|
||||||
|
opacity: 0.5;
|
||||||
|
background: #f0f2f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 5;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 3rem;
|
||||||
|
color: #39A7DE;
|
||||||
|
background-color: rgba(#fff, 0.8);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.rules-table__col--new {
|
||||||
|
.ant-select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.rule-dropdown-error {
|
||||||
|
.ant-input {
|
||||||
|
border-color: #ff0000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
308
ui/src/views/project/iam/ProjectRoleTab.vue
Normal file
308
ui/src/views/project/iam/ProjectRoleTab.vue
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-button type="dashed" icon="plus" style="width: 100%; margin-bottom: 15px" @click="openCreateModal">
|
||||||
|
{{ $t('label.create.project.role') }}
|
||||||
|
</a-button>
|
||||||
|
<a-row :gutter="12">
|
||||||
|
<a-col :md="24" :lg="24">
|
||||||
|
<a-table
|
||||||
|
size="small"
|
||||||
|
:loading="loading"
|
||||||
|
:columns="columns"
|
||||||
|
:dataSource="dataSource"
|
||||||
|
:rowKey="(record,idx) => record.projectid + '-' + idx"
|
||||||
|
:pagination="false">
|
||||||
|
<template slot="expandedRowRender" slot-scope="record">
|
||||||
|
<ProjectRolePermissionTab class="table" :resource="resource" :role="record"/>
|
||||||
|
</template>
|
||||||
|
<template slot="name" slot-scope="record"> {{ record }} </template>
|
||||||
|
<template slot="description" slot-scope="record">
|
||||||
|
{{ record }}
|
||||||
|
</template>
|
||||||
|
<span slot="action" slot-scope="text, record">
|
||||||
|
<a-tooltip placement="top">
|
||||||
|
<template slot="title">
|
||||||
|
{{ $t('label.update.project.role') }}
|
||||||
|
</template>
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
shape="circle"
|
||||||
|
icon="edit"
|
||||||
|
size="small"
|
||||||
|
style="margin:10px"
|
||||||
|
@click="openUpdateModal(record)" />
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip placement="top">
|
||||||
|
<template slot="title">
|
||||||
|
{{ $t('label.remove.project.role') }}
|
||||||
|
</template>
|
||||||
|
<a-button
|
||||||
|
type="danger"
|
||||||
|
shape="circle"
|
||||||
|
icon="delete"
|
||||||
|
size="small"
|
||||||
|
@click="deleteProjectRole(record)"/>
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
</a-table>
|
||||||
|
<a-modal title="Edit Project Role" v-model="editModalVisible" :footer="null" :afterClose="closeAction">
|
||||||
|
<a-form
|
||||||
|
:form="form"
|
||||||
|
@submit="updateProjectRole"
|
||||||
|
layout="vertical">
|
||||||
|
<a-form-item :label="$t('label.name')">
|
||||||
|
<a-input v-decorator="[ 'name' ]"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item :label="$t('label.description')">
|
||||||
|
<a-input v-decorator="[ 'description' ]"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<div :span="24" class="action-button">
|
||||||
|
<a-button @click="closeAction">{{ this.$t('label.cancel') }}</a-button>
|
||||||
|
<a-button type="primary" @click="updateProjectRole" :loading="loading">{{ $t('label.ok') }}</a-button>
|
||||||
|
</div>
|
||||||
|
<span slot="action" slot-scope="text, record">
|
||||||
|
<a-tooltip placement="top">
|
||||||
|
<template slot="title">
|
||||||
|
{{ $t('label.update.project.role') }}
|
||||||
|
</template>
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
shape="circle"
|
||||||
|
icon="edit"
|
||||||
|
size="small"
|
||||||
|
style="margin:10px"
|
||||||
|
@click="openUpdateModal(record)" />
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip placement="top">
|
||||||
|
<template slot="title">
|
||||||
|
{{ $t('label.remove.project.role') }}
|
||||||
|
</template>
|
||||||
|
<a-button
|
||||||
|
type="danger"
|
||||||
|
shape="circle"
|
||||||
|
icon="delete"
|
||||||
|
size="small"
|
||||||
|
@click="deleteProjectRole(record)"/>
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
<a-modal title="Create Project Role" v-model="createModalVisible" :footer="null" :afterClose="closeAction">
|
||||||
|
<a-form
|
||||||
|
:form="form"
|
||||||
|
@submit="createProjectRole"
|
||||||
|
layout="vertical">
|
||||||
|
<a-form-item :label="$t('label.name')">
|
||||||
|
<a-input v-decorator="[ 'name', { rules: [{ required: true, message: 'Please provide input' }] }]"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item :label="$t('label.description')">
|
||||||
|
<a-input v-decorator="[ 'description' ]"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<div :span="24" class="action-button">
|
||||||
|
<a-button @click="closeAction">{{ this.$t('label.cancel') }}</a-button>
|
||||||
|
<a-button type="primary" @click="createProjectRole" :loading="loading">{{ $t('label.ok') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { api } from '@/api'
|
||||||
|
import ProjectRolePermissionTab from '@/views/project/iam/ProjectRolePermissionTab'
|
||||||
|
export default {
|
||||||
|
name: 'ProjectRoleTab',
|
||||||
|
props: {
|
||||||
|
resource: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
ProjectRolePermissionTab
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
columns: [],
|
||||||
|
dataSource: [],
|
||||||
|
loading: false,
|
||||||
|
createModalVisible: false,
|
||||||
|
editModalVisible: false,
|
||||||
|
selectedRole: null,
|
||||||
|
projectPermisssions: [],
|
||||||
|
customStyle: 'margin-bottom: -10px; border-bottom-style: none'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeCreate () {
|
||||||
|
this.form = this.$form.createForm(this)
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.columns = [
|
||||||
|
{
|
||||||
|
title: this.$t('label.name'),
|
||||||
|
dataIndex: 'name',
|
||||||
|
width: '35%',
|
||||||
|
scopedSlots: { customRender: 'name' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: this.$t('label.description'),
|
||||||
|
dataIndex: 'description'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: this.$t('label.action'),
|
||||||
|
dataIndex: 'action',
|
||||||
|
width: 100,
|
||||||
|
scopedSlots: { customRender: 'action' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
resource (newItem, oldItem) {
|
||||||
|
if (!newItem || !newItem.id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.resource = newItem
|
||||||
|
this.fetchData()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchData () {
|
||||||
|
this.loading = true
|
||||||
|
api('listProjectRoles', { projectid: this.resource.id }).then(json => {
|
||||||
|
const projectRoles = json.listprojectrolesresponse.projectrole
|
||||||
|
if (!projectRoles || projectRoles.length === 0) {
|
||||||
|
this.dataSource = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.dataSource = projectRoles
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openUpdateModal (role) {
|
||||||
|
this.selectedRole = role
|
||||||
|
this.editModalVisible = true
|
||||||
|
},
|
||||||
|
openCreateModal () {
|
||||||
|
this.createModalVisible = true
|
||||||
|
},
|
||||||
|
updateProjectRole (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
this.form.validateFields((err, values) => {
|
||||||
|
if (err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var params = {}
|
||||||
|
this.loading = true
|
||||||
|
params.projectid = this.resource.id
|
||||||
|
params.id = this.selectedRole.id
|
||||||
|
for (const key in values) {
|
||||||
|
const input = values[key]
|
||||||
|
if (input === undefined) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
params[key] = input
|
||||||
|
}
|
||||||
|
api('updateProjectRole', params).then(response => {
|
||||||
|
this.$notification.success({
|
||||||
|
message: this.$t('label.update.project.role'),
|
||||||
|
description: this.$t('label.update.project.role')
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
this.fetchData()
|
||||||
|
this.closeAction()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
closeAction () {
|
||||||
|
if (this.editModalVisible) {
|
||||||
|
this.editModalVisible = false
|
||||||
|
}
|
||||||
|
if (this.createModalVisible) {
|
||||||
|
this.createModalVisible = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
createProjectRole (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
this.form.validateFields((err, values) => {
|
||||||
|
if (err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
var params = {}
|
||||||
|
params.projectid = this.resource.id
|
||||||
|
for (const key in values) {
|
||||||
|
const input = values[key]
|
||||||
|
if (input === undefined) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
params[key] = input
|
||||||
|
}
|
||||||
|
api('createProjectRole', params).then(response => {
|
||||||
|
this.$notification.success({
|
||||||
|
message: this.$t('label.create.project.role'),
|
||||||
|
description: this.$t('label.create.project.role')
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
this.fetchData()
|
||||||
|
this.closeAction()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
deleteProjectRole (role) {
|
||||||
|
this.loading = true
|
||||||
|
api('deleteProjectRole', {
|
||||||
|
projectid: this.resource.id,
|
||||||
|
id: role.id
|
||||||
|
}).then(response => {
|
||||||
|
this.$notification.success({
|
||||||
|
message: this.$t('label.delete.project.role'),
|
||||||
|
description: this.$t('label.delete.project.role')
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
this.fetchData()
|
||||||
|
this.closeAction()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.action-button {
|
||||||
|
text-align: right;
|
||||||
|
button {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
x
Reference in New Issue
Block a user