permissions->current_user_has_any()) { wp_die(esc_html__('You do not have permission to access this page.', 'support-provisioning-portal')); } $can_view_portal = $this->permissions->current_user_has(SPP_Permissions::VIEW_PORTAL); $can_manage_templates = $this->permissions->current_user_has(SPP_Permissions::MANAGE_TEMPLATES); $can_manage_settings = $this->permissions->current_user_has(SPP_Permissions::MANAGE_SETTINGS); $can_manage_permissions = $this->permissions->current_user_has(SPP_Permissions::MANAGE_PERMISSIONS); ?>

render_notices(); ?>
render_app_root(); } else { $this->render_admin_only_notice(); } ?>
render_settings(); ?>
render_template_management(); } if ($can_manage_permissions) { $this->render_user_access_management(); } ?>
__('Settings saved.', 'support-provisioning-portal'), 'template_saved' => __('Template saved.', 'support-provisioning-portal'), 'template_removed' => __('Template removed from new provisioning.', 'support-provisioning-portal'), 'template_error' => __('Template could not be saved. Check the fields and confirm the selected Proxmox VM or LXC template exists on the configured node.', 'support-provisioning-portal'), 'user_access_saved' => __('User rights saved.', 'support-provisioning-portal'), 'manager_required' => __('At least one user must keep the Manage user rights permission.', 'support-provisioning-portal'), ]; if (!isset($messages[$notice])) { return; } $class = in_array($notice, ['manager_required', 'template_error'], true) ? 'notice notice-error' : 'notice notice-success'; printf( '

%s

', esc_attr($class), esc_html($messages[$notice]) ); } private function render_admin_only_notice(): void { ?>

repository->admin_templates(); $active_template_keys = $this->repository->active_template_identity_keys(); $proxmox_error = null; try { $proxmox_templates = $this->proxmox->list_templates(); } catch (Throwable $error) { $proxmox_templates = []; $proxmox_error = $error->getMessage(); } ?>

template_identity_label($template)); ?>

normalise_template_type((string) ($template['provisioningType'] ?? 'qemu')); $template_ref = (string) ($template['templateRef'] ?? ''); $is_imported = in_array( $this->template_identity_key($provisioning_type, (int) ($template['vmId'] ?? 0), $template_ref), $active_template_keys, true ); ?>
template_identity_label([ 'provisioningType' => $provisioning_type, 'proxmoxTemplateId' => (int) ($template['vmId'] ?? 0), 'proxmoxTemplateRef' => $template_ref, ])); ?>
Typetemplate_type_label($provisioning_type)); ?> CPU cores Memory MB Disk GB Status

users_for_access_table($search); $definitions = SPP_Permissions::definitions(); ?>

permissions->user_ids_with_permission(SPP_Permissions::MANAGE_PERMISSIONS))) : ?>

