This is an automated email from the ASF dual-hosted git repository.

shwstppr pushed a commit to branch 4.20
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/4.20 by this push:
     new 8261beca2fb ui: fix info card showing invalid template, iso link 
(#13199)
8261beca2fb is described below

commit 8261beca2fb4415015450f78fbf24c6c679abce3
Author: Abhishek Kumar <[email protected]>
AuthorDate: Fri Sep 11 16:08:56 2026 +0530

    ui: fix info card showing invalid template, iso link (#13199)
    
    Template/ISO for a VM could be in deleted state therefore links should not 
be shown for them in the VM info-card.
    
    Signed-off-by: Abhishek Kumar <[email protected]>
---
 ui/src/components/view/InfoCard.vue | 12 ++++---
 ui/src/utils/links.js               | 72 +++++++++++++++++++++++++++++++++++++
 2 files changed, 79 insertions(+), 5 deletions(-)

diff --git a/ui/src/components/view/InfoCard.vue 
b/ui/src/components/view/InfoCard.vue
index 0272df028a3..34c29923123 100644
--- a/ui/src/components/view/InfoCard.vue
+++ b/ui/src/components/view/InfoCard.vue
@@ -560,7 +560,8 @@
               <div class="resource-detail-item__details">
                 <resource-icon v-if="resource.icon" 
:image="getImage(resource.icon.base64image)" size="1x" style="margin-right: 
5px"/>
                 <SaveOutlined v-else />
-                <router-link :to="{ path: (resource.templateformat === 'ISO' ? 
'/iso/' : '/template/') + resource.templateid }">{{ 
resource.templatedisplaytext || resource.templatename || resource.templateid }} 
</router-link>
+                <router-link v-if="validLinks.template" :to="{ path: 
(resource.templateformat === 'ISO' ? '/iso/' : '/template/') + 
resource.templateid }">{{ resource.templatedisplaytext || resource.templatename 
|| resource.templateid }} </router-link>
+                <span v-else>{{ resource.templatedisplaytext || 
resource.templatename || resource.templateid }}</span>
               </div>
             </div>
             <div class="resource-detail-item" v-if="resource.isoid">
@@ -568,7 +569,8 @@
               <div class="resource-detail-item__details">
                 <resource-icon v-if="resource.icon" 
:image="getImage(resource.icon.base64image)" size="1x" style="margin-right: 
5px"/>
                 <UsbOutlined v-else />
-                  <router-link :to="{ path: '/iso/' + resource.isoid }">{{ 
resource.isodisplaytext || resource.isoname || resource.isoid }} </router-link>
+                <router-link v-if="validLinks.iso" :to="{ path: '/iso/' + 
resource.isoid }">{{ resource.isodisplaytext || resource.isoname || 
resource.isoid }} </router-link>
+                <span v-else>{{ resource.isodisplaytext || resource.isoname || 
resource.isoid }}</span>
               </div>
             </div>
             <div class="resource-detail-item" 
v-if="resource.serviceofferingname && resource.serviceofferingid">
@@ -881,7 +883,7 @@
 <script>
 import { api } from '@/api'
 import { createPathBasedOnVmType } from '@/utils/plugins'
-import { validateLinks } from '@/utils/links'
+import { validateLinksAsync } from '@/utils/links'
 import Console from '@/components/widgets/Console'
 import OsLogo from '@/components/widgets/OsLogo'
 import Status from '@/components/widgets/Status'
@@ -966,12 +968,12 @@ export default {
     },
     resource: {
       deep: true,
-      handler (newData, oldData) {
+      async handler (newData, oldData) {
         if (newData === oldData) return
         this.newResource = newData
         this.showKeys = false
         this.setData()
-        this.validLinks = validateLinks(this.$router, this.isStatic, 
this.resource)
+        this.validLinks = await validateLinksAsync(this.$router, 
this.isStatic, this.resource)
 
         if ('apikey' in this.resource) {
           this.getUserKeys()
diff --git a/ui/src/utils/links.js b/ui/src/utils/links.js
index fa650cdd7b8..3524dcc8f8e 100644
--- a/ui/src/utils/links.js
+++ b/ui/src/utils/links.js
@@ -15,6 +15,27 @@
 // specific language governing permissions and limitations
 // under the License.
 
+import { api } from '@/api'
+
+async function isValidObject (apiName, id, params) {
+  try {
+    const allParams = { ...params, listAll: true, id }
+    const json = await api(apiName, allParams)
+    const responseName = Object.keys(json).find(key => 
key.endsWith('response')) || apiName.toLowerCase() + 'response'
+    const response = json?.[responseName]
+    if (!response) {
+      return false
+    }
+    const objectName = Object.keys(response).find(key => key !== 'count')
+    if (!objectName || !Array.isArray(response[objectName])) {
+      return false
+    }
+    return response[objectName].some(item => item.id === id)
+  } catch (e) {
+    return false
+  }
+}
+
 export function validateLinks (router, isStatic, resource) {
   const validLinks = {
     volume: false
@@ -34,3 +55,54 @@ export function validateLinks (router, isStatic, resource) {
 
   return validLinks
 }
+
+export async function validateLinksAsync (router, isStatic, resource) {
+  const validLinks = {
+    volume: false,
+    template: false,
+    iso: false
+  }
+  const pendingChecks = []
+
+  if (isStatic) {
+    return validLinks
+  }
+
+  if (resource.volumeid && router.resolve('/volume/' + 
resource.volumeid).matched[0].redirect !== '/exception/404') {
+    if (resource.volumestate) {
+      validLinks.volume = resource.volumestate !== 'Expunged'
+    } else {
+      validLinks.volume = true
+    }
+  }
+
+  if (resource.templateid) {
+    const templatePath = (resource.templateformat === 'ISO' ? '/iso/' : 
'/template/') + resource.templateid
+    if (router.resolve(templatePath).matched[0].redirect !== '/exception/404') 
{
+      pendingChecks.push(
+        isValidObject('listTemplates', resource.templateid, { templatefilter: 
'executable' }).then(result => {
+          validLinks.template = result
+        })
+      )
+    }
+  }
+
+  if (resource.isoid) {
+    const isoPath = '/iso/' + resource.isoid
+    if (router.resolve(isoPath).matched[0].redirect !== '/exception/404') {
+      pendingChecks.push(
+        isValidObject('listIsos', resource.isoid, { isofilter: 'executable' 
}).then(result => {
+          validLinks.iso = result
+        })
+      )
+    }
+  }
+
+  if (pendingChecks.length) {
+    await Promise.all(pendingChecks).catch(error => {
+      console.error('Error validating links:', error)
+    })
+  }
+
+  return validLinks
+}

Reply via email to