Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
Clubphp
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Clubphp
Commits
7f5b59a3
Commit
7f5b59a3
authored
May 23, 2026
by
Mahmoud Aglan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
xorg
parent
69f666e7
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
73 additions
and
116 deletions
+73
-116
TutorialController.php
app/Modules/Tutorials/Controllers/TutorialController.php
+52
-54
Routes.php
app/Modules/Tutorials/Routes.php
+1
-3
book.php
app/Modules/Tutorials/Views/book.php
+20
-59
No files found.
app/Modules/Tutorials/Controllers/TutorialController.php
View file @
7f5b59a3
...
@@ -556,71 +556,69 @@ class TutorialController extends Controller
...
@@ -556,71 +556,69 @@ class TutorialController extends Controller
]);
]);
}
}
public
function
export
Start
(
Request
$request
)
:
Response
public
function
export
Pdf
(
Request
$request
)
:
Response
{
{
$storageDir
=
__DIR__
.
'/../../../../storage/cache'
;
set_time_limit
(
480
);
$lockFile
=
$storageDir
.
'/book-export.lock'
;
$pdfFile
=
$storageDir
.
'/book-export.pdf'
;
if
(
file_exists
(
$lockFile
)
&&
(
time
()
-
filemtime
(
$lockFile
))
<
300
)
{
$screenshotsPath
=
realpath
(
__DIR__
.
'/../../../../public/assets/tutorials/screenshots'
);
return
$this
->
json
([
'status'
=>
'processing'
]);
$viewsPath
=
realpath
(
__DIR__
.
'/../Views'
);
}
file_put_contents
(
$lockFile
,
'started'
);
@
unlink
(
$pdfFile
);
$phpBinary
=
PHP_BINARY
;
$bookData
=
$this
->
collectBookData
(
$screenshotsPath
,
$viewsPath
);
$scriptPath
=
realpath
(
__DIR__
.
'/../../../../cli.php'
);
$cmd
=
sprintf
(
'%s %s export:book > /dev/null 2>&1 &'
,
escapeshellarg
(
$phpBinary
),
escapeshellarg
(
$scriptPath
)
);
exec
(
$cmd
);
return
$this
->
json
([
'status'
=>
'started'
]);
ob_start
();
}
$data
=
$bookData
;
include
__DIR__
.
'/../Views/export_pdf.php'
;
public
function
exportStatus
(
Request
$request
)
:
Response
$html
=
ob_get_clean
();
{
$storageDir
=
__DIR__
.
'/../../../../storage/cache'
;
$lockFile
=
$storageDir
.
'/book-export.lock'
;
$pdfFile
=
$storageDir
.
'/book-export.pdf'
;
$errorFile
=
$storageDir
.
'/book-export.error'
;
if
(
file_exists
(
$errorFile
))
{
$error
=
file_get_contents
(
$errorFile
);
@
unlink
(
$errorFile
);
@
unlink
(
$lockFile
);
return
$this
->
json
([
'status'
=>
'error'
,
'message'
=>
$error
]);
}
if
(
file_exists
(
$pdfFile
))
{
$wkhtmltopdf
=
null
;
@
unlink
(
$lockFile
);
foreach
([
'/usr/local/bin/wkhtmltopdf'
,
'/usr/bin/wkhtmltopdf'
]
as
$path
)
{
return
$this
->
json
([
'status'
=>
'done'
,
'size'
=>
filesize
(
$pdfFile
)]);
if
(
file_exists
(
$path
)
&&
is_executable
(
$path
))
{
$wkhtmltopdf
=
$path
;
break
;
}
}
}
if
(
!
$wkhtmltopdf
)
{
if
(
file_exists
(
$lockFile
))
{
$wkhtmltopdf
=
trim
((
string
)
shell_exec
(
'which wkhtmltopdf 2>/dev/null'
));
return
$this
->
json
([
'status'
=>
'processing'
]);
}
}
return
$this
->
json
([
'status'
=>
'idle'
]);
if
(
$wkhtmltopdf
)
{
}
$tmpInput
=
tempnam
(
sys_get_temp_dir
(),
'pdf_in_'
)
.
'.html'
;
$tmpOutput
=
tempnam
(
sys_get_temp_dir
(),
'pdf_out_'
)
.
'.pdf'
;
public
function
exportDownload
(
Request
$request
)
:
Response
file_put_contents
(
$tmpInput
,
$html
);
{
$pdfFile
=
realpath
(
__DIR__
.
'/../../../../storage/cache'
)
.
'/book-export.pdf'
;
$cmd
=
escapeshellarg
(
$wkhtmltopdf
)
.
' --encoding utf-8 --page-size A4'
if
(
!
file_exists
(
$pdfFile
))
{
.
' --margin-top 15 --margin-bottom 15 --margin-left 12 --margin-right 12'
return
$this
->
json
([
'error'
=>
'File not found'
],
404
);
.
' --enable-local-file-access'
.
' --no-stop-slow-scripts'
.
' --javascript-delay 200'
.
' --image-quality 85'
.
' --print-media-type'
.
' '
.
escapeshellarg
(
$tmpInput
)
.
' '
.
escapeshellarg
(
$tmpOutput
)
.
' 2>&1'
;
exec
(
$cmd
,
$output
,
$returnCode
);
@
unlink
(
$tmpInput
);
if
((
$returnCode
===
0
||
$returnCode
===
1
)
&&
file_exists
(
$tmpOutput
)
&&
filesize
(
$tmpOutput
)
>
0
)
{
$pdfContent
=
file_get_contents
(
$tmpOutput
);
@
unlink
(
$tmpOutput
);
$response
=
new
Response
();
return
$response
->
html
(
$pdfContent
,
200
,
[
'Content-Type'
=>
'application/pdf'
,
'Content-Disposition'
=>
'attachment; filename="Book-of-the-ERP.pdf"'
,
'Content-Length'
=>
(
string
)
strlen
(
$pdfContent
),
]);
}
@
unlink
(
$tmpOutput
);
}
}
$content
=
file_get_contents
(
$pdfFile
);
$response
=
new
Response
();
$response
=
new
Response
();
return
$response
->
html
(
$content
,
200
,
[
return
$response
->
html
(
$html
,
200
,
[
'Content-Type'
=>
'application/pdf'
,
'Content-Type'
=>
'text/html; charset=utf-8'
,
'Content-Disposition'
=>
'attachment; filename="Book-of-the-ERP.pdf"'
,
'Content-Disposition'
=>
'attachment; filename="Book-of-the-ERP.html"'
,
'Content-Length'
=>
(
string
)
strlen
(
$content
),
]);
]);
}
}
...
...
app/Modules/Tutorials/Routes.php
View file @
7f5b59a3
...
@@ -4,9 +4,7 @@ declare(strict_types=1);
...
@@ -4,9 +4,7 @@ declare(strict_types=1);
return
[
return
[
[
'GET'
,
'/tutorials'
,
'Tutorials\Controllers\TutorialController@index'
,
[
'auth'
],
'tutorials.view'
],
[
'GET'
,
'/tutorials'
,
'Tutorials\Controllers\TutorialController@index'
,
[
'auth'
],
'tutorials.view'
],
[
'GET'
,
'/tutorials/book'
,
'Tutorials\Controllers\TutorialController@bookPage'
,
[
'auth'
],
'tutorials.view'
],
[
'GET'
,
'/tutorials/book'
,
'Tutorials\Controllers\TutorialController@bookPage'
,
[
'auth'
],
'tutorials.view'
],
[
'POST'
,
'/tutorials/export-start'
,
'Tutorials\Controllers\TutorialController@exportStart'
,
[
'auth'
],
'tutorials.view'
],
[
'GET'
,
'/tutorials/export-pdf'
,
'Tutorials\Controllers\TutorialController@exportPdf'
,
[
'auth'
],
'tutorials.view'
],
[
'GET'
,
'/tutorials/export-status'
,
'Tutorials\Controllers\TutorialController@exportStatus'
,
[
'auth'
],
'tutorials.view'
],
[
'GET'
,
'/tutorials/export-download'
,
'Tutorials\Controllers\TutorialController@exportDownload'
,
[
'auth'
],
'tutorials.view'
],
[
'GET'
,
'/tutorials/sports-activity'
,
'Tutorials\Controllers\TutorialController@sportsActivity'
,
[
'auth'
],
'tutorials.view'
],
[
'GET'
,
'/tutorials/sports-activity'
,
'Tutorials\Controllers\TutorialController@sportsActivity'
,
[
'auth'
],
'tutorials.view'
],
[
'GET'
,
'/tutorials/sports-activity/{slug}'
,
'Tutorials\Controllers\TutorialController@show'
,
[
'auth'
],
'tutorials.view'
],
[
'GET'
,
'/tutorials/sports-activity/{slug}'
,
'Tutorials\Controllers\TutorialController@show'
,
[
'auth'
],
'tutorials.view'
],
[
'GET'
,
'/tutorials/membership'
,
'Tutorials\Controllers\TutorialController@membership'
,
[
'auth'
],
'tutorials.view'
],
[
'GET'
,
'/tutorials/membership'
,
'Tutorials\Controllers\TutorialController@membership'
,
[
'auth'
],
'tutorials.view'
],
...
...
app/Modules/Tutorials/Views/book.php
View file @
7f5b59a3
...
@@ -126,17 +126,15 @@
...
@@ -126,17 +126,15 @@
<script>
<script>
var
exportSteps
=
[
var
exportSteps
=
[
{
pct
:
10
,
label
:
'تحضير البيانات...'
},
{
pct
:
5
,
label
:
'تحضير البيانات...'
},
{
pct
:
2
5
,
label
:
'تجميع شروحات العضوية...'
},
{
pct
:
1
5
,
label
:
'تجميع شروحات العضوية...'
},
{
pct
:
4
0
,
label
:
'تجميع شروحات الأنشطة الرياضية...'
},
{
pct
:
3
0
,
label
:
'تجميع شروحات الأنشطة الرياضية...'
},
{
pct
:
5
5
,
label
:
'تجميع شروحات الخزنة...'
},
{
pct
:
4
5
,
label
:
'تجميع شروحات الخزنة...'
},
{
pct
:
6
5
,
label
:
'تجميع باقي الأقسام...'
},
{
pct
:
5
5
,
label
:
'تجميع باقي الأقسام...'
},
{
pct
:
7
5
,
label
:
'تضمين لقطات الشاشة...'
},
{
pct
:
7
0
,
label
:
'تضمين لقطات الشاشة...'
},
{
pct
:
85
,
label
:
'توليد ملف PDF...'
},
{
pct
:
85
,
label
:
'توليد ملف PDF...'
},
{
pct
:
92
,
label
:
'تجهيز التحميل...'
},
{
pct
:
92
,
label
:
'تجهيز التحميل...'
},
];
];
var
pollTimer
=
null
;
var
stepTimer
=
null
;
function
startExport
()
{
function
startExport
()
{
document
.
getElementById
(
'beforeExport'
).
style
.
display
=
'none'
;
document
.
getElementById
(
'beforeExport'
).
style
.
display
=
'none'
;
...
@@ -157,81 +155,44 @@ function startExport() {
...
@@ -157,81 +155,44 @@ function startExport() {
progressStep
.
textContent
=
step
.
label
;
progressStep
.
textContent
=
step
.
label
;
stepIndex
++
;
stepIndex
++
;
if
(
stepIndex
<
exportSteps
.
length
)
{
if
(
stepIndex
<
exportSteps
.
length
)
{
s
tepTimer
=
setTimeout
(
animateStep
,
4
000
);
s
etTimeout
(
animateStep
,
8
000
);
}
}
}
}
animateStep
();
animateStep
();
fetch
(
'/tutorials/export-start'
,
{
fetch
(
'/tutorials/export-pdf'
)
method
:
'POST'
,
headers
:
{
'X-Requested-With'
:
'XMLHttpRequest'
}
})
.
then
(
function
(
r
)
{
return
r
.
json
();
})
.
then
(
function
(
data
)
{
if
(
data
.
status
===
'started'
||
data
.
status
===
'processing'
)
{
pollStatus
();
}
else
{
showError
(
'فشل بدء التصدير'
);
}
})
.
catch
(
function
(
err
)
{
showError
(
'فشل الاتصال: '
+
err
.
message
);
});
}
function
pollStatus
()
{
pollTimer
=
setInterval
(
function
()
{
fetch
(
'/tutorials/export-status'
)
.
then
(
function
(
r
)
{
return
r
.
json
();
})
.
then
(
function
(
data
)
{
if
(
data
.
status
===
'done'
)
{
clearInterval
(
pollTimer
);
clearTimeout
(
stepTimer
);
document
.
getElementById
(
'progressBar'
).
style
.
width
=
'100%'
;
document
.
getElementById
(
'progressLabel'
).
textContent
=
'100%'
;
document
.
getElementById
(
'progressStep'
).
textContent
=
'اكتمل! جاري التحميل...'
;
downloadFile
();
}
else
if
(
data
.
status
===
'error'
)
{
clearInterval
(
pollTimer
);
clearTimeout
(
stepTimer
);
showError
(
data
.
message
||
'فشل التصدير'
);
}
})
.
catch
(
function
()
{});
},
3000
);
}
function
downloadFile
()
{
fetch
(
'/tutorials/export-download'
)
.
then
(
function
(
response
)
{
.
then
(
function
(
response
)
{
if
(
!
response
.
ok
)
throw
new
Error
(
'HTTP '
+
response
.
status
);
if
(
!
response
.
ok
)
throw
new
Error
(
'HTTP '
+
response
.
status
);
return
response
.
blob
();
return
response
.
blob
();
})
})
.
then
(
function
(
blob
)
{
.
then
(
function
(
blob
)
{
progressBar
.
style
.
width
=
'100%'
;
progressLabel
.
textContent
=
'100%'
;
progressStep
.
textContent
=
'اكتمل!'
;
var
filename
=
'Book-of-the-ERP.pdf'
;
if
(
blob
.
type
&&
blob
.
type
.
indexOf
(
'html'
)
!==
-
1
)
{
filename
=
'Book-of-the-ERP.html'
;
}
var
url
=
window
.
URL
.
createObjectURL
(
blob
);
var
url
=
window
.
URL
.
createObjectURL
(
blob
);
var
a
=
document
.
createElement
(
'a'
);
var
a
=
document
.
createElement
(
'a'
);
a
.
href
=
url
;
a
.
href
=
url
;
a
.
download
=
'Book-of-the-ERP.pdf'
;
a
.
download
=
filename
;
document
.
body
.
appendChild
(
a
);
document
.
body
.
appendChild
(
a
);
a
.
click
();
a
.
click
();
setTimeout
(
function
()
{
setTimeout
(
function
()
{
window
.
URL
.
revokeObjectURL
(
url
);
window
.
URL
.
revokeObjectURL
(
url
);
document
.
body
.
removeChild
(
a
);
document
.
body
.
removeChild
(
a
);
},
100
);
},
100
);
setTimeout
(
function
()
{
finishExport
(
true
);
},
600
);
setTimeout
(
function
()
{
finishExport
(
true
);
},
600
);
})
})
.
catch
(
function
(
err
)
{
.
catch
(
function
(
err
)
{
showError
(
'فشل التحميل: '
+
err
.
message
);
document
.
getElementById
(
'errorMessage'
).
textContent
=
'فشل التصدير: '
+
err
.
message
;
finishExport
(
false
);
});
});
}
}
function
showError
(
msg
)
{
clearInterval
(
pollTimer
);
clearTimeout
(
stepTimer
);
document
.
getElementById
(
'errorMessage'
).
textContent
=
msg
;
finishExport
(
false
);
}
function
finishExport
(
success
)
{
function
finishExport
(
success
)
{
document
.
getElementById
(
'duringExport'
).
style
.
display
=
'none'
;
document
.
getElementById
(
'duringExport'
).
style
.
display
=
'none'
;
if
(
success
)
{
if
(
success
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment