table(); if ($wpdb->get_var("SHOW TABLES LIKE '{$table}'")) { return true; } // Create the table if it doesn't exist if (class_exists('\\Yatra\\Services\\InstallerService')) { \Yatra\Services\InstallerService::createDatabaseTables(); } return (bool) $wpdb->get_var("SHOW TABLES LIKE '{$table}'"); } /** * Get all downloads for a trip */ public function getByTripId(int $tripId): array { global $wpdb; $table = $this->table(); if (!$this->ensureTableExists()) { return []; } $sql = $wpdb->prepare( "SELECT * FROM {$table} WHERE trip_id = %d AND content_type = 'download' ORDER BY sort_order ASC, id ASC", $tripId ); $rows = $wpdb->get_results($sql) ?: []; // Normalize metadata back into expected fields - match TripRepository structure $normalizedRows = []; foreach ($rows as $row) { $metadata = []; if (!empty($row->metadata)) { $decoded = json_decode($row->metadata, true); if (is_array($decoded)) { $metadata = $decoded; } } $attachmentId = $metadata['attachment_id'] ?? null; $protectedPath = $metadata['protected_path'] ?? null; $visibilityMeta = $metadata['visibility'] ?? null; // Use visibility from metadata as primary source of truth // Fall back to access_level mapping only if metadata is missing $visibility = $visibilityMeta ?? 'booked_only'; if ($visibilityMeta === null) { // Only use access_level if visibility metadata is not set if ($row->access_level === 'public') { $visibility = 'public'; } elseif ($row->access_level === 'registered') { $visibility = 'logged_in'; } elseif ($row->access_level === 'customer') { $visibility = 'booked_only'; } } // Create normalized object matching TripRepository structure $normalizedRows[] = (object) [ 'id' => isset($row->id) ? (int) $row->id : 0, 'title' => $row->title ?? '', 'description' => $row->description ?? '', 'attachment_id' => $attachmentId ? (int) $attachmentId : null, 'protected_path' => $protectedPath, 'content_url' => $row->content_url ?? '', 'file_path' => $row->file_path ?? null, 'file_size' => isset($row->file_size) ? (int) $row->file_size : null, 'file_type' => $row->file_type ?? null, 'thumbnail_url' => $row->thumbnail_url ?? null, 'visibility' => $visibility, 'is_downloadable' => isset($row->is_downloadable) ? (bool) $row->is_downloadable : true, 'sort_order' => isset($row->sort_order) ? (int) $row->sort_order : 0, ]; } return $normalizedRows; } /** * Get a download by ID */ public function getById(int $id): ?object { global $wpdb; $table = $this->table(); if (!$this->ensureTableExists()) { return null; } $result = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$table} WHERE id = %d AND content_type = 'download' LIMIT 1", $id ) ); if (!$result) { return null; } // Parse metadata to extract visibility and attachment_id $metadata = null; if (!empty($result->metadata)) { $metadata = json_decode($result->metadata, true); } // Extract visibility from metadata if ($metadata && isset($metadata['visibility'])) { $result->visibility = $metadata['visibility']; } elseif (isset($result->access_level)) { // Fallback to access_level for backward compatibility $result->visibility = $result->access_level; } else { $result->visibility = 'booked_only'; // Default } // Extract attachment_id from metadata if ($metadata && isset($metadata['attachment_id'])) { $result->attachment_id = $metadata['attachment_id']; } return $result; } /** * Update the protected path for a download */ public function updateProtectedPath(int $id, string $protectedPath): void { global $wpdb; $table = $this->table(); if (!$this->ensureTableExists()) { return; } $wpdb->update( $table, ['protected_path' => sanitize_text_field($protectedPath)], ['id' => $id, 'content_type' => 'download'], ['%s'], ['%d', '%s'] ); } /** * Replace all downloads for a trip */ public function replaceForTrip(int $tripId, array $items): void { global $wpdb; $table = $this->table(); if (!$this->ensureTableExists()) { return; } // Delete existing downloads for this trip $wpdb->delete($table, ['trip_id' => $tripId, 'content_type' => 'download'], ['%d', '%s']); $order = 0; foreach ($items as $item) { if (!is_array($item)) { continue; } $title = sanitize_text_field($item['title'] ?? ''); if ($title === '') { continue; } // Normalize visibility $visibility = sanitize_key($item['visibility'] ?? 'booked_only'); if ($visibility === 'paid_only') { $visibility = 'booked_only'; } if (!in_array($visibility, ['public', 'logged_in', 'booked_only'], true)) { $visibility = 'booked_only'; } $accessLevel = 'customer'; $isPublic = 0; $requiresLogin = 1; if ($visibility === 'public') { $accessLevel = 'public'; $isPublic = 1; $requiresLogin = 0; } elseif ($visibility === 'logged_in') { $accessLevel = 'registered'; $isPublic = 0; $requiresLogin = 1; } $attachmentId = isset($item['attachment_id']) ? (int) $item['attachment_id'] : null; $contentUrl = null; $filePath = isset($item['file_path']) ? sanitize_text_field($item['file_path']) : null; $fileType = isset($item['file_type']) ? sanitize_text_field($item['file_type']) : null; $fileSize = isset($item['file_size']) ? (int) $item['file_size'] : null; $thumbnailUrl = isset($item['thumbnail_url']) ? esc_url_raw($item['thumbnail_url']) : null; if ($attachmentId) { $contentUrl = wp_get_attachment_url($attachmentId) ?: null; if (empty($filePath)) { $filePath = get_attached_file($attachmentId) ?: null; } if (empty($fileType)) { $fileType = wp_check_filetype(get_attached_file($attachmentId) ?: '')['type'] ?? null; } if (empty($fileSize)) { $fileSize = (int) filesize(get_attached_file($attachmentId) ?: '') ?: null; } if (empty($thumbnailUrl)) { $thumb = wp_get_attachment_image_src($attachmentId, 'thumbnail'); $thumbnailUrl = $thumb && isset($thumb[0]) ? $thumb[0] : null; } } elseif (!empty($item['content_url'])) { $contentUrl = esc_url_raw($item['content_url']); if (empty($fileType)) { $fileType = wp_check_filetype($contentUrl)['type'] ?? null; } } $metadata = [ 'attachment_id' => $attachmentId, 'protected_path' => isset($item['protected_path']) ? sanitize_text_field($item['protected_path']) : null, 'visibility' => $visibility, ]; $result = $wpdb->insert( $table, [ 'trip_id' => $tripId, 'content_type' => 'download', 'title' => $title, 'description' => isset($item['description']) ? wp_kses_post($item['description']) : null, 'content_url' => $contentUrl, 'metadata' => wp_json_encode($metadata), 'file_path' => $filePath, 'file_size' => $fileSize, 'file_type' => $fileType, 'thumbnail_url' => $thumbnailUrl, 'display_options' => isset($item['display_options']) ? wp_json_encode($item['display_options']) : null, 'access_level' => $accessLevel, 'is_downloadable' => isset($item['enabled']) ? (int) (bool) $item['enabled'] : 1, 'is_public' => $isPublic, 'requires_login' => $requiresLogin, 'sort_order' => isset($item['sort_order']) ? (int) $item['sort_order'] : $order, 'created_by' => get_current_user_id() ?: null, 'updated_by' => get_current_user_id() ?: null, ], [ '%d', // trip_id '%s', // content_type '%s', // title '%s', // description '%s', // content_url '%s', // metadata '%s', // file_path '%d', // file_size '%s', // file_type '%s', // thumbnail_url '%s', // display_options '%s', // access_level '%d', // is_downloadable '%d', // is_public '%d', // requires_login '%d', // sort_order '%d', // created_by '%d', // updated_by ] ); $order++; } } /** * Create a single download */ public function create(array $data): ?int { global $wpdb; $table = $this->table(); if (!$this->ensureTableExists()) { return null; } $title = sanitize_text_field($data['title'] ?? ''); if ($title === '') { return null; } $visibility = sanitize_key($data['visibility'] ?? 'booked_only'); if ($visibility === 'paid_only') { $visibility = 'booked_only'; } if (!in_array($visibility, ['public', 'logged_in', 'booked_only'], true)) { $visibility = 'booked_only'; } $accessLevel = 'customer'; $isPublic = 0; $requiresLogin = 1; if ($visibility === 'public') { $accessLevel = 'public'; $isPublic = 1; $requiresLogin = 0; } elseif ($visibility === 'logged_in') { $accessLevel = 'registered'; $isPublic = 0; $requiresLogin = 1; } $attachmentId = isset($data['attachment_id']) ? (int) $data['attachment_id'] : null; $contentUrl = $attachmentId ? (wp_get_attachment_url($attachmentId) ?: null) : (isset($data['content_url']) ? esc_url_raw($data['content_url']) : null); $filePath = isset($data['file_path']) ? sanitize_text_field($data['file_path']) : null; $fileType = isset($data['file_type']) ? sanitize_text_field($data['file_type']) : null; $fileSize = isset($data['file_size']) ? (int) $data['file_size'] : null; $thumbnailUrl = isset($data['thumbnail_url']) ? esc_url_raw($data['thumbnail_url']) : null; if ($attachmentId) { if (empty($filePath)) { $filePath = get_attached_file($attachmentId) ?: null; } if (empty($fileType)) { $fileType = wp_check_filetype(get_attached_file($attachmentId) ?: '')['type'] ?? null; } if (empty($fileSize)) { $fileSize = (int) filesize(get_attached_file($attachmentId) ?: '') ?: null; } if (empty($thumbnailUrl)) { $thumb = wp_get_attachment_image_src($attachmentId, 'thumbnail'); $thumbnailUrl = $thumb && isset($thumb[0]) ? $thumb[0] : null; } } elseif (!empty($data['content_url']) && empty($fileType)) { $fileType = wp_check_filetype($data['content_url'])['type'] ?? null; } $metadata = [ 'attachment_id' => $attachmentId, 'protected_path' => isset($data['protected_path']) ? sanitize_text_field($data['protected_path']) : null, 'visibility' => $visibility, ]; $result = $wpdb->insert( $table, [ 'trip_id' => (int) ($data['trip_id'] ?? 0), 'content_type' => 'download', 'title' => $title, 'description' => isset($data['description']) ? wp_kses_post($data['description']) : null, 'content_url' => $contentUrl, 'metadata' => wp_json_encode($metadata), 'file_path' => $filePath, 'file_size' => $fileSize, 'file_type' => $fileType, 'thumbnail_url' => $thumbnailUrl, 'display_options' => isset($data['display_options']) ? wp_json_encode($data['display_options']) : null, 'access_level' => $accessLevel, 'is_downloadable' => 1, 'is_public' => $isPublic, 'requires_login' => $requiresLogin, 'sort_order' => isset($data['sort_order']) ? (int) $data['sort_order'] : 0, 'created_by' => get_current_user_id() ?: null, 'updated_by' => get_current_user_id() ?: null, ], ['%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%d', '%d', '%d', '%d', '%d'] ); return $result ? (int) $wpdb->insert_id : null; } /** * Update a download */ public function update(int $id, array $data): bool { global $wpdb; $table = $this->table(); if (!$this->ensureTableExists()) { return false; } $updateData = []; $formats = []; if (isset($data['title'])) { $updateData['title'] = sanitize_text_field($data['title']); $formats[] = '%s'; } if (isset($data['description'])) { $updateData['description'] = wp_kses_post($data['description']); $formats[] = '%s'; } $metadata = []; if (!empty($existing = $this->getById($id))) { if (!empty($existing->metadata)) { $decoded = json_decode($existing->metadata, true); if (is_array($decoded)) { $metadata = $decoded; } } } if (isset($data['attachment_id'])) { $attachmentId = (int) $data['attachment_id']; $updateData['content_url'] = $attachmentId ? (wp_get_attachment_url($attachmentId) ?: null) : null; $formats[] = '%s'; $metadata['attachment_id'] = $attachmentId; } if (isset($data['content_url'])) { $updateData['content_url'] = esc_url_raw($data['content_url']); $formats[] = '%s'; } if (isset($data['protected_path'])) { $metadata['protected_path'] = sanitize_text_field($data['protected_path']); } if (isset($data['visibility'])) { $visibility = sanitize_key($data['visibility']); if ($visibility === 'paid_only') { $visibility = 'booked_only'; } if (!in_array($visibility, ['public', 'logged_in', 'booked_only'], true)) { $visibility = 'booked_only'; } $accessLevel = 'customer'; $isPublic = 0; $requiresLogin = 1; if ($visibility === 'public') { $accessLevel = 'public'; $isPublic = 1; $requiresLogin = 0; } elseif ($visibility === 'logged_in') { $accessLevel = 'registered'; $isPublic = 0; $requiresLogin = 1; } $updateData['access_level'] = $accessLevel; $formats[] = '%s'; $updateData['is_public'] = $isPublic; $formats[] = '%d'; $updateData['requires_login'] = $requiresLogin; $formats[] = '%d'; $metadata['visibility'] = $visibility; } if (!empty($metadata)) { $updateData['metadata'] = wp_json_encode($metadata); $formats[] = '%s'; } if (isset($data['file_path'])) { $updateData['file_path'] = sanitize_text_field($data['file_path']); $formats[] = '%s'; } if (isset($data['file_size'])) { $updateData['file_size'] = (int) $data['file_size']; $formats[] = '%d'; } if (isset($data['file_type'])) { $updateData['file_type'] = sanitize_text_field($data['file_type']); $formats[] = '%s'; } if (isset($data['thumbnail_url'])) { $updateData['thumbnail_url'] = esc_url_raw($data['thumbnail_url']); $formats[] = '%s'; } if (isset($data['display_options'])) { $updateData['display_options'] = wp_json_encode($data['display_options']); $formats[] = '%s'; } if (isset($data['enabled'])) { $updateData['is_downloadable'] = (int) (bool) $data['enabled']; // Map enabled to is_downloadable $formats[] = '%d'; } if (isset($data['sort_order'])) { $updateData['sort_order'] = (int) $data['sort_order']; $formats[] = '%d'; } if (!empty($updateData)) { $result = $wpdb->update( $table, $updateData, ['id' => $id, 'content_type' => 'download'], $formats, ['%d', '%s'] ); } return !empty($updateData) ? $result !== false : true; } /** * Delete a download */ public function delete(int $id): bool { global $wpdb; $table = $this->table(); if (!$this->ensureTableExists()) { return false; } $result = $wpdb->delete($table, ['id' => $id, 'content_type' => 'download'], ['%d', '%s']); return $result !== false; } /** * Delete all downloads for a trip */ public function deleteByTripId(int $tripId): bool { global $wpdb; $table = $this->table(); if (!$this->ensureTableExists()) { return false; } $result = $wpdb->delete($table, ['trip_id' => $tripId, 'content_type' => 'download'], ['%d', '%s']); return $result !== false; } }