| | |
| | | apply_filters('jvb_extract_video_thumbnail', null, $attachment_id, $video_path, $time); |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | /** |
| | | * Phase 1: Validate and process file on disk (no DB writes) |
| | | * Safe to run outside transactions — only does file I/O |
| | | * |
| | | * @return array Prepared file info for registerFile() |
| | | */ |
| | | public function prepareFile(string $temp_path, array $context): array |
| | | { |
| | | if (!file_exists($temp_path)) { |
| | | throw new Exception('Temporary file not found: ' . $temp_path); |
| | | } |
| | | |
| | | $mime_type = mime_content_type($temp_path); |
| | | if (!$this->validateMimeType($temp_path, $mime_type)) { |
| | | throw new Exception('Invalid or potentially dangerous file type'); |
| | | } |
| | | |
| | | $file_type = $this->allowedTypes[$mime_type] ?? 'unknown'; |
| | | if ($file_type === 'unknown') { |
| | | throw new Exception('Unsupported file type: ' . $mime_type); |
| | | } |
| | | |
| | | $config = array_merge($this->defaultConfig, $context['config'] ?? [], $context); |
| | | $post_id = $context['post_id'] ?? 0; |
| | | |
| | | if (!empty($config['allowed_types']) && !in_array($mime_type, $config['allowed_types'])) { |
| | | throw new Exception('File type not allowed: ' . $mime_type); |
| | | } |
| | | |
| | | $this->validateFileSize($temp_path, $file_type, $config); |
| | | |
| | | $directory = $this->generateDirectory( |
| | | $file_type, |
| | | $config['directory_pattern'] ?? '{user_id}/{content}/{file_type}', |
| | | $context |
| | | ); |
| | | $this->ensureDirectories($directory); |
| | | |
| | | $original_name = $context['original_name'] ?? basename($temp_path); |
| | | $filename = $this->generateFilename($original_name, $context); |
| | | $original_path = $this->storeOriginalFromTemp($temp_path, $directory, $filename); |
| | | |
| | | // File-type-specific I/O (conversion, copying) — no DB |
| | | $final_path = match ($file_type) { |
| | | 'image' => $this->prepareImage($original_path, $directory, $filename, $config), |
| | | 'video' => $this->prepareMedia($original_path, $directory, $filename), |
| | | 'document' => $this->prepareMedia($original_path, $directory, $filename), |
| | | default => throw new Exception('Unknown file type'), |
| | | }; |
| | | |
| | | return [ |
| | | 'final_path' => $final_path, |
| | | 'file_type' => $file_type, |
| | | 'filename' => $filename, |
| | | 'post_id' => $post_id, |
| | | 'config' => $config, |
| | | 'context' => $context, |
| | | ]; |
| | | } |
| | | |
| | | /** |
| | | * Phase 2: Register processed file in WordPress (DB writes only) |
| | | * Quick operation — individual WP functions handle their own queries |
| | | * |
| | | * @param array $prepared Output from prepareFile() |
| | | * @return array Standard upload result |
| | | */ |
| | | public function registerFile(array $prepared): array |
| | | { |
| | | $attachment_id = $this->createAttachment( |
| | | $prepared['final_path'], |
| | | $prepared['filename'], |
| | | $prepared['context'], |
| | | $prepared['post_id'] |
| | | ); |
| | | |
| | | // Type-specific post-registration |
| | | if ($prepared['file_type'] === 'image') { |
| | | $alt_text = apply_filters('jvb_upload_alt_text', '', $prepared['context']); |
| | | if ($alt_text !== '') { |
| | | update_post_meta($attachment_id, '_wp_attachment_image_alt', $alt_text); |
| | | } |
| | | } |
| | | |
| | | if ($prepared['file_type'] === 'video' && ($prepared['config']['extract_video_thumbnail'] ?? false)) { |
| | | $this->extractVideoThumbnail( |
| | | $attachment_id, |
| | | $prepared['final_path'], |
| | | $prepared['config']['video_thumbnail_time'] ?? 0 |
| | | ); |
| | | } |
| | | |
| | | return [ |
| | | 'success' => true, |
| | | 'attachment_id' => $attachment_id, |
| | | 'url' => wp_get_attachment_url($attachment_id), |
| | | 'file' => $prepared['final_path'], |
| | | 'type' => $prepared['file_type'], |
| | | ]; |
| | | } |
| | | |
| | | /** |
| | | * Image file I/O: convert or copy to final location |
| | | */ |
| | | protected function prepareImage(string $original_path, string $directory, string $filename, array $config): string |
| | | { |
| | | $full_dir = "{$this->upload_dir}/{$directory}"; |
| | | $original_mime = mime_content_type($original_path); |
| | | |
| | | $should_convert = false; |
| | | $target_format = false; |
| | | |
| | | if ($config['convert'] && $config['convert'] !== false) { |
| | | $target_format = strtolower($config['convert']); |
| | | if (!isset($this->supportedFormats[$target_format])) { |
| | | throw new Exception("Unsupported conversion format: {$target_format}"); |
| | | } |
| | | $should_convert = ($original_mime !== $this->supportedFormats[$target_format]); |
| | | } |
| | | |
| | | if ($should_convert) { |
| | | $final_path = "{$full_dir}/{$filename}.{$target_format}"; |
| | | try { |
| | | $this->convertImage($original_path, $final_path, $target_format, $config['quality'], $config['use_imagick']); |
| | | } catch (Exception $e) { |
| | | JVB()->error()->log('Image conversion failed: ' . $e->getMessage()); |
| | | $ext = pathinfo($original_path, PATHINFO_EXTENSION); |
| | | $final_path = "{$full_dir}/{$filename}.{$ext}"; |
| | | copy($original_path, $final_path); |
| | | } |
| | | } else { |
| | | $ext = pathinfo($original_path, PATHINFO_EXTENSION); |
| | | $final_path = "{$full_dir}/{$filename}.{$ext}"; |
| | | copy($original_path, $final_path); |
| | | } |
| | | |
| | | return $final_path; |
| | | } |
| | | |
| | | /** |
| | | * Video/document file I/O: copy to final location |
| | | */ |
| | | protected function prepareMedia(string $original_path, string $directory, string $filename): string |
| | | { |
| | | $full_dir = "{$this->upload_dir}/{$directory}"; |
| | | $ext = pathinfo($original_path, PATHINFO_EXTENSION); |
| | | $final_path = "{$full_dir}/{$filename}.{$ext}"; |
| | | |
| | | copy($original_path, $final_path); |
| | | |
| | | return $final_path; |
| | | } |
| | | } |