File: /home/xedaptot/be.naniguide.com/app/Http/Controllers/Refactor/BuilderExportController.php
<?php
namespace App\Http\Controllers\Refactor;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use ZipArchive;
use RecursiveIteratorIterator;
use RecursiveDirectoryIterator;
class BuilderExportController extends Controller
{
/**
* Export builder HTML as a downloadable .html file.
* Receives the rendered HTML from the builder (absolute URLs intact).
*/
public function html(Request $request)
{
$html = $request->input('html', '');
$baseUrl = rtrim($request->input('baseUrl', ''), '/');
if (empty($html)) {
return response()->json(['error' => 'No HTML content provided.'], 422);
}
// Convert root-relative URLs to absolute (src="/..." → src="https://domain/...")
if ($baseUrl) {
$html = preg_replace(
'/((?:src|href|action)=")(\/)/',
'$1' . $baseUrl . '/',
$html
);
}
return response($html, 200, [
'Content-Type' => 'text/html; charset=utf-8',
'Content-Disposition' => 'attachment; filename="index.html"',
'Content-Length' => strlen($html),
]);
}
/**
* Export builder HTML + theme assets as a downloadable .zip file.
* Makes asset URLs relative by stripping themeUrl prefix from the HTML,
* then bundles the HTML + theme directory into a ZIP.
*/
public function zip(Request $request)
{
$html = $request->input('html', '');
$themeUrl = rtrim($request->input('themeUrl', ''), '/');
$theme = $request->input('theme', 'default');
if (empty($html)) {
return response()->json(['error' => 'No HTML content provided.'], 422);
}
// Convert absolute asset URLs → relative paths (strip themeUrl prefix)
if ($themeUrl) {
$html = str_replace($themeUrl . '/', '', $html);
}
// Resolve theme directory on disk
$themeDir = storage_path(join_paths('themes', $theme));
if (!is_dir($themeDir)) {
// Fallback to resource themes (built-in)
$themeDir = resource_path(join_paths('themes', $theme));
}
if (!is_dir($themeDir)) {
return response()->json(['error' => "Theme directory not found: {$theme}"], 422);
}
// Write HTML to temp file
$tmpDir = sys_get_temp_dir() . '/builder_export_' . uniqid();
mkdir($tmpDir, 0755, true);
$zipPath = $tmpDir . '/export.zip';
$zip = new ZipArchive();
if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
return response()->json(['error' => 'Failed to create ZIP archive.'], 500);
}
// Add modified index.html
$zip->addFromString('index.html', $html);
// Add theme assets, skipping builder-internal files
$skip = ['store.json', 'thumb.png', 'thumb.svg', 'index.json', 'index.html'];
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($themeDir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $file) {
if (!$file->isFile()) {
continue;
}
$absPath = $file->getRealPath();
$relPath = substr($absPath, strlen($themeDir) + 1);
// Skip builder-internal files
if (in_array(basename($relPath), $skip)) {
continue;
}
// Skip template HTML files and widgets folder
if (preg_match('/\.template\.html$/', $relPath)) {
continue;
}
if (strpos($relPath, 'widgets/') === 0 || strpos($relPath, 'widgets\\') === 0) {
continue;
}
$zip->addFile($absPath, $relPath);
}
$zip->close();
$filename = preg_replace('/[^a-zA-Z0-9_-]/', '_', $theme) . '.zip';
return response()->download($zipPath, $filename, [
'Content-Type' => 'application/zip',
])->deleteFileAfterSend(true);
}
}