validate(); if (!empty($errors)) { die("Configuration Error: " . implode(', ', $errors)); } $config = [ 'baseUrl' => $configV3->getBaseUrl(), 'timeout' => $configV3->get('api.timeout'), 'headers' => [ 'Content-Type' => 'application/json', 'Accept' => 'application/json' ] ]; $collectionPath = __DIR__ . '/' . $configV3->get('postman_collection'); $testRunner = null; $error = null; $success = null; $currentTab = $_GET['tab'] ?? $_POST['tab'] ?? 'download'; try { if (file_exists($collectionPath)) { $testRunner = new TestRunner($collectionPath, $config); } else { $error = "Postman collection file not found: " . $collectionPath; } } catch (Exception $e) { $error = "Error initializing: " . $e->getMessage(); } // Initialize results storage if (!isset($_SESSION['workflow_results'])) { $_SESSION['workflow_results'] = []; } $results = &$_SESSION['workflow_results']; // Handle AJAX requests (return JSON) if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') { header('Content-Type: application/json'); $action = $_POST['action'] ?? $_GET['action'] ?? ''; $response = ['success' => false, 'error' => 'Unknown action']; try { switch ($action) { case 'box_list_files': $folderId = $_POST['folder_id'] ?? ''; if (empty($folderId)) { $response = ['success' => false, 'error' => 'Folder ID is required']; break; } $retriever = new BoxFileRetriever(); $result = $retriever->listFilesWithTrackingIDs($folderId); if ($result['success']) { // Parse each filename $parser = new FilenameParser(); foreach ($result['files'] as &$file) { $parsed = $parser->parseFilename($file['name']); $file['parsed'] = $parsed; $file['is_valid'] = $parsed ? $parsed['is_valid'] : false; $file['clean_filename'] = $parsed ? $parser->stripUploadComponents($file['name']) : $file['name']; } } $response = $result; break; case 'parse_filename': $filename = $_POST['filename'] ?? ''; if (empty($filename)) { $response = ['success' => false, 'error' => 'Filename is required']; break; } $parser = new FilenameParser(); $parsed = $parser->parseFilename($filename); $response = [ 'success' => true, 'parsed' => $parsed, 'clean_filename' => $parser->stripUploadComponents($filename) ]; break; case 'load_master_metadata': $trackingId = $_POST['tracking_id'] ?? ''; if (empty($trackingId)) { $response = ['success' => false, 'error' => 'Tracking ID is required']; break; } $db = new DatabaseClient(); $stmt = $db->getConnection()->prepare(" SELECT tracking_id, opentext_id, upload_directory, description, brand_code, country_code, language_code, asset_type, full_metadata FROM master_assets WHERE tracking_id = :tracking_id AND status = 'active' "); $stmt->execute(['tracking_id' => $trackingId]); $masterAsset = $stmt->fetch(PDO::FETCH_ASSOC); if ($masterAsset) { // Decode full_metadata JSONB column (contains COMPLETE DAM metadata) if (isset($masterAsset['full_metadata'])) { $masterAsset['metadata'] = json_decode($masterAsset['full_metadata'], true); } else { $masterAsset['metadata'] = []; } $response = [ 'success' => true, 'master_asset' => $masterAsset ]; } else { $response = [ 'success' => false, 'error' => "No master asset found for tracking ID: $trackingId" ]; } break; case 'merge_metadata': $masterMetadata = json_decode($_POST['master_metadata'] ?? '{}', true); $parsedFilename = json_decode($_POST['parsed_filename'] ?? '{}', true); if (empty($masterMetadata) || empty($parsedFilename)) { $response = ['success' => false, 'error' => 'Master metadata and parsed filename are required']; break; } $merger = new MetadataMerger(); $merged = $merger->mergeMetadata($masterMetadata, $parsedFilename); $editableFields = $merger->identifyEditableFields($merged); $response = [ 'success' => true, 'merged' => $merged, 'editable_fields' => $editableFields ]; break; case 'upload_from_box': // Main upload handler for Box files $fileId = $_POST['file_id'] ?? ''; $filename = $_POST['filename'] ?? ''; $trackingId = $_POST['tracking_id'] ?? ''; if (empty($fileId) || empty($filename) || empty($trackingId)) { $response = ['success' => false, 'error' => 'Missing required parameters']; break; } try { // 1. Parse filename first (validate V2 convention) $parser = new FilenameParser(); $parsed = $parser->parseFilename($filename); if (!$parsed || !$parsed['is_valid']) { $errors = $parsed ? implode(', ', $parsed['validation_errors']) : 'Invalid filename'; $response = ['success' => false, 'error' => 'Invalid filename: ' . $errors]; break; } // 2. Load master metadata from database (FULL metadata from JSONB column) $db = new DatabaseClient(); $stmt = $db->getConnection()->prepare(" SELECT tracking_id, opentext_id, upload_directory, description, brand_code, country_code, language_code, asset_type, full_metadata FROM master_assets WHERE tracking_id = :tracking_id AND status = 'active' "); $stmt->execute(['tracking_id' => $trackingId]); $masterAsset = $stmt->fetch(PDO::FETCH_ASSOC); if (!$masterAsset) { $response = ['success' => false, 'error' => "Master asset not found for tracking ID: $trackingId"]; break; } // Decode full_metadata JSONB column (contains COMPLETE DAM metadata - no truncation) if (isset($masterAsset['full_metadata'])) { $masterAsset['metadata'] = json_decode($masterAsset['full_metadata'], true); error_log("Upload from Box: Loaded full metadata - size: " . strlen($masterAsset['full_metadata']) . " bytes"); } else { $masterAsset['metadata'] = []; error_log("Upload from Box: WARNING - No full_metadata found for tracking ID: $trackingId"); } // Check if upload directory exists if (empty($masterAsset['upload_directory'])) { $response = ['success' => false, 'error' => 'Upload directory not found in master asset']; break; } // 3. Download file from Box $retriever = new BoxFileRetriever(); $downloadResult = $retriever->downloadFile($fileId, $filename); if (!$downloadResult['success']) { $response = ['success' => false, 'error' => 'Box download failed: ' . $downloadResult['error']]; break; } $localPath = $downloadResult['local_path']; // 4. Extract video metadata from downloaded file (for width/height) require_once 'src/VideoMetadataExtractor.php'; $videoExtractor = new VideoMetadataExtractor(); $videoMetadata = $videoExtractor->extractVideoMetadata($localPath); // 5. Get clean filename (strip OMG Job Number & Tracking ID) $cleanFilename = $parser->stripUploadComponents($filename); if (!$cleanFilename) { unlink($localPath); $response = ['success' => false, 'error' => 'Failed to generate clean filename']; break; } // 6. Extract ONLY MVP fields from master metadata + update from filename $mvpExtractor = new MetadataExtractorMVP(); $assetRepresentation = $mvpExtractor->buildMVPAssetRepresentation( $masterAsset, $cleanFilename, $parsed, // Parsed V2 filename for field extraction $localPath // Local file for video metadata extraction ); error_log("Upload from Box: Built MVP asset representation with clean filename: $cleanFilename"); // 7. Upload to DAM $apiClient = new ApiClient(); $apiClient->setBaseUrl($configV3->getBaseUrl()); // Get OAuth token from TestRunner if ($testRunner) { $reflection = new ReflectionClass($testRunner); $property = $reflection->getProperty('oauth2Handler'); $property->setAccessible(true); $oauth2Handler = $property->getValue($testRunner); if ($oauth2Handler) { $accessToken = $oauth2Handler->getAccessToken(); $apiClient->setHeaders(['Authorization' => 'Bearer ' . $accessToken]); } } // Rename temp file to clean filename before upload $tempDir = dirname($localPath); $cleanLocalPath = $tempDir . '/' . $cleanFilename; if ($localPath !== $cleanLocalPath) { rename($localPath, $cleanLocalPath); $localPath = $cleanLocalPath; } $uploader = new AssetUploaderSimple($apiClient); $uploadResult = $uploader->uploadWithMetadata( $localPath, $masterAsset['upload_directory'], $assetRepresentation, $videoMetadata // Pass video dimensions to upload manifest ); // 8. Clean up temp file if (file_exists($localPath)) { unlink($localPath); } // 9. Return result if ($uploadResult['success']) { $response = [ 'success' => true, 'filename' => $filename, 'clean_filename' => $cleanFilename, 'asset_id' => $uploadResult['asset_id'] ?? 'Unknown', 'tracking_id' => $trackingId, 'upload_folder' => $masterAsset['upload_directory'], 'master_asset_id' => $masterAsset['opentext_id'], 'http_code' => $uploadResult['http_code'] ?? 202, 'asset_representation' => $assetRepresentation // Include for download/view ]; } else { $response = [ 'success' => false, 'error' => 'Upload failed: ' . ($uploadResult['error'] ?? 'Unknown error'), 'filename' => $filename, 'http_code' => $uploadResult['http_code'] ?? 0 ]; } } catch (Exception $e) { // Clean up on error if (isset($localPath) && file_exists($localPath)) { unlink($localPath); } $response = ['success' => false, 'error' => 'Upload exception: ' . $e->getMessage()]; } break; default: $response = ['success' => false, 'error' => 'Unknown action: ' . $action]; } } catch (Exception $e) { $response = ['success' => false, 'error' => $e->getMessage()]; } echo json_encode($response); exit; } // Handle POST actions if ($_POST && $testRunner) { $action = $_POST['action'] ?? ''; try { switch ($action) { case 'debug_all_campaigns': // Check OAuth status first $oauth2Status = $testRunner->getOAuth2Status(); $results['oauth_debug'] = [ 'has_token' => $oauth2Status['has_token'] ?? false, 'expires_at' => $oauth2Status['expires_at'] ?? 'Unknown' ]; $statusManager = createStatusManager($testRunner); $response = $statusManager->searchAllCampaignTypes($testRunner); if ($response['success']) { $data = json_decode($response['body'], true); $campaigns = CampaignFormatter::getActionableCampaigns($response['body'], []); $results['debug_campaigns'] = $campaigns; $results['debug_raw'] = $data; $results['debug_search_response'] = [ 'http_code' => $response['http_code'], 'url' => $response['url'] ?? 'N/A', 'total_results' => $data['search_result_resource']['search_result']['total_hit_count'] ?? 0, 'response_body_preview' => substr($response['body'] ?? '', 0, 500) ]; $success = "Found " . count($campaigns) . " campaigns total (Local Adaptation + Global comm)"; } else { $error = "Debug search failed: HTTP " . ($response['http_code'] ?? 'N/A') . " - " . ($response['error'] ?? 'Unknown error'); $results['debug_search_response'] = [ 'http_code' => $response['http_code'], 'url' => $response['url'] ?? 'N/A', 'error' => $response['error'] ?? 'Unknown', 'response_preview' => substr($response['body'] ?? '', 0, 500) ]; } break; case 'load_campaigns_a1': $results['a1_campaigns'] = loadCampaignsByStatus($testRunner, 'A1'); $success = "Loaded " . count($results['a1_campaigns']) . " campaigns with status A1"; break; case 'select_campaign_a1': $campaignId = $_POST['campaign_id'] ?? ''; if ($campaignId && isset($results['a1_campaigns'])) { foreach ($results['a1_campaigns'] as $campaign) { if ($campaign['asset_id'] === $campaignId) { $results['selected_campaign'] = $campaign; $success = "Campaign selected: " . $campaign['campaign_name']; break; } } } break; case 'get_master_assets': if (isset($results['selected_campaign'])) { $campaignId = $results['selected_campaign']['asset_id']; $masterFolderId = findMasterAssetsFolder($testRunner, $campaignId, $configV3); if ($masterFolderId) { $results['master_folder_id'] = $masterFolderId; $results['master_assets'] = getAssetsFromFolder($testRunner, $masterFolderId); // ALSO find Final Assets folder ID for uploads (stored in DB) $finalFolderId = findUploadFolder($testRunner, $campaignId, $configV3); if ($finalFolderId) { $results['final_assets_folder_id'] = $finalFolderId; $success = "Found Master Assets (" . count($results['master_assets']) . " files) and Final Assets folder"; } else { $success = "Found Master Assets folder with " . count($results['master_assets']) . " assets"; } } else { $error = "Master Assets folder not found in campaign"; } } break; case 'get_final_assets': if (isset($results['selected_campaign'])) { $campaignId = $results['selected_campaign']['asset_id']; $finalFolderId = findUploadFolder($testRunner, $campaignId, $configV3); if ($finalFolderId) { $results['final_assets_folder_id'] = $finalFolderId; $results['final_assets'] = getAssetsFromFolder($testRunner, $finalFolderId); $success = "Found Final Assets folder with " . count($results['final_assets']) . " assets"; } else { $error = "Final Assets folder not found in campaign"; } } break; case 'download_asset': $assetId = $_POST['asset_id'] ?? ''; $filename = $_POST['filename'] ?? ''; if ($assetId && isset($results['selected_campaign'])) { // Find the asset in our master_assets array to get its full data $assetData = null; if (isset($results['master_assets'])) { foreach ($results['master_assets'] as $asset) { if ($asset['asset_id'] === $assetId) { $assetData = $asset; break; } } } $result = downloadAsset($testRunner, $assetId, $filename, $results['selected_campaign']['campaign_id'], $assetData); $results['last_download'] = $result; if ($result['success']) { $success = "Downloaded: " . $result['filename']; } else { $error = "Download failed: " . $result['error']; } } break; case 'download_asset_metadata': $assetId = $_POST['asset_id'] ?? ''; $filename = $_POST['filename'] ?? 'metadata.json'; if ($assetId && isset($results['master_assets'])) { // Find the asset in master_assets foreach ($results['master_assets'] as $asset) { if ($asset['asset_id'] === $assetId) { // Create JSON file $jsonFilename = pathinfo($filename, PATHINFO_FILENAME) . '_metadata.json'; $jsonContent = json_encode($asset, JSON_PRETTY_PRINT); // Send as download header('Content-Type: application/json'); header('Content-Disposition: attachment; filename="' . $jsonFilename . '"'); header('Content-Length: ' . strlen($jsonContent)); echo $jsonContent; exit; } } } break; case 'download_all_assets': if (isset($results['master_assets']) && isset($results['selected_campaign'])) { $downloadResults = downloadAllAssets($testRunner, $results['master_assets'], $results['selected_campaign']['campaign_id']); $results['bulk_download'] = $downloadResults; // Store master assets metadata for later upload reference $results['master_assets_metadata'] = $results['master_assets']; $success = "Downloaded {$downloadResults['successful']} of {$downloadResults['total']} assets"; } break; case 'download_and_upload_to_box': if (isset($results['master_assets']) && isset($results['selected_campaign'])) { $campaign = $results['selected_campaign']; // Download from DAM $downloadResults = downloadAllAssets($testRunner, $results['master_assets'], $campaign['campaign_id']); // Upload to Box with upload folder ID $boxResults = uploadAssetsToBox( $downloadResults, $campaign['campaign_id'], $campaign['campaign_name'], $results['master_assets'], $results['final_assets_folder_id'] ?? null ); $results['bulk_download'] = $downloadResults; $results['box_upload'] = $boxResults; $success = "Downloaded {$downloadResults['successful']} assets and uploaded {$boxResults['successful']} to Box"; } break; case 'reset_to_a1': $campaignId = $_POST['campaign_id'] ?? ''; if ($campaignId) { $statusManager = createStatusManager($testRunner); $statusResult = $statusManager->updateCampaignStatus($campaignId, 'A1'); if ($statusResult['success']) { $success = "Campaign reset to A1 status for testing"; // Clear session data so we can start fresh unset($results['selected_campaign']); unset($results['selected_campaign_a2']); unset($results['master_assets_metadata']); } else { $errorMsg = "Unknown error"; if (isset($statusResult['response']['body'])) { $responseData = json_decode($statusResult['response']['body'], true); if ($responseData && isset($responseData['exception_body'])) { $errorMsg = $responseData['exception_body']['message'] ?? $responseData['exception_body']['debug_message'] ?? 'API Error'; } elseif ($responseData && isset($responseData['error'])) { $errorMsg = $responseData['error']; } } $error = "Failed to reset to A1 (HTTP {$statusResult['http_code']}): {$errorMsg}"; } } $currentTab = $_POST['tab'] ?? 'download'; break; case 'update_status_to_a2': if (isset($results['selected_campaign'])) { $statusManager = createStatusManager($testRunner); $statusResult = $statusManager->updateCampaignStatus( $results['selected_campaign']['asset_id'], 'A2' ); if ($statusResult['success']) { $results['status_update'] = $statusResult; $results['selected_campaign']['status'] = 'A2'; $success = "Campaign status updated to A2: Selected Master Assets sent to Agency"; } else { // Extract error details from API response $errorMsg = "Unknown error"; if (isset($statusResult['response']['body'])) { $responseData = json_decode($statusResult['response']['body'], true); if ($responseData && isset($responseData['exception_body'])) { $errorMsg = $responseData['exception_body']['message'] ?? $responseData['exception_body']['debug_message'] ?? 'API Error'; } elseif ($responseData && isset($responseData['error'])) { $errorMsg = $responseData['error']; } } $error = "Failed to update status to A2 (HTTP {$statusResult['http_code']}): {$errorMsg}"; // Store full result for debugging $results['status_update_error'] = $statusResult; } } break; case 'load_campaigns_a2': $results['a2_campaigns'] = loadCampaignsByStatus($testRunner, 'A2'); $success = "Loaded " . count($results['a2_campaigns']) . " campaigns with status A2"; $currentTab = 'upload'; break; case 'select_campaign_a2': $campaignId = $_POST['campaign_id'] ?? ''; if ($campaignId && isset($results['a2_campaigns'])) { foreach ($results['a2_campaigns'] as $campaign) { if ($campaign['asset_id'] === $campaignId) { $results['selected_campaign_a2'] = $campaign; $success = "Campaign selected: " . $campaign['campaign_name']; $currentTab = 'upload'; break; } } } break; case 'get_upload_folder': if (isset($results['selected_campaign_a2'])) { $campaignId = $results['selected_campaign_a2']['asset_id']; // Find Final Assets or Localized Assets folder for upload $uploadFolderId = findUploadFolder($testRunner, $campaignId, $configV3); if ($uploadFolderId) { $results['upload_folder_id'] = $uploadFolderId; // Get Master Assets to copy their metadata try { $masterFolderId = findMasterAssetsFolder($testRunner, $campaignId, $configV3); if ($masterFolderId) { $fullMasterAssets = getAssetsFromFolder($testRunner, $masterFolderId); // Store lightweight version - only IDs and basic info $results['master_assets_for_upload'] = []; if (!empty($fullMasterAssets)) { foreach ($fullMasterAssets as $asset) { // Only store minimal fields - no huge metadata structure $lightAsset = [ 'asset_id' => $asset['asset_id'] ?? null, 'name' => $asset['name'] ?? 'Unknown', 'mime_type' => $asset['mime_type'] ?? null ]; // Safely extract metadata_model_id if it exists if (isset($asset['metadata_model_id'])) { $lightAsset['metadata_model_id'] = $asset['metadata_model_id']; } // Safely extract security policy IDs only if (isset($asset['security_policy_list']) && is_array($asset['security_policy_list'])) { $policyIds = []; foreach ($asset['security_policy_list'] as $policy) { if (isset($policy['id'])) { $policyIds[] = ['id' => $policy['id']]; } } if (!empty($policyIds)) { $lightAsset['security_policy_list'] = $policyIds; } } $results['master_assets_for_upload'][] = $lightAsset; } } $success = "Found upload folder and " . count($results['master_assets_for_upload']) . " master assets for reference"; } else { $success = "Found upload target folder (no master assets folder found)"; } } catch (Exception $e) { $success = "Found upload target folder (master assets error: " . $e->getMessage() . ")"; } } else { $error = "Upload folder not found in campaign"; } } $currentTab = 'upload'; break; case 'test_upload_to_folder': // Test upload to specific folder provided by Ferrero team $testFolderId = $_POST['test_folder_id'] ?? 'e96080ba0cd1427d253a28a87504b6665eaa02cb'; if (isset($_FILES['test_upload_file'])) { $tmpName = $_FILES['test_upload_file']['tmp_name']; $fileName = $_FILES['test_upload_file']['name']; $fileError = $_FILES['test_upload_file']['error']; if ($fileError === UPLOAD_ERR_OK) { $apiClient = new ApiClient(); $configV3 = new ConfigV3(); $apiClient->setBaseUrl($configV3->getBaseUrl()); $oauth2Handler = new ReflectionProperty($testRunner, 'oauth2Handler'); $oauth2Handler->setAccessible(true); $oauth2HandlerInstance = $oauth2Handler->getValue($testRunner); if ($oauth2HandlerInstance) { $apiClient->setHeader('Authorization', $oauth2HandlerInstance->getAuthHeader()); } // Use SIMPLE uploader that exactly matches standalone script $uploader = new AssetUploaderSimple($apiClient); // Create a copy with the actual filename $actualFilePath = sys_get_temp_dir() . '/' . $fileName; copy($tmpName, $actualFilePath); $result = $uploader->uploadFile($actualFilePath, $testFolderId); // Clean up @unlink($actualFilePath); $results['test_upload_result'] = $result; $results['test_folder_id'] = $testFolderId; if ($result['success']) { $success = "✅ TEST UPLOAD SUCCESSFUL to folder {$testFolderId}!"; } else { $error = "Test upload failed: " . $result['error']; } } else { $error = "File upload error: " . $fileError; } } $currentTab = 'upload'; break; case 'upload_files': if (isset($_FILES['upload_files']) && isset($results['upload_folder_id'])) { // Get selected master asset lightweight data $selectedMasterAssetLight = null; $masterMetadataIndex = $_POST['master_metadata_index'] ?? '0'; // Use master_assets_for_upload (lightweight version) if ($masterMetadataIndex !== '' && isset($results['master_assets_for_upload'][$masterMetadataIndex])) { $selectedMasterAssetLight = $results['master_assets_for_upload'][$masterMetadataIndex]; } elseif (isset($results['master_assets_for_upload']) && !empty($results['master_assets_for_upload'])) { // If no selection, use first master asset as default $selectedMasterAssetLight = $results['master_assets_for_upload'][0]; } // Use the lightweight master asset data directly // Full metadata fetch causes crashes - we only need model ID and security policies $selectedMasterAsset = $selectedMasterAssetLight; // Store debug info about selected master asset and uploaded file $uploadFileName = is_array($_FILES['upload_files']['name']) ? $_FILES['upload_files']['name'][0] : $_FILES['upload_files']['name']; $uploadTmpName = is_array($_FILES['upload_files']['tmp_name']) ? $_FILES['upload_files']['tmp_name'][0] : $_FILES['upload_files']['tmp_name']; $uploadMimeType = file_exists($uploadTmpName) ? mime_content_type($uploadTmpName) : 'Unknown'; $results['upload_debug'] = [ 'selected_master_asset_id' => $selectedMasterAsset['asset_id'] ?? 'N/A', 'selected_master_name' => $selectedMasterAsset['name'] ?? 'N/A', 'metadata_model_id' => $selectedMasterAsset['metadata_model_id'] ?? 'NOT FOUND', 'has_security_policies' => isset($selectedMasterAsset['security_policy_list']) ? 'YES' : 'NO', 'security_policy_count' => isset($selectedMasterAsset['security_policy_list']) ? count($selectedMasterAsset['security_policy_list']) : 0, 'master_mime_type' => $selectedMasterAsset['mime_type'] ?? 'Unknown', 'upload_filename' => $uploadFileName, 'upload_detected_mime' => $uploadMimeType, 'upload_folder_id' => $results['upload_folder_id'] ]; $uploadResults = uploadFiles($testRunner, $_FILES['upload_files'], $results['upload_folder_id'], $selectedMasterAsset); $results['upload_results'] = $uploadResults; if ($uploadResults['successful'] > 0) { $success = "Uploaded {$uploadResults['successful']} of {$uploadResults['total']} files"; } else { $error = "All uploads failed"; } } $currentTab = 'upload'; break; case 'update_status_to_a3': if (isset($results['selected_campaign_a2'])) { $statusManager = createStatusManager($testRunner); $statusResult = $statusManager->updateCampaignStatus( $results['selected_campaign_a2']['asset_id'], 'A3' ); if ($statusResult['success']) { $results['status_update_a3'] = $statusResult; $success = "Campaign status updated to A3: Localized Asset received from Agency"; } else { // Extract error details from API response $errorMsg = "Unknown error"; if (isset($statusResult['response']['body'])) { $responseData = json_decode($statusResult['response']['body'], true); if ($responseData && isset($responseData['exception_body'])) { $errorMsg = $responseData['exception_body']['message'] ?? $responseData['exception_body']['debug_message'] ?? 'API Error'; } elseif ($responseData && isset($responseData['error'])) { $errorMsg = $responseData['error']; } } $error = "Failed to update status to A3 (HTTP {$statusResult['http_code']}): {$errorMsg}"; $results['status_update_a3_error'] = $statusResult; } } $currentTab = 'upload'; break; case 'debug_all_campaigns_rework': // Check OAuth status first $oauth2Status = $testRunner->getOAuth2Status(); $results['oauth_debug_rework'] = [ 'has_token' => $oauth2Status['has_token'] ?? false, 'expires_at' => $oauth2Status['expires_at'] ?? 'Unknown' ]; $statusManager = createStatusManager($testRunner); $response = $statusManager->searchAllCampaigns($testRunner); if ($response['success']) { $data = json_decode($response['body'], true); $campaigns = CampaignFormatter::getActionableCampaigns($response['body'], []); $results['debug_campaigns_rework'] = $campaigns; $results['debug_raw_rework'] = $data; $results['debug_search_response_rework'] = [ 'http_code' => $response['http_code'], 'url' => $response['url'] ?? 'N/A', 'total_results' => $data['search_result_resource']['search_result']['total_hit_count'] ?? 0, 'response_body_preview' => substr($response['body'] ?? '', 0, 500) ]; $success = "Found " . count($campaigns) . " campaigns total (without status filter)"; } else { $error = "Debug search failed: HTTP " . ($response['http_code'] ?? 'N/A') . " - " . ($response['error'] ?? 'Unknown error'); $results['debug_search_response_rework'] = [ 'http_code' => $response['http_code'], 'url' => $response['url'] ?? 'N/A', 'error' => $response['error'] ?? 'Unknown', 'response_preview' => substr($response['body'] ?? '', 0, 500) ]; } $currentTab = 'rework'; break; case 'reset_to_a5': $campaignId = $_POST['campaign_id'] ?? ''; if ($campaignId) { $statusManager = createStatusManager($testRunner); $statusResult = $statusManager->updateCampaignStatus($campaignId, 'A5'); if ($statusResult['success']) { $success = "Campaign reset to A5 status for testing"; // Clear session data unset($results['selected_campaign_a5']); unset($results['rework_assets']); } else { $errorMsg = "Unknown error"; if (isset($statusResult['response']['body'])) { $responseData = json_decode($statusResult['response']['body'], true); if ($responseData && isset($responseData['exception_body'])) { $errorMsg = $responseData['exception_body']['message'] ?? $responseData['exception_body']['debug_message'] ?? 'API Error'; } elseif ($responseData && isset($responseData['error'])) { $errorMsg = $responseData['error']; } } $error = "Failed to reset to A5 (HTTP {$statusResult['http_code']}): {$errorMsg}"; } } $currentTab = 'rework'; break; case 'load_campaigns_a5': $results['a5_campaigns'] = loadCampaignsByStatus($testRunner, 'A5'); $success = "Loaded " . count($results['a5_campaigns']) . " campaigns with status A5"; $currentTab = 'rework'; break; case 'select_campaign_a5': $campaignId = $_POST['campaign_id'] ?? ''; if ($campaignId && isset($results['a5_campaigns'])) { foreach ($results['a5_campaigns'] as $campaign) { if ($campaign['asset_id'] === $campaignId) { $results['selected_campaign_a5'] = $campaign; $success = "Campaign selected: " . $campaign['campaign_name']; $currentTab = 'rework'; break; } } } break; case 'get_rework_assets': if (isset($results['selected_campaign_a5'])) { $campaignId = $results['selected_campaign_a5']['asset_id']; // Find folder with rework assets (could be Final Assets or Rework folder) $reworkFolderId = findUploadFolder($testRunner, $campaignId, $configV3); if ($reworkFolderId) { $results['rework_folder_id'] = $reworkFolderId; $results['rework_assets'] = getAssetsFromFolder($testRunner, $reworkFolderId); $success = "Found " . count($results['rework_assets']) . " assets needing rework"; } else { $error = "Rework assets folder not found"; } } $currentTab = 'rework'; break; case 'download_rework_asset': $assetId = $_POST['asset_id'] ?? ''; $filename = $_POST['filename'] ?? ''; if ($assetId && isset($results['selected_campaign_a5'])) { $assetData = null; if (isset($results['rework_assets'])) { foreach ($results['rework_assets'] as $asset) { if ($asset['asset_id'] === $assetId) { $assetData = $asset; break; } } } $result = downloadAsset($testRunner, $assetId, $filename, $results['selected_campaign_a5']['campaign_id'], $assetData); $results['last_rework_download'] = $result; if ($result['success']) { $success = "Downloaded rework asset: " . $result['filename']; } else { $error = "Download failed: " . $result['error']; } } $currentTab = 'rework'; break; case 'download_all_rework': if (isset($results['rework_assets']) && isset($results['selected_campaign_a5'])) { $downloadResults = downloadAllAssets($testRunner, $results['rework_assets'], $results['selected_campaign_a5']['campaign_id']); $results['rework_bulk_download'] = $downloadResults; $success = "Downloaded {$downloadResults['successful']} of {$downloadResults['total']} rework assets"; } $currentTab = 'rework'; break; case 'update_status_to_a6': if (isset($results['selected_campaign_a5'])) { $statusManager = createStatusManager($testRunner); $statusResult = $statusManager->updateCampaignStatus( $results['selected_campaign_a5']['asset_id'], 'A6' ); if ($statusResult['success']) { $results['status_update_a6'] = $statusResult; $success = "Campaign status updated to A6: Assets to be reworked received by the Agency"; } else { // Extract error details from API response $errorMsg = "Unknown error"; if (isset($statusResult['response']['body'])) { $responseData = json_decode($statusResult['response']['body'], true); if ($responseData && isset($responseData['exception_body'])) { $errorMsg = $responseData['exception_body']['message'] ?? $responseData['exception_body']['debug_message'] ?? 'API Error'; } elseif ($responseData && isset($responseData['error'])) { $errorMsg = $responseData['error']; } } $error = "Failed to update status to A6 (HTTP {$statusResult['http_code']}): {$errorMsg}"; $results['status_update_a6_error'] = $statusResult; } } $currentTab = 'rework'; break; case 'load_campaigns_b1': $results['b1_campaigns'] = loadCampaignsGlobal($testRunner, 'B1'); $success = "Loaded " . count($results['b1_campaigns']) . " Global campaigns with status B1"; break; case 'select_campaign_b1': $campaignId = $_POST['campaign_id'] ?? ''; if ($campaignId && isset($results['b1_campaigns'])) { foreach ($results['b1_campaigns'] as $campaign) { if ($campaign['asset_id'] === $campaignId) { $results['selected_campaign_b1'] = $campaign; $success = "Campaign selected: " . $campaign['campaign_name']; break; } } } $currentTab = 'global-masters'; break; case 'get_global_master_assets': if (isset($results['selected_campaign_b1'])) { $campaignId = $results['selected_campaign_b1']['asset_id']; // B1 campaigns use "05. Final Assets" folder (not "01. Master Assets") $finalAssetsFolderId = findUploadFolder($testRunner, $campaignId, $configV3); if ($finalAssetsFolderId) { $results['global_master_folder_id'] = $finalAssetsFolderId; $results['global_master_assets'] = getAssetsFromFolder($testRunner, $finalAssetsFolderId); $success = "Found " . count($results['global_master_assets']) . " Global Master assets in Final Assets folder"; } else { $error = "Final Assets folder not found in campaign (B1 campaigns use '05. Final Assets')"; } } $currentTab = 'global-masters'; break; case 'update_status_to_b2': $campaignId = $_POST['campaign_id'] ?? null; if (!$campaignId && isset($results['selected_campaign_b1'])) { $campaignId = $results['selected_campaign_b1']['asset_id']; } if ($campaignId) { $statusManager = createStatusManager($testRunner); $statusResult = $statusManager->updateCampaignStatus($campaignId, 'B2'); if ($statusResult['success']) { $results['status_update_b2'] = $statusResult; $success = "Campaign status updated to B2: Master campaign received from Agency"; } else { $error = "Failed to update status to B2"; $results['status_update_b2_error'] = $statusResult; } } $currentTab = 'global-masters'; break; case 'reset_to_b1': $campaignId = $_POST['campaign_id'] ?? ''; if ($campaignId) { $statusManager = createStatusManager($testRunner); $statusResult = $statusManager->updateCampaignStatus($campaignId, 'B1'); if ($statusResult['success']) { $success = "Campaign status reset to B1"; unset($results['b1_campaigns']); } else { $error = "Failed to reset status to B1"; } } break; case 'debug_all_global_campaigns': $statusManager = createStatusManager($testRunner); $response = $statusManager->searchGlobalCampaigns($testRunner); if ($response['success']) { $data = json_decode($response['body'], true); $campaigns = CampaignFormatter::getActionableCampaigns($response['body'], []); $results['debug_global_campaigns'] = $campaigns; $success = "Found " . count($campaigns) . " Global campaigns total"; } else { $error = "Debug search failed for Global campaigns"; } break; // Debug View Actions case 'load_all_campaigns_debug': $statusManager = createStatusManager($testRunner); $allStatuses = ['A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'B1', 'B2']; $allCampaigns = []; foreach ($allStatuses as $status) { $campaigns = loadCampaignsByStatus($testRunner, $status); foreach ($campaigns as $campaign) { $campaign['current_status'] = $status; $allCampaigns[] = $campaign; } } $results['debug_all_campaigns'] = $allCampaigns; $success = "Loaded " . count($allCampaigns) . " campaigns across all statuses"; $currentTab = 'debug-view'; break; case 'update_campaign_status_debug': $selectedIndex = $_POST['selected_campaign'] ?? null; $targetStatus = $_POST['target_status'] ?? null; if ($selectedIndex !== null && $targetStatus && isset($results['debug_all_campaigns'][$selectedIndex])) { $campaign = $results['debug_all_campaigns'][$selectedIndex]; $campaignId = $campaign['asset_id']; $campaignName = $campaign['campaign_name']; $campaignNumber = $campaign['campaign_id'] ?? 'N/A'; $oldStatus = $campaign['current_status']; $statusManager = createStatusManager($testRunner); $statusResult = $statusManager->updateCampaignStatus($campaignId, $targetStatus); if ($statusResult['success']) { $results['status_update_result'] = [ 'success' => true, 'campaign_name' => $campaignName, 'campaign_number' => $campaignNumber, 'old_status' => $oldStatus, 'new_status' => $targetStatus ]; $success = "Campaign status updated: {$oldStatus} → {$targetStatus}"; unset($results['debug_all_campaigns']); } else { $results['status_update_result'] = [ 'success' => false, 'error' => $statusResult['error'] ?? 'Unknown error', 'campaign_name' => $campaignName ]; $error = "Failed to update campaign status"; } } else { $error = "Please select a campaign and target status"; } $currentTab = 'debug-view'; break; case 'view_master_assets_folder': case 'view_final_assets_folder': $folderType = ($action === 'view_master_assets_folder') ? 'Master Assets' : 'Final Assets'; $folderName = ($action === 'view_master_assets_folder') ? '01. Master Assets' : '01. Final Assets'; $assetManager = createAssetManager($testRunner); $searchResult = $assetManager->searchForFolder($folderName); if ($searchResult['success'] && count($searchResult['folders']) > 0) { $folderId = $searchResult['folders'][0]['id']; $contentsResult = $assetManager->getFolderContents($folderId); if ($contentsResult['success']) { $items = $contentsResult['items']; foreach ($items as &$item) { if ($item['type'] === 'folder') { $item['campaign_id'] = extractCampaignId($item); $item['status'] = extractStatus($item); } } $results['debug_folder_contents'] = [ 'folder_name' => $folderName, 'folder_type' => $folderType, 'folder_id' => $folderId, 'items' => $items ]; $success = "Found " . count($items) . " items in {$folderName}"; } else { $results['debug_folder_error'] = "Failed to load folder contents"; $error = $results['debug_folder_error']; } } else { $results['debug_folder_error'] = "Folder '{$folderName}' not found"; $error = $results['debug_folder_error']; } $currentTab = 'debug-view'; break; case 'select_campaign_and_view_folders': $campaignIndex = $_POST['campaign_index'] ?? null; $folderType = $_POST['folder_type'] ?? 'master'; if ($campaignIndex !== null && isset($results['debug_all_campaigns'][$campaignIndex])) { $campaign = $results['debug_all_campaigns'][$campaignIndex]; $results['selected_campaign_debug'] = $campaign; // Find the campaign's folder $campaignId = $campaign['asset_id']; $folderName = ($folderType === 'master') ? '01. Master Assets' : '01. Final Assets'; $results['debug_folder_view_type'] = $folderName; // Use existing helper functions - EXACT same as Download tab if ($folderType === 'master') { $folderId = findMasterAssetsFolder($testRunner, $campaignId, $configV3); } else { $folderId = findUploadFolder($testRunner, $campaignId, $configV3); } if ($folderId) { // Get ALL assets from folder - same as Download tab $assets = getAssetsFromFolder($testRunner, $folderId); // Store assets directly (don't transform, keep original structure) $results['debug_campaign_assets'] = $assets; $results['debug_folder_id'] = $folderId; $results['debug_folder_name'] = $folderName; $results['debug_folder_type'] = ucfirst($folderType) . ' Assets'; $success = "Found " . count($assets) . " assets in {$folderName} for campaign " . ($campaign['campaign_id'] ?? ''); } else { $results['debug_folder_error'] = "Folder '{$folderName}' not found in campaign"; $error = $results['debug_folder_error']; } } else { $error = "Campaign not found"; } $currentTab = 'debug-view'; break; case 'view_folder_contents': $folderId = $_POST['folder_id'] ?? ''; $folderName = $_POST['folder_name'] ?? 'Unknown Folder'; if ($folderId) { $assetManager = createAssetManager($testRunner); $contentsResult = $assetManager->getFolderContents($folderId); if ($contentsResult['success']) { $items = $contentsResult['items']; foreach ($items as &$item) { if ($item['type'] === 'folder') { $item['campaign_id'] = extractCampaignId($item); $item['status'] = extractStatus($item); } } $results['debug_folder_contents'] = [ 'folder_name' => $folderName, 'folder_type' => 'Subfolder', 'folder_id' => $folderId, 'items' => $items ]; $success = "Found " . count($items) . " items in {$folderName}"; } else { $results['debug_folder_error'] = "Failed to load folder"; $error = $results['debug_folder_error']; } } else { $error = "Folder ID is required"; } $currentTab = 'debug-view'; break; } } catch (Exception $e) { $error = "Action failed: " . $e->getMessage(); } } // Helper Functions function extractCampaignId($folder) { if (isset($folder['metadata'])) { foreach ($folder['metadata'] as $field) { if (isset($field['id']) && $field['id'] === 'FERRERO.FIELD.CAMPAIGN ID') { return $field['value']['value']['value'] ?? null; } } } return null; } function extractStatus($folder) { if (isset($folder['metadata'])) { foreach ($folder['metadata'] as $field) { if (isset($field['id']) && $field['id'] === 'CONTENT.SCALING.STATUS') { return $field['value']['value']['value'] ?? null; } } } return null; } function extractStatusFromAsset($asset) { if (isset($asset['metadata']) && isset($asset['metadata']['metadata_element_list'])) { foreach ($asset['metadata']['metadata_element_list'] as $category) { if (isset($category['metadata_element_list'])) { foreach ($category['metadata_element_list'] as $field) { if ($field['id'] === 'CONTENT.SCALING.STATUS') { return $field['value']['value']['value'] ?? null; } } } } } return null; } function loadCampaignsByStatus($testRunner, $status) { // Search for ALL Local Adaptation campaigns (without status filter in API) // Then let CampaignFormatter filter by status in PHP $statusManager = createStatusManager($testRunner); $response = $statusManager->searchAllCampaigns($testRunner); if ($response['success']) { // Get ALL campaigns first $allCampaigns = CampaignFormatter::getActionableCampaigns($response['body'], []); // Debug: Store all campaigns to see their statuses global $results; $results['debug_all_campaigns_count'] = count($allCampaigns); $results['debug_status_filter'] = $status; // Now filter by status $filteredCampaigns = CampaignFormatter::getActionableCampaigns($response['body'], ['status' => $status]); $results['debug_filtered_count'] = count($filteredCampaigns); return $filteredCampaigns; } return []; } function loadCampaignsGlobal($testRunner, $status) { // Search for ALL Global comm campaigns // Then filter by status in PHP $statusManager = createStatusManager($testRunner); $response = $statusManager->searchGlobalCampaigns($testRunner); if ($response['success']) { $allCampaigns = CampaignFormatter::getActionableCampaigns($response['body'], []); global $results; $results['debug_all_global_count'] = count($allCampaigns); $results['debug_status_filter_global'] = $status; // Filter by status $filteredCampaigns = CampaignFormatter::getActionableCampaigns($response['body'], ['status' => $status]); $results['debug_filtered_global_count'] = count($filteredCampaigns); return $filteredCampaigns; } return []; } function findMasterAssetsFolder($testRunner, $campaignId, $configV3) { $masterFolderName = $configV3->getFolderName('master_assets'); $requests = $testRunner->getAvailableRequests(); foreach ($requests as $index => $request) { $name = strtolower($request['name']); if (strpos($name, 'retrieve master asset folder') !== false || strpos($name, 'master asset folder and final') !== false) { $modifiedRequest = $request; $url = is_array($request['request']['url']) ? $request['request']['url']['raw'] : $request['request']['url']; $url = preg_replace('/folders\/[a-f0-9]+\//', "folders/{$campaignId}/", $url); $url = str_replace('{{baseUrl}}', $configV3->getBaseUrl(), $url); if (is_array($modifiedRequest['request']['url'])) { $modifiedRequest['request']['url']['raw'] = $url; } else { $modifiedRequest['request']['url'] = $url; } $result = $testRunner->runSingleTest($modifiedRequest, $index); if ($result['status'] === 'PASS') { $data = json_decode($result['response']['body'], true); $folders = $data['folder_children']['asset_list'] ?? []; foreach ($folders as $folder) { $folderName = extractFolderName($folder); if (strpos($folderName, 'Master') !== false || $folderName === $masterFolderName) { return $folder['asset_id']; } } } break; } } return null; } function extractFolderName($folder) { $nameFields = [ 'INER_NAME_GENERIC', 'ARTESIA.FIELD.NAME', 'FERRERO.FIELD.CAMPAIGN_NAME', 'ARTESIA.FIELD.TITLE' ]; if (isset($folder['metadata']['metadata_element_list'])) { foreach ($folder['metadata']['metadata_element_list'] as $category) { if (isset($category['metadata_element_list'])) { foreach ($category['metadata_element_list'] as $field) { if (in_array($field['id'], $nameFields) && isset($field['value']['value']['value'])) { return $field['value']['value']['value']; } } } } } return $folder['name'] ?? $folder['title'] ?? 'Unnamed'; } function getAssetsFromFolder($testRunner, $folderId) { $requests = $testRunner->getAvailableRequests(); foreach ($requests as $index => $request) { $name = strtolower($request['name']); if (strpos($name, 'retrieve all assets from') !== false) { $modifiedRequest = $request; $url = is_array($request['request']['url']) ? $request['request']['url']['raw'] : $request['request']['url']; $url = preg_replace('/folders\/[a-f0-9]+\//', "folders/{$folderId}/", $url); if (is_array($modifiedRequest['request']['url'])) { $modifiedRequest['request']['url']['raw'] = $url; } else { $modifiedRequest['request']['url'] = $url; } $result = $testRunner->runSingleTest($modifiedRequest, $index); if ($result['status'] === 'PASS') { $data = json_decode($result['response']['body'], true); $assets = $data['folder_children']['asset_list'] ?? []; // Filter out folders, only return files return array_filter($assets, function($asset) { return ($asset['data_type'] ?? '') !== 'CONTAINER'; }); } break; } } return []; } function downloadAsset($testRunner, $assetId, $filename, $campaignId, $assetData = null) { $apiClient = new ApiClient(); $configV3 = new ConfigV3(); $apiClient->setBaseUrl($configV3->getBaseUrl()); // Get OAuth2 token $oauth2Handler = new ReflectionProperty($testRunner, 'oauth2Handler'); $oauth2Handler->setAccessible(true); $oauth2HandlerInstance = $oauth2Handler->getValue($testRunner); if ($oauth2HandlerInstance) { $apiClient->setHeader('Authorization', $oauth2HandlerInstance->getAuthHeader()); } $renditionUrl = null; $debugInfo = []; // If we have asset data from folder listing, use it directly if ($assetData) { $debugInfo['using_cached_asset_data'] = true; // Look for rendition URL in asset_content_info from folder listing if (isset($assetData['asset_content_info']['master_content']['url'])) { $renditionUrl = $assetData['asset_content_info']['master_content']['url']; $debugInfo['found_master_content_url'] = true; } elseif (isset($assetData['asset_content_info'][0]['url'])) { $renditionUrl = $assetData['asset_content_info'][0]['url']; $debugInfo['found_array_url'] = true; } elseif (isset($assetData['asset_content_info']['url'])) { $renditionUrl = $assetData['asset_content_info']['url']; $debugInfo['found_direct_url'] = true; } $debugInfo['has_asset_content_info'] = isset($assetData['asset_content_info']); $debugInfo['asset_content_info_type'] = isset($assetData['asset_content_info']) ? gettype($assetData['asset_content_info']) : 'not set'; } // Fallback: Fetch asset metadata if not provided or rendition not found if (!$renditionUrl) { $debugInfo['fetching_from_api'] = true; $metadataRequest = [ 'method' => 'GET', 'url' => "/v6/assets/{$assetId}?load_type=full" ]; $metadataResponse = $apiClient->executeRequest($metadataRequest); if ($metadataResponse['success']) { $fetchedData = json_decode($metadataResponse['body'], true); $asset = $fetchedData['asset'] ?? $fetchedData; if (isset($asset['asset_content_info']['master_content']['url'])) { $renditionUrl = $asset['asset_content_info']['master_content']['url']; } elseif (isset($asset['asset_content_info'][0]['url'])) { $renditionUrl = $asset['asset_content_info'][0]['url']; } elseif (isset($asset['asset_content_info']['url'])) { $renditionUrl = $asset['asset_content_info']['url']; } $debugInfo['api_has_asset_content_info'] = isset($asset['asset_content_info']); } } // Try direct download if no rendition URL found if (!$renditionUrl) { $renditionUrl = "/v6/assets/{$assetId}/contents"; } // Clean up the URL - remove /otmmapi prefix if present (base URL already has it) $renditionUrl = str_replace('/otmmapi/', '/', $renditionUrl); $debugInfo['final_url'] = $renditionUrl; // Try to download - first attempt with original/master rendition $request = [ 'method' => 'GET', 'url' => $renditionUrl ]; $response = $apiClient->executeRequest($request); $attempts = [['url' => $renditionUrl, 'type' => 'original', 'http_code' => $response['http_code']]]; // If original fails and we have asset data, try preview/thumbnail renditions if ($response['success'] && !empty($response['body'])) { $jsonCheck = json_decode($response['body'], true); if ($jsonCheck && isset($jsonCheck['exception_body'])) { // Original failed, try alternative renditions from asset data $alternativeRenditions = []; if ($assetData && isset($assetData['asset_content_info'])) { $debugInfo['asset_content_info_count'] = count($assetData['asset_content_info']); foreach ($assetData['asset_content_info'] as $key => $contentInfo) { if (is_array($contentInfo)) { $rendType = $contentInfo['rendition_type'] ?? $key; $debugInfo['content_info_keys'][] = $key; if (isset($contentInfo['url'])) { // Add ALL renditions, not just preview/thumbnail $alternativeRenditions[] = [ 'url' => str_replace('/otmmapi/', '/', $contentInfo['url']), 'type' => $rendType ]; } } } $debugInfo['alternative_renditions_found'] = count($alternativeRenditions); } // Try alternative renditions foreach ($alternativeRenditions as $altRendition) { $altRequest = ['method' => 'GET', 'url' => $altRendition['url']]; $altResponse = $apiClient->executeRequest($altRequest); $attempts[] = ['url' => $altRendition['url'], 'type' => $altRendition['type'], 'http_code' => $altResponse['http_code']]; if ($altResponse['success'] && !empty($altResponse['body'])) { $altCheck = json_decode($altResponse['body'], true); if (!$altCheck || !isset($altCheck['exception_body'])) { // Success with alternative rendition! $response = $altResponse; $renditionUrl = $altRendition['url']; $debugInfo['used_rendition'] = $altRendition['type']; break; } } } // Still failed after all attempts $finalCheck = json_decode($response['body'], true); if ($finalCheck && isset($finalCheck['exception_body'])) { return [ 'success' => false, 'error' => $finalCheck['exception_body']['message'] ?? 'File not found in storage', 'http_code' => $finalCheck['exception_body']['http_response_code'] ?? 500, 'debug_message' => $finalCheck['exception_body']['debug_message'] ?? '', 'tried_url' => $renditionUrl, 'debug_info' => $debugInfo, 'all_attempts' => $attempts ]; } } $downloadPath = $configV3->getDownloadPath($campaignId); if (!is_dir($downloadPath)) { mkdir($downloadPath, 0755, true); } $safeFilename = preg_replace('/[^a-zA-Z0-9._-]/', '_', $filename ?: "asset_{$assetId}"); $fullPath = $downloadPath . '/' . $safeFilename; $bytesWritten = file_put_contents($fullPath, $response['body']); return [ 'success' => $bytesWritten !== false, 'filename' => $safeFilename, 'filepath' => $fullPath, 'size' => $bytesWritten, 'asset_id' => $assetId, 'download_method' => 'rendition', 'rendition_url' => $renditionUrl ]; } return [ 'success' => false, 'error' => $response['error'] ?? 'Download failed', 'http_code' => $response['http_code'], 'tried_url' => $renditionUrl ]; } function downloadAllAssets($testRunner, $assets, $campaignId) { $results = [ 'total' => count($assets), 'successful' => 0, 'failed' => 0, 'details' => [] ]; foreach ($assets as $asset) { $filename = extractFolderName($asset); $result = downloadAsset($testRunner, $asset['asset_id'], $filename, $campaignId); if ($result['success']) { $results['successful']++; } else { $results['failed']++; } $results['details'][] = $result; } return $results; } function createStatusManager($testRunner) { $apiClient = new ApiClient(); $configV3 = new ConfigV3(); $apiClient->setBaseUrl($configV3->getBaseUrl()); $oauth2Handler = new ReflectionProperty($testRunner, 'oauth2Handler'); $oauth2Handler->setAccessible(true); $oauth2HandlerInstance = $oauth2Handler->getValue($testRunner); if ($oauth2HandlerInstance) { $apiClient->setHeader('Authorization', $oauth2HandlerInstance->getAuthHeader()); } return new StatusManager($apiClient); } function findUploadFolder($testRunner, $campaignId, $configV3) { $finalFolderName = $configV3->getFolderName('final_assets'); $requests = $testRunner->getAvailableRequests(); foreach ($requests as $index => $request) { $name = strtolower($request['name']); if (strpos($name, 'retrieve master asset folder') !== false || strpos($name, 'master asset folder and final') !== false) { $modifiedRequest = $request; $url = is_array($request['request']['url']) ? $request['request']['url']['raw'] : $request['request']['url']; $url = preg_replace('/folders\/[a-f0-9]+\//', "folders/{$campaignId}/", $url); $url = str_replace('{{baseUrl}}', $configV3->getBaseUrl(), $url); if (is_array($modifiedRequest['request']['url'])) { $modifiedRequest['request']['url']['raw'] = $url; } else { $modifiedRequest['request']['url'] = $url; } $result = $testRunner->runSingleTest($modifiedRequest, $index); if ($result['status'] === 'PASS') { $data = json_decode($result['response']['body'], true); $folders = $data['folder_children']['asset_list'] ?? []; foreach ($folders as $folder) { $folderName = extractFolderName($folder); // Look for Final Assets or Localized folder if (strpos($folderName, 'Final') !== false || strpos($folderName, 'Localized') !== false || $folderName === $finalFolderName) { return $folder['asset_id']; } } } break; } } return null; } function fetchFullAssetMetadata($testRunner, $assetId) { $apiClient = new ApiClient(); $configV3 = new ConfigV3(); $apiClient->setBaseUrl($configV3->getBaseUrl()); // Get OAuth2 token $oauth2Handler = new ReflectionProperty($testRunner, 'oauth2Handler'); $oauth2Handler->setAccessible(true); $oauth2HandlerInstance = $oauth2Handler->getValue($testRunner); if ($oauth2HandlerInstance) { $apiClient->setHeader('Authorization', $oauth2HandlerInstance->getAuthHeader()); } // Fetch full asset with metadata $request = [ 'method' => 'GET', 'url' => "/v6/assets/{$assetId}?load_type=full" ]; $response = $apiClient->executeRequest($request); if ($response['success']) { $data = json_decode($response['body'], true); return $data['asset'] ?? null; } return null; } function uploadAssetsToBox($downloadResults, $campaignId, $campaignName, $masterAssets, $uploadFolderId = null) { // Debug error_log("Box Upload: Starting with " . count($downloadResults['details'] ?? []) . " downloads"); // Load Box credentials and use JWT authentication $boxConfig = json_decode(file_get_contents(__DIR__ . '/Box-config.json'), true); $clientId = $boxConfig['boxAppSettings']['clientID']; $clientSecret = $boxConfig['boxAppSettings']['clientSecret']; $boxClient = new BoxClient($clientId, $clientSecret); $idGenerator = new IDGenerator(true); // Use database for ID generation $dbClient = new DatabaseClient(); // Test Box connection first $connectionTest = $boxClient->testConnection(); error_log("Box Connection Test: " . ($connectionTest['success'] ? 'SUCCESS' : 'FAILED')); if (!$connectionTest['success']) { return [ 'success' => false, 'error' => 'Box connection failed. Check developer token.', 'total' => 0, 'successful' => 0, 'failed' => 0 ]; } // Create campaign folder in Box $folderResult = $boxClient->createCampaignFolder($campaignId, $campaignName); error_log("Box Folder Creation: " . ($folderResult['success'] ? 'SUCCESS' : 'FAILED')); if (!$folderResult['success']) { return [ 'success' => false, 'error' => 'Failed to create Box folder: ' . ($folderResult['error'] ?? 'Unknown'), 'total' => 0, 'successful' => 0, 'failed' => 0 ]; } $boxFolderId = $folderResult['folder_id']; $downloadDetails = $downloadResults['details'] ?? []; error_log("Box Upload: Processing " . count($downloadDetails) . " files"); error_log("Box Upload: Upload folder ID passed: " . ($uploadFolderId ?? 'NULL')); error_log("Box Upload: Master assets count: " . count($masterAssets)); $results = [ 'total' => count($downloadDetails), 'successful' => 0, 'failed' => 0, 'details' => [], 'box_folder_id' => $boxFolderId, 'box_folder_name' => $folderResult['folder_name'] ]; // Upload each successfully downloaded file to Box foreach ($downloadDetails as $index => $download) { if (!$download['success']) { $results['details'][] = [ 'success' => false, 'filename' => $download['filename'] ?? 'Unknown', 'error' => 'Download failed' ]; $results['failed']++; continue; } // Generate unique ID $idResult = $idGenerator->generateId(); $uniqueId = $idResult['id']; // Get asset metadata - use numeric index to match download order $assetMetadata = $masterAssets[$index] ?? []; error_log("Box Upload: Asset index " . $index . " has " . count(array_keys($assetMetadata)) . " keys"); // Upload to Box $boxUploadResult = $boxClient->uploadFile( $download['filepath'], $download['filename'], $uniqueId, $boxFolderId, $assetMetadata ); if ($boxUploadResult['success']) { $results['successful']++; // Store in database with upload folder ID error_log("Box Upload: Storing in DB with upload folder: " . ($uploadFolderId ?? 'NULL')); $dbResult = $dbClient->storeMasterAsset( $uniqueId, $assetMetadata, $boxUploadResult['file_id'], $boxUploadResult['box_url'], $uploadFolderId // MISSING PARAMETER - was causing NULL! ); $results['details'][] = [ 'success' => true, 'original_filename' => $download['filename'], 'box_filename' => $boxUploadResult['filename'], 'unique_id' => $uniqueId, 'box_file_id' => $boxUploadResult['file_id'], 'box_url' => $boxUploadResult['box_url'], 'db_stored' => $dbResult['success'] ?? false, 'id_source' => $idResult['source'] ?? 'unknown' ]; } else { $results['failed']++; $results['details'][] = [ 'success' => false, 'filename' => $download['filename'], 'error' => $boxUploadResult['error'] ?? 'Unknown error' ]; } } return $results; } function uploadFiles($testRunner, $uploadedFiles, $folderId, $selectedMasterAsset = null) { $apiClient = new ApiClient(); $configV3 = new ConfigV3(); $apiClient->setBaseUrl($configV3->getBaseUrl()); $oauth2Handler = new ReflectionProperty($testRunner, 'oauth2Handler'); $oauth2Handler->setAccessible(true); $oauth2HandlerInstance = $oauth2Handler->getValue($testRunner); if ($oauth2HandlerInstance) { $apiClient->setHeader('Authorization', $oauth2HandlerInstance->getAuthHeader()); } // Use SIMPLE uploader that works! $uploader = new AssetUploaderSimple($apiClient); $results = [ 'total' => 0, 'successful' => 0, 'failed' => 0, 'details' => [] ]; // Handle multiple file upload if (isset($uploadedFiles['tmp_name'])) { $fileCount = is_array($uploadedFiles['tmp_name']) ? count($uploadedFiles['tmp_name']) : 1; $results['total'] = $fileCount; for ($i = 0; $i < $fileCount; $i++) { $tmpName = is_array($uploadedFiles['tmp_name']) ? $uploadedFiles['tmp_name'][$i] : $uploadedFiles['tmp_name']; $fileName = is_array($uploadedFiles['name']) ? $uploadedFiles['name'][$i] : $uploadedFiles['name']; $fileError = is_array($uploadedFiles['error']) ? $uploadedFiles['error'][$i] : $uploadedFiles['error']; if ($fileError !== UPLOAD_ERR_OK) { $results['details'][] = [ 'success' => false, 'filename' => $fileName, 'error' => 'Upload error code: ' . $fileError ]; $results['failed']++; continue; } // Create temp file with actual filename (not phpXXXXX) $actualFilePath = sys_get_temp_dir() . '/' . $fileName; copy($tmpName, $actualFilePath); $result = $uploader->uploadFile($actualFilePath, $folderId); // Clean up @unlink($actualFilePath); if ($result['success']) { $results['successful']++; } else { $results['failed']++; } $results['details'][] = $result; } } return $results; } $oauth2Status = $testRunner ? $testRunner->getOAuth2Status() : null; $envInfo = $configV3->getEnvironmentInfo(); ?> Ferrero Content Scaling Workflow

