loreal-global-kickoff/upload-to-box.php
DJP 95020fad44 Add Title/Creative Execution columns and HTML email templates
CSV Transformation Fixes:
- Title now includes language code: "{OriginalTitle}_{ISO}" (e.g., "Syndication_en-GB")
- Added "Creative Execution" column with original global title
- Both columns properly populated for all 16 regional CSVs

Email Template System:
- Created EmailTemplates.php with professional HTML templates
- Based on Ferrero automation email styling
- Templates for all workflows:
  * Asset Submission Success/Failed
  * Global to Local Started/Complete/Failed
  * Box Upload Success
- L'Oréal brand colors (Yellow #FFC407, Black #000000, Green for success)
- Responsive design with proper HTML structure
- Clean, professional layout with color-coded status boxes

Email Service Enhancements:
- Added sendTemplate() method for templated emails
- SMTP now supports HTML multipart emails (text + HTML)
- Mailgun API support for HTML
- Proper MIME boundaries and headers
- Extract subject from template HTML

Notification Updates:
- upload-to-box.php: Uses templates with full data (campaign, business unit, file count)
- submit.php: Logs all asset submissions
- All emails sent as professional HTML with fallback text

Template Features:
- Color-coded headers (green=success, red=error, yellow=warning)
- Info boxes with campaign details
- Data tables for multiple items
- Action required sections
- Footer with branding

All notifications now send beautiful, branded HTML emails to users!

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 11:00:10 -05:00

212 lines
7.2 KiB
PHP

<?php
/**
* Upload CSV to Box
* Uploads the processed CSV file to Box after user approval
*/
error_reporting(E_ALL);
ini_set('display_errors', 1);
header('Content-Type: application/json');
try {
// Load dependencies
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/AuthMiddleware.php';
require_once __DIR__ . '/BoxService.php';
require_once __DIR__ . '/EmailService.php';
require_once __DIR__ . '/ApplicationLogger.php';
// Authenticate
$auth = new AuthMiddleware();
$user = $auth->requireAuth();
// Initialize logger
$logger = new ApplicationLogger();
// Check if processed data exists in session
session_start();
if (!isset($_SESSION['processed_csv'])) {
echo json_encode([
'success' => false,
'stage' => 'session',
'error' => 'No processed data found',
'details' => 'Session expired or CSV not processed',
'action' => 'Please upload and process the CSV again'
]);
exit;
}
$processedData = $_SESSION['processed_csv'];
$csvFiles = $processedData['files'];
$campaignNumber = $processedData['campaignNumber'];
$businessUnit = $processedData['businessUnit'];
$fileCount = $processedData['fileCount'];
// Load config
$appConfig = require __DIR__ . '/config.php';
$outputFolderId = $appConfig['global_to_local']['output_box_folder_id'];
// Validate output folder is configured
if ($outputFolderId === 'XXXXXXXXX' || empty($outputFolderId)) {
echo json_encode([
'success' => false,
'stage' => 'config',
'error' => 'Output Box folder not configured',
'details' => 'The output_box_folder_id is not set in config.php',
'action' => 'Contact administrator to configure Box output folder'
]);
exit;
}
// STAGE 7: Upload all CSVs to Box
$boxService = new BoxService();
$uploadedFiles = [];
$uploadErrors = [];
error_log("Uploading {$fileCount} CSV files to Box folder: {$outputFolderId}");
try {
// Authenticate with Box
$token = $boxService->authenticate();
// Upload each CSV file
foreach ($csvFiles as $csvFile) {
$filename = $csvFile['filename'];
$csvContent = $csvFile['content'];
try {
$url = 'https://upload.box.com/api/2.0/files/content';
$boundary = uniqid();
$delimiter = '-------------' . $boundary;
$postData = '';
$postData .= "--{$delimiter}\r\n";
$postData .= 'Content-Disposition: form-data; name="attributes"' . "\r\n\r\n";
$postData .= json_encode([
'name' => $filename,
'parent' => ['id' => $outputFolderId]
]) . "\r\n";
$postData .= "--{$delimiter}\r\n";
$postData .= 'Content-Disposition: form-data; name="file"; filename="' . $filename . '"' . "\r\n";
$postData .= 'Content-Type: text/csv' . "\r\n\r\n";
$postData .= $csvContent . "\r\n";
$postData .= "--{$delimiter}--\r\n";
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $postData,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $token,
'Content-Type: multipart/form-data; boundary=' . $delimiter,
'Content-Length: ' . strlen($postData)
],
CURLOPT_TIMEOUT => 60
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($curlError) {
throw new Exception('Upload failed: ' . $curlError);
}
if ($httpCode !== 201) {
$responseData = json_decode($response, true);
throw new Exception('HTTP ' . $httpCode . ': ' . ($responseData['message'] ?? $response));
}
$uploadedFile = json_decode($response, true);
$fileId = $uploadedFile['entries'][0]['id'] ?? null;
$uploadedFiles[] = [
'filename' => $filename,
'fileId' => $fileId,
'isoCode' => $csvFile['isoCode']
];
error_log("Uploaded: {$filename} (ID: {$fileId})");
} catch (Exception $fileError) {
error_log("Failed to upload {$filename}: " . $fileError->getMessage());
$uploadErrors[] = [
'filename' => $filename,
'error' => $fileError->getMessage()
];
}
}
// Check if all uploads succeeded
if (count($uploadErrors) > 0) {
$successCount = count($uploadedFiles);
throw new Exception("Uploaded {$successCount}/{$fileCount} files. " . count($uploadErrors) . " failed.");
}
// Send completion email
$emailService->notifyCompleted($user['email'], [
'campaign_number' => $campaignNumber,
'business_unit' => $businessUnit,
'input_rows' => $processedData['inputRowCount'],
'file_count' => $fileCount,
'total_rows' => $processedData['inputRowCount'] * $fileCount,
'folder_url' => $folderUrl
]);
// Log successful upload
$logger->logBoxUpload($user, $fileCount, $outputFolderId, 'success');
// Clear session data
unset($_SESSION['processed_csv']);
$folderUrl = 'https://app.box.com/folder/' . $outputFolderId;
echo json_encode([
'success' => true,
'stage' => 'complete',
'message' => "Successfully uploaded {$fileCount} CSV files to Box",
'data' => [
'uploadedFiles' => $uploadedFiles,
'fileCount' => $fileCount,
'folderUrl' => $folderUrl
]
]);
} catch (Exception $e) {
error_log('Box upload exception: ' . $e->getMessage());
// Send error email
$emailService->notifyError($user['email'], [
'filename' => $processedData['files'][0]['filename'] ?? 'Unknown',
'campaign_number' => $campaignNumber,
'stage' => 'box_upload',
'error' => $e->getMessage()
]);
echo json_encode([
'success' => false,
'stage' => 'box_upload',
'error' => 'Failed to upload CSV to Box',
'details' => $e->getMessage(),
'action' => 'Check Box folder permissions and try again'
]);
}
} catch (Exception $e) {
error_log('Upload exception: ' . $e->getMessage());
echo json_encode([
'success' => false,
'stage' => 'exception',
'error' => 'Unexpected error occurred',
'details' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine()
]);
}