$product) { $pid = $product['pid'] ?? null; if (!$pid) { continue; } $dbProduct = \WHMCS\Database\Capsule::table('tblproducts') ->where('id', $pid) ->where('servertype', 'VirtFusionDirect') ->first(); if (!$dbProduct) { continue; } // Check if Initial Operating System custom field has a value if (isset($product['customfields']) && is_array($product['customfields'])) { $osSelected = false; $customFields = \WHMCS\Database\Capsule::table('tblcustomfields') ->where('relid', $pid) ->where('type', 'product') ->get(); foreach ($customFields as $field) { if (strtolower(str_replace(' ', '', $field->fieldname)) === 'initialoperatingsystem') { $fieldValue = $product['customfields'][$field->id] ?? ''; if (!empty($fieldValue) && is_numeric($fieldValue)) { $osSelected = true; } break; } } if (!$osSelected) { $errors[] = 'Please select an Operating System for your VPS order.'; } } } return $errors; }); /** * Client Area Footer Output Hook * * Dynamically converts hidden text fields for OS templates and SSH keys * into dropdown selects populated from the VirtFusion API. * Works with all WHMCS themes by using vanilla JavaScript and standard form-control classes. */ add_hook('ClientAreaFooterOutput', 1, function ($vars) { if (!isset($vars['productinfo']['module']) || $vars['productinfo']['module'] !== 'VirtFusionDirect') { return null; } try { $cs = new ConfigureService(); $templates_data = $cs->fetchTemplates( $cs->fetchPackageByDbId($vars['productinfo']['pid']) ?? $cs->fetchPackageId($vars['productinfo']['name']) ); if (empty($templates_data)) { return null; } $baseUrl = ''; $firstServer = \WHMCS\Database\Capsule::table('tblservers') ->where('type', 'VirtFusionDirect') ->where('disabled', 0) ->first(); if ($firstServer) { $baseUrl = rtrim('https://' . $firstServer->hostname, '/'); } $categories = []; $otherTemplates = []; foreach ($templates_data['data'] as $osCategory) { $catTemplates = []; foreach ($osCategory['templates'] as $template) { $catTemplates[] = [ 'id' => $template['id'], 'name' => htmlspecialchars($template['name'], ENT_QUOTES, 'UTF-8'), 'version' => htmlspecialchars($template['version'] ?? '', ENT_QUOTES, 'UTF-8'), 'variant' => htmlspecialchars($template['variant'] ?? '', ENT_QUOTES, 'UTF-8'), 'icon' => $template['icon'] ?? null, 'eol' => $template['eol'] ?? false, 'description' => htmlspecialchars($template['description'] ?? '', ENT_QUOTES, 'UTF-8'), ]; } if (count($catTemplates) <= 1) { $otherTemplates = array_merge($otherTemplates, $catTemplates); } else { $categories[] = [ 'name' => htmlspecialchars($osCategory['name'] ?? 'Unknown', ENT_QUOTES, 'UTF-8'), 'icon' => $osCategory['icon'] ?? null, 'templates' => $catTemplates, ]; } } if (!empty($otherTemplates)) { $categories[] = ['name' => 'Other', 'icon' => null, 'templates' => $otherTemplates]; } $galleryData = ['baseUrl' => $baseUrl, 'categories' => $categories]; $sshKeys = []; $sshKeysOptions = []; if (isset($vars['loggedinuser']) && $vars['loggedinuser']) { $sshKeysData = $cs->getUserSshKeys($vars['loggedinuser']); if ($sshKeysData && isset($sshKeysData['data'])) { $sshKeysOptions = array_values(array_filter(array_map(function ($sshKey) { if ($sshKey['enabled'] === false) { return null; } return [ 'id' => $sshKey['id'], 'name' => htmlspecialchars($sshKey['name'], ENT_QUOTES, 'UTF-8') ]; }, $sshKeysData['data']))); } } $osID = array_values(array_filter(array_map(function ($option) { if ($option['textid'] === 'initialoperatingsystem') { return $option['id']; } }, $vars['customfields'] ?? []))); $sshID = array_values(array_filter(array_map(function ($option) { if ($option['textid'] === 'initialsshkey') { return $option['id']; } }, $vars['customfields'] ?? []))); $osFieldId = $osID[0] ?? null; $sshFieldId = $sshID[0] ?? null; if ($osFieldId === null) { return null; } $systemUrl = Database::getSystemUrl(); return " "; } catch (\Throwable $e) { // Silently fail - don't break the checkout page return null; } });