🎯 Content Scaling Workflow V3

Status-based workflow management for Ferrero DAM

Environment: ()
Base URL:
❌ Error:
✅ Success:
- Expires:

📥 Download Workflow: A1 → A2

Load campaigns with status A1, download master assets, and update status to A2

🔍 Debug Results: Found Campaigns

OAuth Status: | Expires:

API Response: HTTP - Service Unavailable (Server may be down or OAuth token expired)
Total results from API:
URL:

View Response Body

Showing ALL campaigns without status filter to see their metadata fields:

$campaign): ?>
Campaign : () - Content Scaling Status:

Asset ID:

Campaign ID:

Content Scaling Status:

Brand:

Market:

View Full Raw Metadata

No campaigns returned from API (check OAuth status and response body above)

🔍 Debug Info:
Found total campaigns before filtering
Filtering for Content Scaling Status: ""
Campaigns after filtering:

Found Campaigns

Campaign ID:

Asset ID:

Brand:

Market:

Content Scaling Status: - Ready for Localization

Selected Campaign: ()
Asset ID:
🔍 Debug folder structure for this campaign

Master Assets ( files)

$asset): ?>

ID:
Type: | Size: bytes

⚠️ Update Status to A2

Once all assets are downloaded (or attempted), update the campaign status to A2 (Selected Master Assets sent to Agency)

Note: If files don't exist in storage (test environment), you can still update the status to test the workflow progression.

Final Assets / Uploaded Assets ( files)

These are the localized/processed assets that have been uploaded back to the DAM.

$asset): ?>

ID:
Type: | Size: bytes
✅ Model:
⚠️ Status Update Debug Information:
View detailed error information
HTTP Status Code:
Folder ID:
Target Status:
API Response Body:
Request URL:
✅ Download Successful:
Size: bytes | Path:
Method: | URL: ❌ Download Failed:
HTTP Code:
Tried URL:
Details:
Debug:
View all download attempts ()
$att): ?>
. [] - HTTP
Bulk Download Results: of assets downloaded successfully 0): ?>
files failed (may not exist in storage)
📦 Box Upload Results: of files uploaded to Box
Box Folder:
Folder ID:
View Box upload details

Tracking ID: | 📦 View in Box | DB: | ID Source:
Error:

📤 Upload Workflow: A2 → A3

Load campaigns with status A2 (assets sent to agency), upload processed files, and update status to A3

🧪 Test Upload to Specific Folder

Upload directly to a folder ID for testing (bypasses campaign workflow)

📋 Copy OAuth Token for Standalone Test

Use this token with: php test_upload_standalone.php "token"

✅ TEST UPLOAD SUCCESSFUL!
Filename:
Asset ID:
Folder: ❌ Test Upload Failed:
HTTP Code:
Folder:
View API Response

