Commit 6e57c40a authored by Mahmoud Aglan's avatar Mahmoud Aglan

cool

parent 1bf257c9
......@@ -62,27 +62,6 @@ COPY . /var/www/html/
WORKDIR /var/www/html
RUN composer install --no-dev --optimize-autoloader --no-interaction --no-scripts
# ── Create optimized JPG copies of screenshots for PDF export ──
RUN if [ -d /var/www/html/public/assets/tutorials/screenshots ]; then \
php -r ' \
$dir = "/var/www/html/public/assets/tutorials/screenshots"; \
foreach (glob("$dir/*.png") as $f) { \
$img = @imagecreatefrompng($f); \
if (!$img) continue; \
$w = imagesx($img); $h = imagesy($img); \
if ($w > 900) { \
$nw = 900; $nh = (int)($h * 900 / $w); \
$dst = imagecreatetruecolor($nw, $nh); \
imagecopyresampled($dst, $img, 0, 0, 0, 0, $nw, $nh, $w, $h); \
imagedestroy($img); $img = $dst; \
} \
$jpg = substr($f, 0, -4) . ".jpg"; \
imagejpeg($img, $jpg, 55); \
imagedestroy($img); \
} \
'; \
fi
# ── Create storage directories ──
RUN mkdir -p \
/var/www/html/storage/logs \
......
......@@ -558,10 +558,6 @@ class TutorialController extends Controller
public function exportPdf(Request $request): Response
{
set_time_limit(600);
$dlToken = $_GET['dl'] ?? '';
$screenshotsPath = realpath(__DIR__ . '/../../../../public/assets/tutorials/screenshots');
$viewsPath = realpath(__DIR__ . '/../Views');
......@@ -572,70 +568,17 @@ class TutorialController extends Controller
include __DIR__ . '/../Views/export_pdf.php';
$html = ob_get_clean();
$wkhtmltopdf = null;
foreach (['/usr/local/bin/wkhtmltopdf', '/usr/bin/wkhtmltopdf'] as $path) {
if (file_exists($path) && is_executable($path)) {
$wkhtmltopdf = $path;
break;
}
}
if (!$wkhtmltopdf) {
$wkhtmltopdf = trim((string) shell_exec('which wkhtmltopdf 2>/dev/null'));
}
if ($wkhtmltopdf) {
$tmpInput = tempnam(sys_get_temp_dir(), 'pdf_in_') . '.html';
$tmpOutput = tempnam(sys_get_temp_dir(), 'pdf_out_') . '.pdf';
file_put_contents($tmpInput, $html);
$cmd = escapeshellarg($wkhtmltopdf)
. ' --encoding utf-8 --page-size A4'
. ' --margin-top 15 --margin-bottom 15 --margin-left 12 --margin-right 12'
. ' --enable-local-file-access'
. ' --no-stop-slow-scripts'
. ' --disable-javascript'
. ' --image-quality 60'
. ' --image-dpi 100'
. ' --lowquality'
. ' ' . escapeshellarg($tmpInput)
. ' ' . escapeshellarg($tmpOutput)
. ' 2>&1';
exec($cmd, $output, $returnCode);
if ($returnCode > 1) {
error_log('wkhtmltopdf failed (code ' . $returnCode . '): ' . implode("\n", $output));
}
@unlink($tmpInput);
$printBar = '<div id="printBar" style="position:fixed;top:0;left:0;right:0;z-index:99999;background:#1E40AF;color:#fff;padding:12px 24px;display:flex;align-items:center;justify-content:space-between;font-family:sans-serif;box-shadow:0 2px 8px rgba(0,0,0,.2);">'
. '<span style="font-size:14px;">لحفظ الكتاب كـ PDF: اضغط <b>Ctrl+P</b> (أو <b>⌘+P</b>) ثم اختر "حفظ كـ PDF"</span>'
. '<button onclick="document.getElementById(\'printBar\').remove();window.print();" style="background:#fff;color:#1E40AF;border:none;padding:8px 20px;border-radius:6px;font-size:14px;font-weight:700;cursor:pointer;">طباعة / حفظ PDF</button>'
. '</div><div style="height:52px;"></div>';
$html = str_replace('<body>', '<body>' . $printBar, $html);
if (($returnCode === 0 || $returnCode === 1) && file_exists($tmpOutput) && filesize($tmpOutput) > 0) {
$pdfContent = file_get_contents($tmpOutput);
@unlink($tmpOutput);
if ($dlToken) {
setcookie('book_download', $dlToken, time() + 120, '/');
}
$response = new Response();
return $response->html($pdfContent, 200)->withHeaders([
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'attachment; filename="Book-of-the-ERP.pdf"',
'Content-Length' => (string) strlen($pdfContent),
]);
}
@unlink($tmpOutput);
}
if ($dlToken) {
setcookie('book_download', $dlToken, time() + 120, '/');
}
$printCss = '<style>@media print { #printBar, #printBar + div { display:none !important; } }</style>';
$html = str_replace('</head>', $printCss . '</head>', $html);
$response = new Response();
return $response->html($html, 200)->withHeaders([
'Content-Type' => 'text/html; charset=utf-8',
'Content-Disposition' => 'attachment; filename="Book-of-the-ERP.html"',
]);
return $response->html($html);
}
public function collectBookData(string $screenshotsPath, string $viewsPath): array
......@@ -693,13 +636,9 @@ class TutorialController extends Controller
$categories = TutorialRegistry::getCategories($sectionKey);
$screenshot = null;
if ($screenshotsPath) {
$base = $sectionKey;
$baseAlt = str_replace('-', '_', $sectionKey);
$possibleFiles = [
$screenshotsPath . '/' . $base . '.jpg',
$screenshotsPath . '/' . $base . '.png',
$screenshotsPath . '/' . $baseAlt . '.jpg',
$screenshotsPath . '/' . $baseAlt . '.png',
$screenshotsPath . '/' . $sectionKey . '.png',
$screenshotsPath . '/' . str_replace('-', '_', $sectionKey) . '.png',
];
foreach ($possibleFiles as $f) {
if (file_exists($f)) {
......@@ -740,22 +679,6 @@ class TutorialController extends Controller
$content = preg_replace('/<div class="tut-breadcrumb">.*?<\/div>/s', '', $content);
$content = str_replace('loading="lazy"', '', $content);
$publicPath = realpath(__DIR__ . '/../../../../public');
$content = (string) preg_replace_callback(
'#src="(/assets/[^"]+)"#',
function (array $m) use ($publicPath): string {
$rel = $m[1];
if (str_ends_with($rel, '.png')) {
$jpgPath = $publicPath . substr($rel, 0, -4) . '.jpg';
if (file_exists($jpgPath)) {
return 'src="file://' . $jpgPath . '"';
}
}
return 'src="file://' . $publicPath . $rel . '"';
},
$content
);
$content = preg_replace('/<i\s+data-lucide="[^"]*"[^>]*><\/i>/', '', $content);
return $content;
......
......@@ -125,83 +125,27 @@
</style>
<script>
var exportSteps = [
{ pct: 5, label: 'تحضير البيانات...' },
{ pct: 12, label: 'تجميع شروحات العضوية...' },
{ pct: 20, label: 'تجميع شروحات الأنشطة الرياضية...' },
{ pct: 28, label: 'تجميع شروحات الخزنة...' },
{ pct: 35, label: 'تجميع باقي الأقسام...' },
{ pct: 45, label: 'تضمين لقطات الشاشة...' },
{ pct: 55, label: 'توليد ملف PDF...' },
{ pct: 65, label: 'معالجة الصفحات...' },
{ pct: 72, label: 'تنسيق المحتوى...' },
{ pct: 80, label: 'ضغط الملف...' },
{ pct: 88, label: 'المراجعة النهائية...' },
{ pct: 94, label: 'جاري الانتهاء...' },
];
var stepTimer = null;
var cookieTimer = null;
function startExport() {
document.getElementById('beforeExport').style.display = 'none';
document.getElementById('duringExport').style.display = 'block';
document.getElementById('afterExport').style.display = 'none';
document.getElementById('errorExport').style.display = 'none';
var stepIndex = 0;
var progressBar = document.getElementById('progressBar');
var progressLabel = document.getElementById('progressLabel');
var progressStep = document.getElementById('progressStep');
progressBar.style.width = '30%';
progressLabel.textContent = '30%';
progressStep.textContent = 'جاري فتح الكتاب...';
function animateStep() {
if (stepIndex >= exportSteps.length) return;
var step = exportSteps[stepIndex];
progressBar.style.width = step.pct + '%';
progressLabel.textContent = step.pct + '%';
progressStep.textContent = step.label;
stepIndex++;
if (stepIndex < exportSteps.length) {
stepTimer = setTimeout(animateStep, 8000);
}
}
animateStep();
// Use hidden iframe — browsers don't timeout iframe downloads
var token = Date.now().toString();
var iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.setAttribute('id', 'exportFrame');
iframe.src = '/tutorials/export-pdf?dl=' + token;
document.body.appendChild(iframe);
// Poll for download cookie (set by server when response is sent)
cookieTimer = setInterval(function() {
if (document.cookie.indexOf('book_download=' + token) !== -1) {
clearInterval(cookieTimer);
clearTimeout(stepTimer);
// Clear the cookie
document.cookie = 'book_download=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT';
progressBar.style.width = '100%';
progressLabel.textContent = '100%';
progressStep.textContent = 'اكتمل! تم التحميل ✓';
setTimeout(function() { finishExport(true); }, 1500);
// Clean up iframe
var f = document.getElementById('exportFrame');
if (f) setTimeout(function() { f.remove(); }, 5000);
}
}, 2000);
var w = window.open('/tutorials/export-pdf', '_blank');
// Safety timeout: 10 minutes max
setTimeout(function() {
if (document.getElementById('duringExport').style.display !== 'none') {
clearInterval(cookieTimer);
clearTimeout(stepTimer);
document.getElementById('errorMessage').textContent = 'انتهت المهلة. قد يكون الملف قد تم تحميله — تحقق من مجلد التنزيلات.';
finishExport(false);
var f = document.getElementById('exportFrame');
if (f) f.remove();
}
}, 600000);
progressBar.style.width = '100%';
progressLabel.textContent = '100%';
progressStep.textContent = 'تم فتح الكتاب ✓';
setTimeout(function() { finishExport(true); }, 1000);
}, 2000);
}
function finishExport(success) {
......@@ -219,8 +163,6 @@ function resetExport() {
document.getElementById('afterExport').style.display = 'none';
document.getElementById('errorExport').style.display = 'none';
document.getElementById('progressBar').style.width = '0%';
clearInterval(cookieTimer);
clearTimeout(stepTimer);
}
document.addEventListener('DOMContentLoaded', function() {
......
......@@ -889,8 +889,9 @@ $sectionIcons = [
</div>
<?php if (!empty($section['screenshot']) && file_exists($section['screenshot'])): ?>
<?php $webPath = str_replace(realpath(__DIR__ . '/../../../../public'), '', $section['screenshot']); ?>
<div style="text-align:center;margin: 24px 0;page-break-inside:avoid;">
<img src="file://<?= $section['screenshot'] ?>" class="tutorial-screenshot" alt="<?= htmlspecialchars($section['title'], ENT_QUOTES, 'UTF-8') ?>">
<img src="<?= $webPath ?>" class="tutorial-screenshot" alt="<?= htmlspecialchars($section['title'], ENT_QUOTES, 'UTF-8') ?>">
</div>
<?php endif; ?>
......
......@@ -2,7 +2,7 @@
upload_max_filesize = 20M
post_max_size = 25M
memory_limit = 2048M
memory_limit = 1024M
max_execution_time = 1800
max_input_time = 240
max_input_vars = 5000
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment