ferrero-opentext/workflow_v3.php
DJP 156f9ae51d Fix status updates and enhance workflow with proper error handling
Major improvements to workflow_v3.php and supporting classes:

**Status Updates (A1→A2, A2→A3, A5→A6):**
- Fix StatusManager to use correct PATCH endpoint: /v6/folders/{id}
- Add lock_strategy=optimistic parameter to prevent locking errors
- Update request body structure to use edited_folder format
- Status updates now working correctly

**Status Field Extraction:**
- Fix CampaignFormatter to extract CONTENT.SCALING.STATUS field
- Handle domain values with field_value.value path
- Now correctly filters campaigns by status (A1, A2, A5, etc.)

**Error Handling:**
- Extract and display actual API error messages
- Show HTTP status codes in all error messages
- Add expandable debug panels with full API responses
- Enhanced upload error reporting with detailed diagnostics

**Campaign Search:**
- Update to use Postman collection requests directly (avoids 503 errors)
- Fix URL encoding (rawurlencode instead of urlencode)
- Add comprehensive debug output showing OAuth status and API responses
- Search now attempts Postman request first, falls back to manual query

**Upload Improvements:**
- Rewrite AssetUploader to use native PHP CURLFile for multipart uploads
- Add support for additional file types: .mov, .mp4, .avi, .zip, .txt, .doc, .xlsx
- Increase max upload size to 100MB for video files
- Simplify asset_representation to minimal structure
- Add infrastructure to inherit metadata from master assets

**Testing Features:**
- Add "Reset to A1" button for testing workflow iterations
- Add debug mode to view all campaigns and their metadata
- Show Content Scaling Status on all campaign cards
- Display filtering debug info (total vs filtered counts)

**UI Improvements:**
- Rename buttons to clarify "Content Scaling Status" terminology
- Add status badges to campaign cards showing current status
- Better visual feedback for successful/failed operations

Current Status: Workflow A1→A2 fully working. Upload A2→A3 ready for testing
once DAM server recovers from HTTP 503 errors.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 16:43:09 -04:00

1940 lines
96 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
session_start();
// Clear session on reset
if (isset($_GET['reset']) || isset($_POST['reset'])) {
session_destroy();
session_start();
header('Location: workflow_v3.php');
exit;
}
// Clear specific workflow results
if (isset($_POST['clear_results'])) {
unset($_SESSION['workflow_results']);
$_SESSION['workflow_results'] = [];
header('Location: workflow_v3.php?tab=' . ($_POST['tab'] ?? 'download'));
exit;
}
require_once 'config_v3.php';
require_once 'src/TestRunner.php';
require_once 'src/CampaignFormatter.php';
require_once 'src/AssetDownloader.php';
require_once 'src/MetadataExtractor.php';
require_once 'src/StatusManager.php';
require_once 'src/ApiClient.php';
require_once 'src/AssetUploader.php';
$configV3 = new ConfigV3();
$errors = $configV3->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 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->searchAllCampaigns($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 (without status filter)";
} 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);
$success = "Found Master Assets folder with " . count($results['master_assets']) . " assets";
} else {
$error = "Master 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_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 '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
$uploadFolderId = findUploadFolder($testRunner, $campaignId, $configV3);
if ($uploadFolderId) {
$results['upload_folder_id'] = $uploadFolderId;
$success = "Found upload target folder";
} else {
$error = "Upload folder not found in campaign";
}
}
$currentTab = 'upload';
break;
case 'upload_files':
if (isset($_FILES['upload_files']) && isset($results['upload_folder_id'])) {
// Use master assets metadata if available (from A1 campaign downloads)
$masterMetadata = $results['master_assets_metadata'] ?? null;
$uploadResults = uploadFiles($testRunner, $_FILES['upload_files'], $results['upload_folder_id'], $masterMetadata);
$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 '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;
}
} catch (Exception $e) {
$error = "Action failed: " . $e->getMessage();
}
}
// Helper Functions
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 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 uploadFiles($testRunner, $uploadedFiles, $folderId, $masterAssetsMetadata = 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());
}
$uploader = new AssetUploader($apiClient, $folderId);
$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;
}
// Extract metadata from master asset if available
$assetMetadata = null;
if ($masterAssetsMetadata && is_array($masterAssetsMetadata)) {
// Try to find matching master asset by filename or use first one
$assetMetadata = $masterAssetsMetadata[0]['metadata'] ?? null;
}
$result = $uploader->uploadFile($tmpName, $folderId, [], $assetMetadata);
if ($result['success']) {
$results['successful']++;
} else {
$results['failed']++;
}
$results['details'][] = $result;
}
}
return $results;
}
$oauth2Status = $testRunner ? $testRunner->getOAuth2Status() : null;
$envInfo = $configV3->getEnvironmentInfo();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Content Scaling Workflow V3</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background-color: #f5f5f5;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
text-align: center;
}
.header h1 { margin-bottom: 10px; }
.header p { opacity: 0.9; }
.env-info {
background: rgba(255,255,255,0.2);
padding: 10px;
border-radius: 4px;
margin-top: 15px;
font-size: 13px;
}
.tabs {
display: flex;
background: #f8f9fa;
border-bottom: 2px solid #dee2e6;
}
.tab {
flex: 1;
padding: 15px 20px;
text-align: center;
cursor: pointer;
border: none;
background: transparent;
font-size: 16px;
font-weight: 500;
color: #6c757d;
transition: all 0.3s;
}
.tab:hover { background: #e9ecef; }
.tab.active {
background: white;
color: #667eea;
border-bottom: 3px solid #667eea;
}
.tab-content {
padding: 30px;
display: none;
}
.tab-content.active { display: block; }
.alert {
padding: 12px 20px;
border-radius: 6px;
margin-bottom: 20px;
}
.alert-error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.alert-success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-info {
background: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
margin-right: 10px;
margin-bottom: 10px;
}
.btn-primary {
background: #667eea;
color: white;
}
.btn-primary:hover { background: #5568d3; }
.btn-success {
background: #28a745;
color: white;
}
.btn-success:hover { background: #218838; }
.btn-danger {
background: #dc3545;
color: white;
}
.btn-secondary {
background: #6c757d;
color: white;
}
.campaign-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 20px;
margin: 20px 0;
}
.campaign-card {
background: #f8f9fa;
border: 2px solid #dee2e6;
border-radius: 8px;
padding: 20px;
cursor: pointer;
transition: all 0.3s;
}
.campaign-card:hover {
border-color: #667eea;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
}
.campaign-card.selected {
border-color: #28a745;
background: #f8fff9;
}
.campaign-card h3 {
color: #333;
margin-bottom: 10px;
}
.campaign-card p {
margin: 5px 0;
font-size: 14px;
color: #666;
}
.asset-list {
background: #f8f9fa;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
}
.asset-item {
background: white;
border: 1px solid #dee2e6;
border-radius: 6px;
padding: 15px;
margin: 10px 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.asset-info {
flex: 1;
}
.asset-actions {
display: flex;
gap: 10px;
}
.metadata-display {
background: #e9ecef;
padding: 15px;
border-radius: 6px;
margin-top: 10px;
font-size: 12px;
font-family: monospace;
max-height: 300px;
overflow-y: auto;
display: none;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
}
.status-a1 { background: #fff3cd; color: #856404; }
.status-a2 { background: #cfe2ff; color: #084298; }
.status-a3 { background: #d1e7dd; color: #0f5132; }
.status-a5 { background: #f8d7da; color: #842029; }
.status-a6 { background: #e2d9f3; color: #432874; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🎯 Content Scaling Workflow V3</h1>
<p>Status-based workflow management for Ferrero DAM</p>
<div class="env-info">
<strong>Environment:</strong> <?= htmlspecialchars($envInfo['description']) ?>
(<?= htmlspecialchars($envInfo['name']) ?>)
<br>
<strong>Base URL:</strong> <?= htmlspecialchars($envInfo['baseUrl']) ?>
</div>
</div>
<?php if ($error): ?>
<div style="padding: 20px;">
<div class="alert alert-error">
<strong>❌ Error:</strong> <?= htmlspecialchars($error) ?>
</div>
</div>
<?php endif; ?>
<?php if ($success): ?>
<div style="padding: 20px;">
<div class="alert alert-success">
<strong>✅ Success:</strong> <?= htmlspecialchars($success) ?>
</div>
</div>
<?php endif; ?>
<?php if ($oauth2Status && $oauth2Status['enabled']): ?>
<div style="padding: 20px; padding-bottom: 0;">
<div class="alert alert-<?= ($oauth2Status['has_token'] ?? false) ? 'success' : 'error' ?>">
<?= ($oauth2Status['has_token'] ?? false) ? '✅ OAuth2 Token Active' : '❌ OAuth2 Token Issue' ?>
<?php if (($oauth2Status['has_token'] ?? false) && isset($oauth2Status['expires_at'])): ?>
- Expires: <?= htmlspecialchars($oauth2Status['expires_at']) ?>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<div class="tabs">
<button class="tab <?= $currentTab === 'download' ? 'active' : '' ?>"
onclick="switchTab('download')">
📥 Download Workflow (A1→A2)
</button>
<button class="tab <?= $currentTab === 'upload' ? 'active' : '' ?>"
onclick="switchTab('upload')">
📤 Upload Workflow (A2→A3)
</button>
<button class="tab <?= $currentTab === 'rework' ? 'active' : '' ?>"
onclick="switchTab('rework')">
🔄 Rework Workflow (A5→A6)
</button>
</div>
<!-- Download Workflow Tab -->
<div id="tab-download" class="tab-content <?= $currentTab === 'download' ? 'active' : '' ?>">
<h2>📥 Download Workflow: A1 → A2</h2>
<p style="color: #666; margin: 10px 0 20px 0;">
Load campaigns with status A1, download master assets, and update status to A2
</p>
<form method="POST" style="display: inline-block;">
<input type="hidden" name="tab" value="download">
<input type="hidden" name="action" value="load_campaigns_a1">
<button type="submit" class="btn btn-primary">Load Campaigns (Content Scaling Status = A1)</button>
</form>
<form method="POST" style="display: inline-block;">
<input type="hidden" name="tab" value="download">
<input type="hidden" name="action" value="debug_all_campaigns">
<button type="submit" class="btn btn-secondary">🔍 Debug: Load ALL Campaigns (no filter)</button>
</form>
<?php if (!empty($results)): ?>
<form method="POST" style="display: inline-block;">
<input type="hidden" name="tab" value="download">
<input type="hidden" name="clear_results" value="1">
<button type="submit" class="btn btn-secondary">🗑️ Clear Workflow Data</button>
</form>
<?php endif; ?>
<?php if (isset($results['debug_campaigns']) || isset($results['debug_search_response'])): ?>
<div class="alert alert-<?= ($results['debug_search_response']['http_code'] ?? 0) == 503 ? 'error' : 'info' ?>" style="margin-top: 30px;">
<h3>🔍 Debug Results: Found <?= count($results['debug_campaigns'] ?? []) ?> Campaigns</h3>
<?php if (isset($results['oauth_debug'])): ?>
<p style="font-size: 13px; margin: 10px 0; padding: 10px; background: white; border-radius: 4px;">
<strong>OAuth Status:</strong>
<?= $results['oauth_debug']['has_token'] ? '✅ Token Active' : '❌ No Token' ?> |
Expires: <?= htmlspecialchars($results['oauth_debug']['expires_at']) ?>
</p>
<?php endif; ?>
<?php if (isset($results['debug_search_response'])): ?>
<p style="font-size: 13px; margin: 10px 0; padding: 10px; background: white; border-radius: 4px;">
<strong>API Response:</strong> HTTP <?= $results['debug_search_response']['http_code'] ?>
<?php if ($results['debug_search_response']['http_code'] == 503): ?>
<span style="color: #dc3545; font-weight: bold;">- Service Unavailable (Server may be down or OAuth token expired)</span>
<?php endif; ?>
<br>
<strong>Total results from API:</strong> <?= $results['debug_search_response']['total_results'] ?? 0 ?><br>
<strong>URL:</strong> <code style="font-size: 11px;"><?= htmlspecialchars($results['debug_search_response']['url']) ?></code>
<?php if (isset($results['debug_search_response']['response_body_preview'])): ?>
<details style="margin-top: 10px;">
<summary style="cursor: pointer;">View Response Body</summary>
<pre style="font-size: 11px; background: #f8f9fa; padding: 10px; margin-top: 5px;"><?= htmlspecialchars($results['debug_search_response']['response_body_preview']) ?></pre>
</details>
<?php endif; ?>
</p>
<?php endif; ?>
<?php if (!empty($results['debug_campaigns'])): ?>
<p>Showing ALL campaigns without status filter to see their metadata fields:</p>
<?php foreach ($results['debug_campaigns'] as $idx => $campaign): ?>
<details style="margin: 15px 0; background: white; padding: 15px; border-radius: 6px;">
<summary style="cursor: pointer; font-weight: 600;">
Campaign <?= $idx + 1 ?>: <?= htmlspecialchars($campaign['campaign_name'] ?? 'Unnamed') ?>
(<?= htmlspecialchars($campaign['campaign_id'] ?? 'N/A') ?>)
- Content Scaling Status: <span class="status-badge status-<?= strtolower($campaign['status'] ?? 'unknown') ?>"><?= htmlspecialchars($campaign['status'] ?? 'NOT FOUND') ?></span>
</summary>
<div style="margin-top: 15px;">
<p><strong>Asset ID:</strong> <?= htmlspecialchars($campaign['asset_id']) ?></p>
<p><strong>Campaign ID:</strong> <?= htmlspecialchars($campaign['campaign_id'] ?? 'N/A') ?></p>
<p><strong>Content Scaling Status:</strong> <?= htmlspecialchars($campaign['status'] ?? 'NOT FOUND') ?></p>
<p><strong>Brand:</strong> <?= htmlspecialchars($campaign['brand'] ?? 'N/A') ?></p>
<p><strong>Market:</strong> <?= htmlspecialchars($campaign['market'] ?? 'N/A') ?></p>
<form method="POST" style="margin-top: 15px; display: inline-block;">
<input type="hidden" name="tab" value="download">
<input type="hidden" name="action" value="reset_to_a1">
<input type="hidden" name="campaign_id" value="<?= htmlspecialchars($campaign['asset_id']) ?>">
<button type="submit" class="btn btn-danger" onclick="return confirm('Reset campaign <?= htmlspecialchars($campaign['campaign_name']) ?> to A1 status?')">
🔄 Reset to A1 (for testing)
</button>
</form>
<details style="margin-top: 10px;">
<summary style="cursor: pointer; font-size: 13px;">View Full Raw Metadata</summary>
<pre style="background: #f0f0f0; padding: 10px; border-radius: 4px; overflow: auto; max-height: 400px; font-size: 11px; margin-top: 10px;"><?= htmlspecialchars(json_encode($campaign, JSON_PRETTY_PRINT)) ?></pre>
</details>
</div>
</details>
<?php endforeach; ?>
<?php else: ?>
<p style="margin-top: 15px;">No campaigns returned from API (check OAuth status and response body above)</p>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if (isset($results['debug_all_campaigns_count'])): ?>
<div class="alert alert-info" style="margin-top: 20px;">
<strong>🔍 Debug Info:</strong><br>
Found <?= $results['debug_all_campaigns_count'] ?> total campaigns before filtering<br>
Filtering for Content Scaling Status: "<?= htmlspecialchars($results['debug_status_filter']) ?>"<br>
Campaigns after filtering: <?= $results['debug_filtered_count'] ?>
</div>
<?php endif; ?>
<?php if (isset($results['a1_campaigns']) && !empty($results['a1_campaigns'])): ?>
<h3 style="margin-top: 30px;">Found <?= count($results['a1_campaigns']) ?> Campaigns</h3>
<div class="campaign-grid">
<?php foreach ($results['a1_campaigns'] as $campaign): ?>
<div class="campaign-card <?= (isset($results['selected_campaign']) && $results['selected_campaign']['asset_id'] === $campaign['asset_id']) ? 'selected' : '' ?>"
onclick="selectCampaign('<?= htmlspecialchars($campaign['asset_id']) ?>')">
<h3><?= htmlspecialchars($campaign['campaign_name'] ?? 'Unnamed Campaign') ?></h3>
<p><strong>Campaign ID:</strong> <?= htmlspecialchars($campaign['campaign_id'] ?? 'N/A') ?></p>
<p><strong>Asset ID:</strong> <code style="font-size: 11px;"><?= htmlspecialchars($campaign['asset_id']) ?></code></p>
<p><strong>Brand:</strong> <?= htmlspecialchars($campaign['brand'] ?? 'N/A') ?></p>
<p><strong>Market:</strong> <?= htmlspecialchars($campaign['market'] ?? 'N/A') ?></p>
<p><strong>Content Scaling Status:</strong> <span class="status-badge status-a1"><?= htmlspecialchars($campaign['status'] ?? 'N/A') ?> - Ready for Localization</span></p>
</div>
<?php endforeach; ?>
</div>
<form method="POST" id="select-campaign-form">
<input type="hidden" name="tab" value="download">
<input type="hidden" name="action" value="select_campaign_a1">
<input type="hidden" name="campaign_id" id="selected-campaign-id">
<button type="submit" class="btn btn-success" id="select-campaign-btn" disabled>
Select Campaign & Continue
</button>
</form>
<?php endif; ?>
<?php if (isset($results['selected_campaign'])): ?>
<div class="alert alert-info" style="margin-top: 30px;">
<strong>Selected Campaign:</strong>
<?= htmlspecialchars($results['selected_campaign']['campaign_name']) ?>
(<?= htmlspecialchars($results['selected_campaign']['campaign_id']) ?>)
<br>
<small><strong>Asset ID:</strong> <code><?= htmlspecialchars($results['selected_campaign']['asset_id']) ?></code></small>
<br>
<a href="debug_assets.php?action=get_folders&campaign_id=<?= urlencode($results['selected_campaign']['asset_id']) ?>"
target="_blank" style="color: #667eea; font-size: 12px;">
🔍 Debug folder structure for this campaign
</a>
</div>
<form method="POST" style="margin-top: 20px;">
<input type="hidden" name="tab" value="download">
<input type="hidden" name="action" value="get_master_assets">
<button type="submit" class="btn btn-primary">Get Master Assets</button>
</form>
<?php endif; ?>
<?php if (isset($results['master_assets']) && !empty($results['master_assets'])): ?>
<h3 style="margin-top: 30px;">Master Assets (<?= count($results['master_assets']) ?> files)</h3>
<form method="POST" style="margin: 20px 0;">
<input type="hidden" name="tab" value="download">
<input type="hidden" name="action" value="download_all_assets">
<button type="submit" class="btn btn-success">📥 Download All Assets</button>
</form>
<div class="asset-list">
<?php foreach ($results['master_assets'] as $index => $asset): ?>
<?php
$assetName = extractFolderName($asset);
$assetId = $asset['asset_id'];
$metadataId = 'metadata-' . $index; // Use index instead of asset_id for simpler ID
?>
<div class="asset-item">
<div class="asset-info">
<strong><?= htmlspecialchars($assetName) ?></strong><br>
<small>ID: <?= htmlspecialchars($assetId) ?></small><br>
<small>Type: <?= htmlspecialchars($asset['mime_type'] ?? 'Unknown') ?></small>
<?php if (isset($asset['file_size'])): ?>
<small> | Size: <?= number_format($asset['file_size']) ?> bytes</small>
<?php endif; ?>
</div>
<div class="asset-actions">
<form method="POST" style="display: inline;">
<input type="hidden" name="tab" value="download">
<input type="hidden" name="action" value="download_asset">
<input type="hidden" name="asset_id" value="<?= htmlspecialchars($assetId) ?>">
<input type="hidden" name="filename" value="<?= htmlspecialchars($assetName) ?>">
<button type="submit" class="btn btn-success">📥 Download</button>
</form>
<button class="btn btn-secondary"
onclick="toggleMetadata('<?= $metadataId ?>')">
📋 Metadata
</button>
</div>
</div>
<div id="<?= $metadataId ?>" class="metadata-display" style="display: none;">
<?php
$metadata = MetadataExtractor::extractAllMetadata($asset);
if (!empty($metadata)):
?>
<div style="font-size: 13px;">
<!-- Basic Info -->
<?php if (isset($metadata['basic'])): ?>
<div style="background: white; padding: 12px; margin: 8px 0; border-radius: 4px; border-left: 4px solid #667eea;">
<strong>📋 Basic Info:</strong><br>
<div style="margin-top: 8px; display: grid; grid-template-columns: 150px 1fr; gap: 5px;">
<?php foreach ($metadata['basic'] as $key => $value): ?>
<?php if ($value !== null): ?>
<div style="color: #666;"><?= ucwords(str_replace('_', ' ', $key)) ?>:</div>
<div><strong><?= htmlspecialchars($value) ?></strong></div>
<?php endif; ?>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<!-- Content Info -->
<?php if (isset($metadata['content'])): ?>
<div style="background: white; padding: 12px; margin: 8px 0; border-radius: 4px; border-left: 4px solid #28a745;">
<strong>🖼️ Content Info:</strong><br>
<div style="margin-top: 8px; display: grid; grid-template-columns: 150px 1fr; gap: 5px;">
<?php foreach ($metadata['content'] as $key => $value): ?>
<?php if ($value !== null && $value !== -1): ?>
<div style="color: #666;"><?= ucwords(str_replace('_', ' ', $key)) ?>:</div>
<div><strong><?= is_string($value) ? htmlspecialchars($value) : $value ?></strong></div>
<?php endif; ?>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<!-- Custom Fields by Category -->
<?php if (isset($metadata['custom_fields'])): ?>
<?php foreach ($metadata['custom_fields'] as $category => $fields): ?>
<?php if (!empty($fields)): ?>
<details style="background: white; padding: 12px; margin: 8px 0; border-radius: 4px; border-left: 4px solid #ffc107;">
<summary style="cursor: pointer; font-weight: 600; color: #333;">
📁 <?= htmlspecialchars($category) ?> (<?= count($fields) ?> fields)
</summary>
<div style="margin-top: 8px; display: grid; grid-template-columns: 200px 1fr; gap: 5px;">
<?php foreach ($fields as $fieldName => $fieldData): ?>
<div style="color: #666; padding-top: 4px;"><?= htmlspecialchars($fieldName) ?>:</div>
<div style="padding-top: 4px;">
<strong>
<?php
if (is_array($fieldData['value'])) {
echo htmlspecialchars(implode(', ', $fieldData['value']));
} else {
echo htmlspecialchars($fieldData['value']);
}
?>
</strong>
</div>
<?php endforeach; ?>
</div>
</details>
<?php endif; ?>
<?php endforeach; ?>
<?php endif; ?>
<!-- Permissions -->
<?php if (isset($metadata['permissions'])): ?>
<details style="background: white; padding: 12px; margin: 8px 0; border-radius: 4px; border-left: 4px solid #dc3545;">
<summary style="cursor: pointer; font-weight: 600; color: #333;">🔐 Permissions</summary>
<div style="margin-top: 8px; columns: 2;">
<?php foreach ($metadata['permissions'] as $perm => $value): ?>
<div style="padding: 2px 0;">
<?= $value ? '✅' : '❌' ?> <?= htmlspecialchars(preg_replace('/Permission$/', '', $perm)) ?>
</div>
<?php endforeach; ?>
</div>
</details>
<?php endif; ?>
<!-- Renditions -->
<?php if (isset($metadata['renditions'])): ?>
<details style="background: white; padding: 12px; margin: 8px 0; border-radius: 4px; border-left: 4px solid #17a2b8;">
<summary style="cursor: pointer; font-weight: 600; color: #333;">🎨 Renditions (<?= count($metadata['renditions']) ?>)</summary>
<div style="margin-top: 8px;">
<?php foreach ($metadata['renditions'] as $rendName => $rendData): ?>
<div style="background: #f8f9fa; padding: 8px; margin: 4px 0; border-radius: 3px;">
<strong><?= htmlspecialchars($rendName) ?></strong><br>
<small>Size: <?= number_format($rendData['content_size'] ?? 0) ?> bytes |
<?= $rendData['width'] ?? 0 ?>x<?= $rendData['height'] ?? 0 ?>px |
<?= htmlspecialchars($rendData['mime_type'] ?? 'N/A') ?></small>
</div>
<?php endforeach; ?>
</div>
</details>
<?php endif; ?>
</div>
<?php else: ?>
<p>No metadata available for this asset</p>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<div style="margin-top: 30px; padding: 20px; background: #fff3cd; border-radius: 8px;">
<h4>⚠️ Update Status to A2</h4>
<p style="margin: 10px 0;">
Once all assets are downloaded (or attempted), update the campaign status to A2
(Selected Master Assets sent to Agency)
</p>
<p style="margin: 10px 0; font-size: 13px; color: #856404;">
<strong>Note:</strong> If files don't exist in storage (test environment), you can still
update the status to test the workflow progression.
</p>
<form method="POST">
<input type="hidden" name="tab" value="download">
<input type="hidden" name="action" value="update_status_to_a2">
<button type="submit" class="btn btn-success">Update Status: A1 → A2</button>
</form>
</div>
<?php endif; ?>
<?php if (isset($results['status_update_error'])): ?>
<div class="alert alert-error" style="margin-top: 20px;">
<strong>⚠️ Status Update Debug Information:</strong>
<details style="margin-top: 10px;">
<summary style="cursor: pointer; font-weight: 600;">View detailed error information</summary>
<div style="margin-top: 10px; font-size: 12px; font-family: monospace;">
<div style="background: #f8f9fa; padding: 10px; border-radius: 4px; margin: 5px 0;">
<strong>HTTP Status Code:</strong> <?= htmlspecialchars($results['status_update_error']['http_code']) ?>
</div>
<div style="background: #f8f9fa; padding: 10px; border-radius: 4px; margin: 5px 0;">
<strong>Folder ID:</strong> <?= htmlspecialchars($results['status_update_error']['folder_id']) ?>
</div>
<div style="background: #f8f9fa; padding: 10px; border-radius: 4px; margin: 5px 0;">
<strong>Target Status:</strong> <?= htmlspecialchars($results['status_update_error']['new_status']) ?>
</div>
<?php if (isset($results['status_update_error']['response']['body'])): ?>
<div style="background: #f8f9fa; padding: 10px; border-radius: 4px; margin: 5px 0;">
<strong>API Response Body:</strong>
<pre style="margin-top: 5px; white-space: pre-wrap; word-wrap: break-word;"><?= htmlspecialchars($results['status_update_error']['response']['body']) ?></pre>
</div>
<?php endif; ?>
<?php if (isset($results['status_update_error']['response']['url'])): ?>
<div style="background: #f8f9fa; padding: 10px; border-radius: 4px; margin: 5px 0;">
<strong>Request URL:</strong> <?= htmlspecialchars($results['status_update_error']['response']['url']) ?>
</div>
<?php endif; ?>
</div>
</details>
</div>
<?php endif; ?>
<?php if (isset($results['last_download'])): ?>
<?php $dl = $results['last_download']; ?>
<div class="alert alert-<?= $dl['success'] ? 'success' : 'error' ?>" style="margin-top: 20px;">
<?php if ($dl['success']): ?>
<strong>✅ Download Successful:</strong> <?= htmlspecialchars($dl['filename']) ?>
<br><small>Size: <?= number_format($dl['size']) ?> bytes | Path: <?= htmlspecialchars($dl['filepath']) ?></small>
<?php if (isset($dl['download_method'])): ?>
<br><small>Method: <?= htmlspecialchars($dl['download_method']) ?> | URL: <?= htmlspecialchars($dl['rendition_url'] ?? 'N/A') ?></small>
<?php endif; ?>
<?php else: ?>
<strong>❌ Download Failed:</strong> <?= htmlspecialchars($dl['error']) ?>
<br><small>HTTP Code: <?= $dl['http_code'] ?></small>
<?php if (isset($dl['tried_url'])): ?>
<br><small>Tried URL: <?= htmlspecialchars($dl['tried_url']) ?></small>
<?php endif; ?>
<?php if (isset($dl['debug_message'])): ?>
<br><small>Details: <?= htmlspecialchars($dl['debug_message']) ?></small>
<?php endif; ?>
<?php if (isset($dl['debug_info']) && !empty($dl['debug_info'])): ?>
<br><small>Debug: <?= htmlspecialchars(json_encode($dl['debug_info'])) ?></small>
<?php endif; ?>
<?php if (isset($dl['all_attempts']) && !empty($dl['all_attempts'])): ?>
<details style="margin-top: 10px;">
<summary style="cursor: pointer; font-size: 12px;">View all download attempts (<?= count($dl['all_attempts']) ?>)</summary>
<div style="margin-top: 8px; font-size: 11px;">
<?php foreach ($dl['all_attempts'] as $idx => $att): ?>
<div style="padding: 4px; background: #f8f9fa; margin: 2px 0; border-radius: 3px;">
<?= $idx + 1 ?>. [<?= htmlspecialchars($att['type']) ?>] <?= htmlspecialchars($att['url']) ?>
- HTTP <?= $att['http_code'] ?>
</div>
<?php endforeach; ?>
</div>
</details>
<?php endif; ?>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if (isset($results['bulk_download'])): ?>
<div class="alert alert-<?= $results['bulk_download']['successful'] === $results['bulk_download']['total'] ? 'success' : 'error' ?>"
style="margin-top: 20px;">
<strong>Bulk Download Results:</strong>
<?= $results['bulk_download']['successful'] ?> of <?= $results['bulk_download']['total'] ?> assets downloaded successfully
<?php if ($results['bulk_download']['failed'] > 0): ?>
<br><small><?= $results['bulk_download']['failed'] ?> files failed (may not exist in storage)</small>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<!-- Upload Workflow Tab -->
<div id="tab-upload" class="tab-content <?= $currentTab === 'upload' ? 'active' : '' ?>">
<h2>📤 Upload Workflow: A2 → A3</h2>
<p style="color: #666; margin: 10px 0 20px 0;">
Load campaigns with status A2 (assets sent to agency), upload processed files, and update status to A3
</p>
<form method="POST" style="display: inline-block;">
<input type="hidden" name="tab" value="upload">
<input type="hidden" name="action" value="load_campaigns_a2">
<button type="submit" class="btn btn-primary">Load Campaigns (Content Scaling Status = A2)</button>
</form>
<?php if (!empty($results) && $currentTab === 'upload'): ?>
<form method="POST" style="display: inline-block;">
<input type="hidden" name="tab" value="upload">
<input type="hidden" name="clear_results" value="1">
<button type="submit" class="btn btn-secondary">🗑️ Clear Workflow Data</button>
</form>
<?php endif; ?>
<?php if (isset($results['a2_campaigns']) && !empty($results['a2_campaigns'])): ?>
<h3 style="margin-top: 30px;">Found <?= count($results['a2_campaigns']) ?> Campaigns</h3>
<div class="campaign-grid">
<?php foreach ($results['a2_campaigns'] as $campaign): ?>
<div class="campaign-card <?= (isset($results['selected_campaign_a2']) && $results['selected_campaign_a2']['asset_id'] === $campaign['asset_id']) ? 'selected' : '' ?>"
onclick="selectCampaignA2('<?= htmlspecialchars($campaign['asset_id']) ?>')">
<h3><?= htmlspecialchars($campaign['campaign_name'] ?? 'Unnamed Campaign') ?></h3>
<p><strong>Campaign ID:</strong> <?= htmlspecialchars($campaign['campaign_id'] ?? 'N/A') ?></p>
<p><strong>Asset ID:</strong> <code style="font-size: 11px;"><?= htmlspecialchars($campaign['asset_id']) ?></code></p>
<p><strong>Brand:</strong> <?= htmlspecialchars($campaign['brand'] ?? 'N/A') ?></p>
<p><strong>Market:</strong> <?= htmlspecialchars($campaign['market'] ?? 'N/A') ?></p>
<p><strong>Content Scaling Status:</strong> <span class="status-badge status-a2"><?= htmlspecialchars($campaign['status'] ?? 'N/A') ?> - Assets Sent to Agency</span></p>
<form method="POST" style="margin-top: 10px;" onclick="event.stopPropagation();">
<input type="hidden" name="tab" value="upload">
<input type="hidden" name="action" value="reset_to_a1">
<input type="hidden" name="campaign_id" value="<?= htmlspecialchars($campaign['asset_id']) ?>">
<button type="submit" class="btn btn-danger" style="font-size: 12px; padding: 8px 12px;" onclick="return confirm('Reset <?= htmlspecialchars($campaign['campaign_name']) ?> to A1 for testing?')">
🔄 Reset to A1
</button>
</form>
</div>
<?php endforeach; ?>
</div>
<form method="POST" id="select-campaign-a2-form">
<input type="hidden" name="tab" value="upload">
<input type="hidden" name="action" value="select_campaign_a2">
<input type="hidden" name="campaign_id" id="selected-campaign-a2-id">
<button type="submit" class="btn btn-success" id="select-campaign-a2-btn" disabled>
Select Campaign & Continue
</button>
</form>
<?php endif; ?>
<?php if (isset($results['selected_campaign_a2'])): ?>
<div class="alert alert-info" style="margin-top: 30px;">
<strong>Selected Campaign:</strong>
<?= htmlspecialchars($results['selected_campaign_a2']['campaign_name']) ?>
(<?= htmlspecialchars($results['selected_campaign_a2']['campaign_id']) ?>)
</div>
<?php if (!isset($results['upload_folder_id'])): ?>
<form method="POST" style="margin-top: 20px;">
<input type="hidden" name="tab" value="upload">
<input type="hidden" name="action" value="get_upload_folder">
<button type="submit" class="btn btn-primary">Find Upload Folder (Final Assets)</button>
</form>
<?php endif; ?>
<?php endif; ?>
<?php if (isset($results['upload_folder_id'])): ?>
<div class="alert alert-success" style="margin-top: 20px;">
✅ Upload target folder found
</div>
<h3 style="margin-top: 30px;">Upload Processed Files</h3>
<?php if (isset($results['master_assets_metadata'])): ?>
<div class="alert alert-success" style="margin-bottom: 20px;">
✅ Master asset metadata detected - uploads will inherit metadata from downloaded assets
</div>
<?php else: ?>
<div class="alert alert-info" style="margin-bottom: 20px;">
<strong>Note:</strong> For best results, complete the full workflow in one session:
Download (A1) → Update to A2 → Upload (A2)
<br><small>This preserves master asset metadata for the uploaded files.</small>
</div>
<?php endif; ?>
<form method="POST" enctype="multipart/form-data">
<input type="hidden" name="tab" value="upload">
<input type="hidden" name="action" value="upload_files">
<div style="background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px 0;">
<label style="display: block; margin-bottom: 10px; font-weight: 600;">
Select files to upload:
</label>
<input type="file" name="upload_files[]" multiple
accept=".jpg,.jpeg,.png,.gif,.pdf,.ai,.psd,.eps,.tif,.tiff,.mov,.mp4,.avi,.zip,.txt,.doc,.docx,.xls,.xlsx"
style="padding: 10px; border: 2px dashed #667eea; border-radius: 6px; width: 100%; background: white;">
<p style="font-size: 13px; color: #666; margin-top: 10px;">
Accepted formats: Images (JPG, PNG, GIF, TIFF), Documents (PDF, AI, PSD, EPS), Video (MOV, MP4, AVI), Archives (ZIP), Office (DOC, XLS, TXT)
</p>
</div>
<button type="submit" class="btn btn-success">📤 Upload Files to DAM</button>
</form>
<?php endif; ?>
<?php if (isset($results['upload_results'])): ?>
<?php $ur = $results['upload_results']; ?>
<div class="alert alert-<?= $ur['successful'] > 0 ? 'success' : 'error' ?>" style="margin-top: 20px;">
<strong>Upload Results:</strong>
<?= $ur['successful'] ?> of <?= $ur['total'] ?> files uploaded successfully
<?php if (!empty($ur['details'])): ?>
<details style="margin-top: 15px;">
<summary style="cursor: pointer;">View upload details</summary>
<div style="margin-top: 10px;">
<?php foreach ($ur['details'] as $idx => $detail): ?>
<div style="padding: 8px; margin: 5px 0; background: white; border-radius: 4px;">
<?php if ($detail['success']): ?>
✅ <?= htmlspecialchars($detail['filename']) ?>
<br><small>Asset ID: <?= htmlspecialchars($detail['asset_id'] ?? 'N/A') ?></small>
<?php else: ?>
❌ <?= htmlspecialchars($detail['filename'] ?? 'Unknown') ?>
<br><small style="color: #dc3545;"><strong>Error:</strong> <?= htmlspecialchars($detail['error']) ?></small>
<?php if (isset($detail['http_code'])): ?>
<br><small>HTTP Code: <?= $detail['http_code'] ?></small>
<?php endif; ?>
<?php if (isset($detail['response_body']) && !empty($detail['response_body'])): ?>
<details style="margin-top: 5px;">
<summary style="cursor: pointer; font-size: 11px;">View API Response</summary>
<pre style="font-size: 10px; background: #f8f9fa; padding: 5px; margin-top: 5px; max-height: 200px; overflow: auto;"><?= htmlspecialchars($detail['response_body']) ?></pre>
</details>
<?php endif; ?>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
</details>
<?php endif; ?>
</div>
<?php if ($ur['successful'] > 0): ?>
<div style="margin-top: 30px; padding: 20px; background: #d4edda; border-radius: 8px;">
<h4>✅ Update Status to A3</h4>
<p style="margin: 10px 0;">
Files have been uploaded. Update the campaign status to A3
(Localized Asset received from Agency)
</p>
<form method="POST">
<input type="hidden" name="tab" value="upload">
<input type="hidden" name="action" value="update_status_to_a3">
<button type="submit" class="btn btn-success">Update Status: A2 → A3</button>
</form>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
<!-- Rework Workflow Tab -->
<div id="tab-rework" class="tab-content <?= $currentTab === 'rework' ? 'active' : '' ?>">
<h2>🔄 Rework Workflow: A5 → A6</h2>
<p style="color: #666; margin: 10px 0 20px 0;">
Load campaigns with status A5 (rework needed), download assets for rework, and update status to A6
</p>
<form method="POST" style="display: inline-block;">
<input type="hidden" name="tab" value="rework">
<input type="hidden" name="action" value="load_campaigns_a5">
<button type="submit" class="btn btn-primary">Load Campaigns (Content Scaling Status = A5)</button>
</form>
<?php if (!empty($results) && $currentTab === 'rework'): ?>
<form method="POST" style="display: inline-block;">
<input type="hidden" name="tab" value="rework">
<input type="hidden" name="clear_results" value="1">
<button type="submit" class="btn btn-secondary">🗑️ Clear Workflow Data</button>
</form>
<?php endif; ?>
<?php if (isset($results['a5_campaigns']) && !empty($results['a5_campaigns'])): ?>
<h3 style="margin-top: 30px;">Found <?= count($results['a5_campaigns']) ?> Campaigns Needing Rework</h3>
<div class="campaign-grid">
<?php foreach ($results['a5_campaigns'] as $campaign): ?>
<div class="campaign-card <?= (isset($results['selected_campaign_a5']) && $results['selected_campaign_a5']['asset_id'] === $campaign['asset_id']) ? 'selected' : '' ?>"
onclick="selectCampaignA5('<?= htmlspecialchars($campaign['asset_id']) ?>')">
<h3><?= htmlspecialchars($campaign['campaign_name'] ?? 'Unnamed Campaign') ?></h3>
<p><strong>Campaign ID:</strong> <?= htmlspecialchars($campaign['campaign_id'] ?? 'N/A') ?></p>
<p><strong>Asset ID:</strong> <code style="font-size: 11px;"><?= htmlspecialchars($campaign['asset_id']) ?></code></p>
<p><strong>Brand:</strong> <?= htmlspecialchars($campaign['brand'] ?? 'N/A') ?></p>
<p><strong>Market:</strong> <?= htmlspecialchars($campaign['market'] ?? 'N/A') ?></p>
<p><strong>Content Scaling Status:</strong> <span class="status-badge status-a5"><?= htmlspecialchars($campaign['status'] ?? 'N/A') ?> - Rework Needed</span></p>
</div>
<?php endforeach; ?>
</div>
<form method="POST" id="select-campaign-a5-form">
<input type="hidden" name="tab" value="rework">
<input type="hidden" name="action" value="select_campaign_a5">
<input type="hidden" name="campaign_id" id="selected-campaign-a5-id">
<button type="submit" class="btn btn-success" id="select-campaign-a5-btn" disabled>
Select Campaign & Continue
</button>
</form>
<?php endif; ?>
<?php if (isset($results['selected_campaign_a5'])): ?>
<div class="alert alert-info" style="margin-top: 30px;">
<strong>Selected Campaign:</strong>
<?= htmlspecialchars($results['selected_campaign_a5']['campaign_name']) ?>
(<?= htmlspecialchars($results['selected_campaign_a5']['campaign_id']) ?>)
</div>
<form method="POST" style="margin-top: 20px;">
<input type="hidden" name="tab" value="rework">
<input type="hidden" name="action" value="get_rework_assets">
<button type="submit" class="btn btn-primary">Get Rework Assets</button>
</form>
<?php endif; ?>
<?php if (isset($results['rework_assets']) && !empty($results['rework_assets'])): ?>
<h3 style="margin-top: 30px;">Rework Assets (<?= count($results['rework_assets']) ?> files)</h3>
<form method="POST" style="margin: 20px 0;">
<input type="hidden" name="tab" value="rework">
<input type="hidden" name="action" value="download_all_rework">
<button type="submit" class="btn btn-success">📥 Download All Rework Assets</button>
</form>
<div class="asset-list">
<?php foreach ($results['rework_assets'] as $index => $asset): ?>
<?php
$assetName = extractFolderName($asset);
$assetId = $asset['asset_id'];
$metadataId = 'rework-metadata-' . $index;
?>
<div class="asset-item">
<div class="asset-info">
<strong><?= htmlspecialchars($assetName) ?></strong><br>
<small>ID: <?= htmlspecialchars($assetId) ?></small><br>
<small>Type: <?= htmlspecialchars($asset['mime_type'] ?? 'Unknown') ?></small>
<?php if (isset($asset['file_size'])): ?>
<small> | Size: <?= number_format($asset['file_size']) ?> bytes</small>
<?php endif; ?>
</div>
<div class="asset-actions">
<form method="POST" style="display: inline;">
<input type="hidden" name="tab" value="rework">
<input type="hidden" name="action" value="download_rework_asset">
<input type="hidden" name="asset_id" value="<?= htmlspecialchars($assetId) ?>">
<input type="hidden" name="filename" value="<?= htmlspecialchars($assetName) ?>">
<button type="submit" class="btn btn-success">📥 Download</button>
</form>
<button class="btn btn-secondary"
onclick="toggleMetadata('<?= $metadataId ?>')">
📋 Metadata
</button>
</div>
</div>
<div id="<?= $metadataId ?>" class="metadata-display" style="display: none;">
<?php
$metadata = MetadataExtractor::extractAllMetadata($asset);
if (!empty($metadata)):
?>
<div style="font-size: 13px;">
<!-- Same metadata display as download tab -->
<?php if (isset($metadata['basic'])): ?>
<div style="background: white; padding: 12px; margin: 8px 0; border-radius: 4px; border-left: 4px solid #667eea;">
<strong>📋 Basic Info:</strong><br>
<div style="margin-top: 8px; display: grid; grid-template-columns: 150px 1fr; gap: 5px;">
<?php foreach ($metadata['basic'] as $key => $value): ?>
<?php if ($value !== null): ?>
<div style="color: #666;"><?= ucwords(str_replace('_', ' ', $key)) ?>:</div>
<div><strong><?= htmlspecialchars($value) ?></strong></div>
<?php endif; ?>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
<?php else: ?>
<p>No metadata available for this asset</p>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<div style="margin-top: 30px; padding: 20px; background: #fff3cd; border-radius: 8px;">
<h4>⚠️ Update Status to A6</h4>
<p style="margin: 10px 0;">
Once rework assets are downloaded, update the campaign status to A6
(Assets to be reworked received by the Agency)
</p>
<form method="POST">
<input type="hidden" name="tab" value="rework">
<input type="hidden" name="action" value="update_status_to_a6">
<button type="submit" class="btn btn-success">Update Status: A5 → A6</button>
</form>
</div>
<?php endif; ?>
<?php if (isset($results['last_rework_download'])): ?>
<?php $dl = $results['last_rework_download']; ?>
<div class="alert alert-<?= $dl['success'] ? 'success' : 'error' ?>" style="margin-top: 20px;">
<?php if ($dl['success']): ?>
<strong>✅ Download Successful:</strong> <?= htmlspecialchars($dl['filename']) ?>
<br><small>Size: <?= number_format($dl['size']) ?> bytes</small>
<?php else: ?>
<strong>❌ Download Failed:</strong> <?= htmlspecialchars($dl['error']) ?>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if (isset($results['rework_bulk_download'])): ?>
<div class="alert alert-success" style="margin-top: 20px;">
<strong>Bulk Download Results:</strong>
<?= $results['rework_bulk_download']['successful'] ?> of <?= $results['rework_bulk_download']['total'] ?> rework assets downloaded
</div>
<?php endif; ?>
</div>
</div>
<script>
function switchTab(tabName) {
// Update tabs
document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
event.target.classList.add('active');
document.getElementById('tab-' + tabName).classList.add('active');
// Update URL without reload
const url = new URL(window.location);
url.searchParams.set('tab', tabName);
window.history.pushState({}, '', url);
}
function selectCampaign(campaignId) {
document.querySelectorAll('.campaign-card').forEach(card => {
card.classList.remove('selected');
});
event.currentTarget.classList.add('selected');
document.getElementById('selected-campaign-id').value = campaignId;
document.getElementById('select-campaign-btn').disabled = false;
}
function toggleMetadata(metadataId) {
const metadataDiv = document.getElementById(metadataId);
if (metadataDiv) {
metadataDiv.style.display = metadataDiv.style.display === 'none' ? 'block' : 'none';
}
}
function selectCampaignA2(campaignId) {
document.querySelectorAll('.campaign-card').forEach(card => {
card.classList.remove('selected');
});
event.currentTarget.classList.add('selected');
document.getElementById('selected-campaign-a2-id').value = campaignId;
document.getElementById('select-campaign-a2-btn').disabled = false;
}
function selectCampaignA5(campaignId) {
document.querySelectorAll('.campaign-card').forEach(card => {
card.classList.remove('selected');
});
event.currentTarget.classList.add('selected');
document.getElementById('selected-campaign-a5-id').value = campaignId;
document.getElementById('select-campaign-a5-btn').disabled = false;
}
</script>
</body>
</html>