Found Campaigns

Campaign ID:

Asset ID:

Brand:

Market:

Content Scaling Status: - Assets Sent to Agency

Selected Campaign: ()
✅ Upload target folder found

Upload Processed Files

✅ Found master assets from this campaign

📋 Select Master Asset Metadata to Use:

Choose which master asset's metadata structure to use for your upload. Your uploaded file will inherit all metadata (model, security, fields) from the selected master asset. Only the filename will be updated to match your new file.

$masterAsset): ?>
ℹ️ Note: No master assets found.
Click "Find Upload Folder" above to fetch master assets from this campaign.

Accepted formats: Images (JPG, PNG, GIF, TIFF), Documents (PDF, AI, PSD, EPS), Video (MOV, MP4, AVI), Archives (ZIP), Office (DOC, XLS, TXT)

🔍 Upload Debug Info:
Your File: ()
Target Folder ID:

Using Metadata From:
Master Asset ID:
Metadata Model ID:
Has Security Policies: ( policies)
Master MIME Type:

Sending: asset_representation (model ID + security policies only) + manifest + parent_folder_id + files
Note: Full metadata too large to send - using model ID inheritance instead
Upload Results: of files uploaded successfully
View upload details
$detail): ?>

Asset ID:
Error:
HTTP Code:
View API Response
0): ?>

