560, 'background_color' => '#f7f7f7', 'default_model_color' => '#b7b7b7', 'preview_model_url' => '', 'auto_rotate' => 'no', 'preview_auto_rotate' => 'no', 'max_pixel_ratio' => 2.0, 'fps_limit' => 45, 'fullscreen' => 'yes', 'lazy_loading' => 'yes', 'disclaimer_enabled' => 'yes', 'disclaimer_text' => __('3D material preview is approximate. The actual shade and color progression may vary depending on display settings, lighting, model geometry, and filament production batch.', '3d-print-showcase'), 'debug' => 'no', 'delete_data_on_uninstall' => 'no', 'selection_mode' => 'builtin', 'appearance_source' => 'builtin', 'appearance_switching' => 'yes', 'remember_appearance' => 'no', 'appearance_display' => 'labels', 'appearance_tooltips' => 'yes', 'material_source' => 'internal', 'custom_rest_url' => '', 'user_viewer_enabled' => 'yes', 'user_viewer_max_size' => 25, 'user_viewer_formats' => ['stl', 'glb'], 'user_viewer_material_mode' => 'all', 'user_viewer_materials' => [], ]; } public static function all(): array { $saved = get_option(self::OPTION, []); return wp_parse_args(is_array($saved) ? $saved : [], self::defaults()); } public static function get(string $key, mixed $default = null): mixed { $settings = self::all(); return $settings[$key] ?? $default; } public static function sanitize(array $input, array $allowedMaterialSources = [], array $allowedSelectionModes = []): array { $defaults = self::defaults(); $output = self::all(); foreach (['auto_rotate', 'preview_auto_rotate', 'fullscreen', 'lazy_loading', 'disclaimer_enabled', 'debug', 'delete_data_on_uninstall', 'appearance_switching', 'remember_appearance', 'appearance_tooltips', 'user_viewer_enabled'] as $key) { $output[$key] = isset($input[$key]) && 'yes' === $input[$key] ? 'yes' : 'no'; } $output['height'] = min(1200, max(240, absint($input['height'] ?? $defaults['height']))); $output['background_color'] = sanitize_hex_color($input['background_color'] ?? '') ?: $defaults['background_color']; $output['default_model_color'] = sanitize_hex_color($input['default_model_color'] ?? '') ?: $defaults['default_model_color']; $previewModelUrl = esc_url_raw((string) ($input['preview_model_url'] ?? $defaults['preview_model_url'])); $previewPath = (string) wp_parse_url($previewModelUrl, PHP_URL_PATH); $previewFormat = strtolower((string) pathinfo($previewPath, PATHINFO_EXTENSION)); $output['preview_model_url'] = '' === $previewModelUrl || in_array($previewFormat, ['stl', '3mf', 'glb', 'gltf'], true) ? $previewModelUrl : ''; $output['max_pixel_ratio'] = min(3.0, max(1.0, (float) ($input['max_pixel_ratio'] ?? $defaults['max_pixel_ratio']))); $output['fps_limit'] = min(60, max(15, absint($input['fps_limit'] ?? $defaults['fps_limit']))); $output['disclaimer_text'] = sanitize_textarea_field((string) ($input['disclaimer_text'] ?? $defaults['disclaimer_text'])); $output['user_viewer_max_size'] = min(200, max(1, absint($input['user_viewer_max_size'] ?? $defaults['user_viewer_max_size']))); $allowedFormats = ['stl', 'glb', '3mf']; $formats = isset($input['user_viewer_formats']) && is_array($input['user_viewer_formats']) ? $input['user_viewer_formats'] : []; $output['user_viewer_formats'] = array_values(array_intersect($allowedFormats, array_map('sanitize_key', $formats))); if (!$output['user_viewer_formats']) { $output['user_viewer_formats'] = $defaults['user_viewer_formats']; } $materialMode = sanitize_key((string) ($input['user_viewer_material_mode'] ?? $defaults['user_viewer_material_mode'])); $output['user_viewer_material_mode'] = in_array($materialMode, ['all', 'include', 'exclude'], true) ? $materialMode : 'all'; $materialIds = isset($input['user_viewer_materials']) && is_array($input['user_viewer_materials']) ? $input['user_viewer_materials'] : []; $output['user_viewer_materials'] = array_values(array_unique(array_filter(array_map('sanitize_text_field', $materialIds)))); $materialSource = sanitize_key((string) ($input['material_source'] ?? $output['material_source'] ?? $defaults['material_source'])); if ($allowedMaterialSources && !in_array($materialSource, $allowedMaterialSources, true)) { $materialSource = $allowedMaterialSources[0]; } $output['material_source'] = $materialSource; if (array_key_exists('custom_rest_url', $input)) { $output['custom_rest_url'] = esc_url_raw(trim((string) $input['custom_rest_url'])); } $selectionMode = sanitize_key((string) ($input['selection_mode'] ?? $output['selection_mode'] ?? $defaults['selection_mode'])); if ($allowedSelectionModes && !in_array($selectionMode, $allowedSelectionModes, true)) { $selectionMode = $allowedSelectionModes[0]; } $output['selection_mode'] = $selectionMode; $appearanceSource = sanitize_key((string) ($input['appearance_source'] ?? $output['appearance_source'] ?? $defaults['appearance_source'])); $output['appearance_source'] = in_array($appearanceSource, ['builtin', 'variations', 'combined'], true) ? $appearanceSource : $defaults['appearance_source']; $appearanceDisplay = sanitize_key((string) ($input['appearance_display'] ?? $output['appearance_display'] ?? $defaults['appearance_display'])); $output['appearance_display'] = in_array($appearanceDisplay, ['labels', 'swatches'], true) ? $appearanceDisplay : $defaults['appearance_display']; return $output; } public static function sanitize_mapping(array $mapping): array { $clean = []; foreach ($mapping as $key => $identifier) { $key = sanitize_text_field((string) $key); $identifier = sanitize_text_field((string) $identifier); if ('' !== $key && '' !== $identifier) { $clean[$key] = $identifier; } } return $clean; } }