permissions->for_user((int) $user->ID); $quota = get_user_meta((int) $user->ID, 'spp_memory_quota_mb', true); ?>
display_name !== '' ? $user->display_name : $user->user_login); ?>
$permissions) : ?>
permissions->current_user_has(SPP_Permissions::MANAGE_SETTINGS)) { wp_die(esc_html__('You do not have permission to save these settings.', 'support-provisioning-portal')); } check_admin_referer('spp_save_settings'); update_option('spp_proxmox_mode', $this->posted_string('spp_proxmox_mode') === 'http' ? 'http' : 'mock'); update_option('spp_proxmox_base_url', $this->sanitize_proxmox_base_url($this->posted_string('spp_proxmox_base_url'))); update_option('spp_proxmox_token_id', sanitize_text_field($this->posted_string('spp_proxmox_token_id'))); $token_secret = sanitize_text_field($this->posted_string('spp_proxmox_token_secret')); if ($token_secret !== '') { update_option('spp_proxmox_token_secret', $token_secret); } update_option('spp_proxmox_node', sanitize_text_field($this->posted_string('spp_proxmox_node'))); update_option('spp_lxc_rootfs_storage', $this->sanitize_proxmox_identifier($this->posted_string('spp_lxc_rootfs_storage'))); update_option('spp_lxc_bridge', $this->sanitize_proxmox_identifier($this->posted_string('spp_lxc_bridge'))); update_option('spp_quota_user_memory_mb', max(0, absint($this->posted_string('spp_quota_user_memory_mb')))); update_option('spp_quota_global_memory_mb', max(0, absint($this->posted_string('spp_quota_global_memory_mb')))); $this->redirect_to_admin_page('settings_saved'); } public function save_template(): void { if (!$this->permissions->current_user_has(SPP_Permissions::MANAGE_TEMPLATES)) { wp_die(esc_html__('You do not have permission to save templates.', 'support-provisioning-portal')); } check_admin_referer('spp_save_template'); $template_id = absint($this->posted_string('spp_template_id')); $action = sanitize_key($this->posted_string('spp_template_action')); if ($action === 'remove') { if ($template_id < 1) { $this->redirect_to_admin_page('template_error'); } $this->repository->deactivate_template($template_id, get_current_user_id()); $this->redirect_to_admin_page('template_removed'); } $data = $this->posted_template_data(); if ($data === null) { $this->redirect_to_admin_page('template_error'); } if (!$this->proxmox_template_exists( (string) $data['provisioning_type'], (int) $data['proxmox_template_id'], (string) $data['proxmox_template_ref'] )) { $this->redirect_to_admin_page('template_error'); } if ($template_id > 0) { $this->repository->update_template($template_id, $data, get_current_user_id()); } else { $this->repository->upsert_template($data, get_current_user_id()); } $this->redirect_to_admin_page('template_saved'); } public function save_user_access(): void { if (!$this->permissions->current_user_has(SPP_Permissions::MANAGE_PERMISSIONS)) { wp_die(esc_html__('You do not have permission to save user rights.', 'support-provisioning-portal')); } check_admin_referer('spp_save_user_access'); $user_ids = $this->posted_user_ids(); $posted_permissions = isset($_POST['spp_permissions']) ? (array) wp_unslash($_POST['spp_permissions']) : []; $posted_quotas = isset($_POST['spp_memory_quota_mb']) ? (array) wp_unslash($_POST['spp_memory_quota_mb']) : []; if (!$this->save_keeps_permission_manager($user_ids, $posted_permissions)) { $this->redirect_to_admin_page('manager_required'); } foreach ($user_ids as $user_id) { $permissions = isset($posted_permissions[$user_id]) && is_array($posted_permissions[$user_id]) ? SPP_Permissions::sanitize_permissions($posted_permissions[$user_id]) : []; $quota_raw = isset($posted_quotas[$user_id]) ? sanitize_text_field((string) $posted_quotas[$user_id]) : ''; $memory_quota_mb = $quota_raw === '' ? null : max(0, absint($quota_raw)); $this->repository->update_user_access($user_id, $permissions, $memory_quota_mb, get_current_user_id()); } $this->redirect_to_admin_page('user_access_saved'); } /** * @return array */ private function users_for_access_table(string $search): array { $args = [ 'fields' => 'all', 'orderby' => 'display_name', 'order' => 'ASC', 'number' => 200, ]; if ($search !== '') { $args['search'] = '*' . $search . '*'; $args['search_columns'] = ['user_login', 'user_email', 'user_nicename', 'display_name']; } $users = get_users($args); return is_array($users) ? $users : []; } private function render_os_type_select(string $selected): void { $options = [ 'LINUX' => 'Linux', 'WINDOWS' => 'Windows', 'APPLIANCE' => 'Appliance', 'OTHER' => 'Other', ]; ?> normalise_template_type($provisioning_type); try { foreach ($this->proxmox->list_templates() as $template) { $candidate_type = $this->normalise_template_type((string) ($template['provisioningType'] ?? 'qemu')); if ($candidate_type !== $provisioning_type) { continue; } if ($candidate_type === 'lxc' && (string) ($template['templateRef'] ?? '') === $template_ref) { return true; } if ($candidate_type === 'qemu' && (int) ($template['vmId'] ?? 0) === $vm_id) { return true; } } } catch (Throwable) { return false; } return false; } /** * @return array|null */ private function posted_template_data(): ?array { $name = sanitize_text_field($this->posted_string('spp_template_name')); $provisioning_type = $this->normalise_template_type($this->posted_string('spp_provisioning_type')); $proxmox_template_id = absint($this->posted_string('spp_proxmox_template_id')); $proxmox_template_ref = sanitize_text_field($this->posted_string('spp_proxmox_template_ref')); $description = sanitize_textarea_field($this->posted_string('spp_template_description')); if ($name === '' || $description === '') { return null; } if ($provisioning_type === 'qemu' && $proxmox_template_id < 1) { return null; } if ($provisioning_type === 'lxc' && $proxmox_template_ref === '') { return null; } return [ 'template_key' => $this->template_identity_key($provisioning_type, $proxmox_template_id, $proxmox_template_ref) . '-' . sanitize_title($name), 'name' => $name, 'description' => $description, 'os_type' => $this->posted_os_type(), 'cpu_cores' => max(1, absint($this->posted_string('spp_cpu_cores'))), 'memory_mb' => max(128, absint($this->posted_string('spp_memory_mb'))), 'disk_gb' => max(1, absint($this->posted_string('spp_disk_gb'))), 'default_ttl_hours' => max(1, min(720, absint($this->posted_string('spp_default_ttl_hours')))), 'provisioning_type' => $provisioning_type, 'proxmox_template_id' => $proxmox_template_id, 'proxmox_template_ref' => $proxmox_template_ref, 'is_active' => $this->posted_string('spp_is_active') === '1', ]; } private function posted_os_type(): string { $os_type = strtoupper(sanitize_key($this->posted_string('spp_os_type'))); return in_array($os_type, ['LINUX', 'WINDOWS', 'APPLIANCE', 'OTHER'], true) ? $os_type : 'OTHER'; } /** * @param array $template */ private function template_identity_label(array $template): string { $type = $this->normalise_template_type((string) ($template['provisioningType'] ?? 'qemu')); if ($type === 'lxc') { return 'LXC ' . (string) ($template['proxmoxTemplateRef'] ?? $template['templateRef'] ?? ''); } return sprintf('PVE VMID %d', (int) ($template['proxmoxTemplateId'] ?? $template['vmId'] ?? 0)); } private function template_type_label(string $type): string { return $this->normalise_template_type($type) === 'lxc' ? 'LXC container' : 'QEMU VM'; } private function template_identity_key(string $provisioning_type, int $proxmox_template_id, string $proxmox_template_ref): string { return $this->normalise_template_type($provisioning_type) === 'lxc' ? 'lxc:' . $proxmox_template_ref : 'qemu:' . $proxmox_template_id; } private function normalise_template_type(string $type): string { return strtolower($type) === 'lxc' ? 'lxc' : 'qemu'; } /** * @return array */ private function posted_user_ids(): array { $raw_user_ids = isset($_POST['spp_user_ids']) ? (array) wp_unslash($_POST['spp_user_ids']) : []; $user_ids = []; foreach ($raw_user_ids as $user_id) { $user_id = absint($user_id); if ($user_id > 0 && !in_array($user_id, $user_ids, true)) { $user_ids[] = $user_id; } } return $user_ids; } /** * @param array $user_ids * @param array $posted_permissions */ private function save_keeps_permission_manager(array $user_ids, array $posted_permissions): bool { $updated_user_ids = array_flip($user_ids); foreach ($this->permissions->user_ids_with_permission(SPP_Permissions::MANAGE_PERMISSIONS) as $manager_id) { if (!isset($updated_user_ids[$manager_id])) { return true; } } foreach ($user_ids as $user_id) { $permissions = isset($posted_permissions[$user_id]) && is_array($posted_permissions[$user_id]) ? SPP_Permissions::sanitize_permissions($posted_permissions[$user_id]) : []; if (in_array(SPP_Permissions::MANAGE_PERMISSIONS, $permissions, true)) { return true; } } return false; } private function redirect_to_admin_page(string $notice): void { wp_safe_redirect(add_query_arg([ 'page' => 'support-provisioning-portal', 'spp_notice' => $notice, ], admin_url('admin.php'))); exit; } }