blob: ba96ad6ee8b5fa3f47ae93bb537ddcde162cdcf8 [file] [log] [blame]
// 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-spin :spinning="loading">
<a-alert
v-if="disableSettings"
banner
:message="$t('message.action.settings.warning.vm.running')" />
<div v-else>
<div v-show="!showAddDetail">
<a-button
type="dashed"
style="width: 100%"
:disabled="!('updateTemplate' in $store.getters.apis && 'updateVirtualMachine' in $store.getters.apis && isAdminOrOwner())"
@click="onShowAddDetail">
<template #icon><plus-outlined /></template>
{{ $t('label.add.setting') }}
</a-button>
</div>
<div v-show="showAddDetail">
<a-input-group
type="text"
compact>
<a-auto-complete
class="detail-input"
ref="keyElm"
:filterOption="filterOption"
v-model:value="newKey"
:options="detailKeys"
:placeholder="$t('label.name')"
@change="e => onAddInputChange(e, 'newKey')" />
<a-input
class="tag-disabled-input"
style=" width: 30px; border-left: 0; pointer-events: none; text-align: center"
placeholder="="
disabled />
<a-auto-complete
class="detail-input"
:filterOption="filterOption"
v-model:value="newValue"
:options="detailValues"
:placeholder="$t('label.value')"
@change="e => onAddInputChange(e, 'newValue')" />
<tooltip-button :tooltip="$t('label.add.setting')" :shape="null" icon="check-outlined" @onClick="addDetail" buttonClass="detail-button" />
<tooltip-button :tooltip="$t('label.cancel')" :shape="null" icon="close-outlined" @onClick="closeDetail" buttonClass="detail-button" />
</a-input-group>
<p v-if="error" style="color: red"> {{ $t(error) }} </p>
</div>
</div>
<a-list size="large">
<a-list-item :key="index" v-for="(item, index) in details">
<a-list-item-meta>
<template #title>
{{ item.name }}
</template>
<template #description>
<div v-if="item.edit" style="display: flex">
<a-auto-complete
style="width: 100%"
v-model:value="item.value"
:options="getDetailOptions(detailOptions[item.name])"
@change="val => handleInputChange(val, index)"
@pressEnter="e => updateDetail(index)" />
<tooltip-button
buttonClass="edit-button"
:tooltip="$t('label.cancel')"
@onClick="hideEditDetail(index)"
v-if="item.edit"
iconType="close-circle-two-tone"
iconTwoToneColor="#f5222d" />
<tooltip-button
buttonClass="edit-button"
:tooltip="$t('label.ok')"
@onClick="updateDetail(index)"
v-if="item.edit"
iconType="check-circle-two-tone"
iconTwoToneColor="#52c41a" />
</div>
<span v-else style="word-break: break-all">{{ item.value }}</span>
</template>
</a-list-item-meta>
<template #actions>
<div
v-if="!disableSettings && 'updateTemplate' in $store.getters.apis &&
'updateVirtualMachine' in $store.getters.apis && isAdminOrOwner() && allowEditOfDetail(item.name)">
<tooltip-button
:tooltip="$t('label.edit')"
icon="edit-outlined"
:disabled="deployasistemplate === true"
v-if="!item.edit"
@onClick="showEditDetail(index)" />
</div>
<div
v-if="!disableSettings && 'updateTemplate' in $store.getters.apis &&
'updateVirtualMachine' in $store.getters.apis && isAdminOrOwner() && allowEditOfDetail(item.name)">
<a-popconfirm
:title="`${$t('label.delete.setting')}?`"
@confirm="deleteDetail(index)"
:okText="$t('label.yes')"
:cancelText="$t('label.no')"
placement="left"
>
<tooltip-button :tooltip="$t('label.delete')" :disabled="deployasistemplate === true" type="primary" :danger="true" icon="delete-outlined" />
</a-popconfirm>
</div>
</template>
</a-list-item>
</a-list>
</a-spin>
</template>
<script>
import { api } from '@/api'
import TooltipButton from '@/components/widgets/TooltipButton'
export default {
components: { TooltipButton },
name: 'DetailSettings',
props: {
resource: {
type: Object,
required: true
}
},
data () {
return {
details: [],
detailOptions: {},
showAddDetail: false,
disableSettings: false,
newKey: '',
newValue: '',
loading: false,
resourceType: 'UserVm',
deployasistemplate: false,
error: false
}
},
watch: {
resource: {
deep: true,
handler (newItem) {
this.updateResource(newItem)
}
}
},
computed: {
detailKeys () {
return Object.keys(this.detailOptions).map(key => {
return { value: key }
})
},
detailValues () {
if (!this.newKey) {
return []
}
if (!Array.isArray(this.detailOptions[this.newKey])) {
if (this.detailOptions[this.newKey]) {
return { value: this.detailOptions[this.newKey] }
} else {
return ''
}
}
return this.detailOptions[this.newKey].map(value => {
return { value: value }
})
}
},
created () {
this.updateResource(this.resource)
},
methods: {
filterOption (input, option) {
return (
option.value.toUpperCase().indexOf(input.toUpperCase()) >= 0
)
},
updateResource (resource) {
this.details = []
if (!resource) {
return
}
this.resourceType = this.$route.meta.resourceType
if (resource.details) {
this.details = Object.keys(resource.details).map(k => {
return { name: k, value: resource.details[k], edit: false }
})
}
api('listDetailOptions', { resourcetype: this.resourceType, resourceid: resource.id }).then(json => {
this.detailOptions = json.listdetailoptionsresponse.detailoptions.details
})
this.disableSettings = (this.$route.meta.name === 'vm' && resource.state !== 'Stopped')
api('listTemplates', { templatefilter: 'all', id: resource.templateid }).then(json => {
this.deployasistemplate = json.listtemplatesresponse.template[0].deployasis
})
},
allowEditOfDetail (name) {
if (this.resource.readonlydetails) {
if (this.resource.readonlydetails.split(',').map(item => item.trim()).includes(name)) {
return false
}
}
return true
},
showEditDetail (index) {
this.details[index].edit = true
this.details[index].originalValue = this.details[index].value
},
hideEditDetail (index) {
this.details[index].edit = false
this.details[index].value = this.details[index].originalValue
},
handleInputChange (val, index) {
this.details[index].value = val
},
getDetailOptions (values) {
if (!values) {
return
}
var data = values.map(value => { return { value: value } })
return data
},
onAddInputChange (val, obj) {
this.error = false
this[obj] = val
},
isAdminOrOwner () {
return ['Admin'].includes(this.$store.getters.userInfo.roletype) ||
(this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.account === this.$store.getters.userInfo.account) ||
this.resource.project && this.resource.projectid === this.$store.getters.project.id
},
getDetailsParam (details) {
var params = {}
var filteredDetails = details
if (this.resource.readonlydetails && filteredDetails) {
filteredDetails = []
var readOnlyDetailNames = this.resource.readonlydetails.split(',').map(item => item.trim())
for (var detail of this.details) {
if (!readOnlyDetailNames.includes(detail.name)) {
filteredDetails.push(detail)
}
}
}
if (filteredDetails.length === 0) {
params.cleanupdetails = true
} else {
filteredDetails.forEach(function (item, index) {
params['details[0].' + item.name] = item.value
})
}
return params
},
runApi () {
var apiName = ''
if (this.resourceType === 'UserVm') {
apiName = 'updateVirtualMachine'
} else if (this.resourceType === 'Template') {
apiName = 'updateTemplate'
}
if (!(apiName in this.$store.getters.apis)) {
this.$notification.error({
message: this.$t('error.execute.api.failed') + ' ' + apiName,
description: this.$t('message.user.not.permitted.api')
})
return
}
var params = { id: this.resource.id }
params = Object.assign(params, this.getDetailsParam(this.details))
this.loading = true
api(apiName, params).then(json => {
var details = {}
if (this.resourceType === 'UserVm' && json.updatevirtualmachineresponse.virtualmachine.details) {
details = json.updatevirtualmachineresponse.virtualmachine.details
} else if (this.resourceType === 'Template' && json.updatetemplateresponse.template.details) {
details = json.updatetemplateresponse.template.details
}
this.details = Object.keys(details).map(k => {
return { name: k, value: details[k], edit: false }
})
}).catch(error => {
this.$notifyError(error)
}).finally(f => {
this.loading = false
this.showAddDetail = false
this.newKey = ''
this.newValue = ''
})
},
addDetail () {
if (this.newKey === '' || this.newValue === '') {
this.error = this.$t('message.error.provide.setting')
return
}
if (!this.allowEditOfDetail(this.newKey)) {
this.error = this.$t('error.unable.to.proceed')
return
}
this.error = false
this.details.push({ name: this.newKey, value: this.newValue })
this.runApi()
},
updateDetail (index) {
this.runApi()
},
deleteDetail (index) {
this.details.splice(index, 1)
this.runApi()
},
onShowAddDetail () {
this.showAddDetail = true
setTimeout(() => {
this.$refs.keyElm.focus()
})
},
closeDetail () {
this.newKey = ''
this.newValue = ''
this.error = false
this.showAddDetail = false
}
}
}
</script>
<style scoped lang="less">
.detail-input {
width: calc(calc(100% / 2) - 45px);
}
.detail-button {
width: 30px;
}
</style>