✅ Update Status to A3

Files have been uploaded. Update the campaign status to A3 (Localized Asset received from Agency)

🔄 Rework Workflow: A5 → A6

Load campaigns with status A5 (rework needed), download assets for rework, and update status to A6

🔍 Debug Results: Found Campaigns

OAuth Status: | Expires:

API Response: HTTP - Service Unavailable
Total results from API:
URL:

Showing ALL campaigns - use reset button to change any to A5 for testing:

$campaign): ?>
Campaign : () - Content Scaling Status:

Asset ID:

Campaign ID:

Content Scaling Status:

Brand:

Market:

View Full Raw Metadata

No campaigns returned from API (check OAuth status and response above)

Found Campaigns Needing Rework

Campaign ID:

Asset ID:

Brand:

Market:

Content Scaling Status: - Rework Needed

Selected Campaign: ()

Rework Assets ( files)

$asset): ?>

ID:
Type: | Size: bytes

⚠️ Update Status to A6

Once rework assets are downloaded, update the campaign status to A6 (Assets to be reworked received by the Agency)

✅ Download Successful:
Size: bytes ❌ Download Failed:
Bulk Download Results: of rework assets downloaded

📦 Upload from Box: A2 → A3

Load files from a Box folder, parse V2 filenames, merge metadata, and upload to DAM

Step 1: Load Files from Box Folder

Enter the Box Folder ID containing files ready for upload. Files must follow the V2 naming convention.

🌍 Global Masters Workflow: B1 → B2

Download Global Master assets for re-mastering workflow

Selected Campaign: ()
Asset ID:
Type: Global Masters
🔍 Debug folder structure for this campaign

Global Master Assets ( files)

$asset): ?>

ID:
Type:

🔍 Debug Results: Found Global Campaigns

- Status:

Campaign Type:

Status:

Asset ID:

⚠️ Update Status to B2

Once all Global Master assets are downloaded and sent to Agency, update the campaign status to B2

Found Global Campaigns (B1)

Campaign ID:

Status: B1

Type:

🔧 Debug View

Campaign status management and folder inspection tools for testing workflows

📋 Campaign Status Management

Found campaigns
0): ?>
$campaign): ?>
Select Campaign Name Campaign Number Current Status Actions
$campaign): ?>

✓ Campaign Status Updated Successfully

Campaign:

Number:

Previous Status:

New Status:

✗ Status Update Failed

Error: htmlspecialchars($results['status_update_result']['error']) ?>

Campaign:

📁

Campaign: | Status:

Viewing:

Folder Type:

Total Assets:

0): ?>
$asset): ?>

ID:
Type: | Size: bytes

No assets found in this folder

Error Loading Folder