Skip to content

Commit 3f55d53

Browse files
Learnpath: Add support for selective LP item PDF export - refs #2969
1 parent 13cf7c8 commit 3f55d53

File tree

6 files changed

+222
-141
lines changed

6 files changed

+222
-141
lines changed

public/main/inc/ajax/lp.ajax.php

+38
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,44 @@
3030
$sessionId = api_get_session_id();
3131

3232
switch ($action) {
33+
case 'get_lp_export_items':
34+
$lpItems = [];
35+
if ($lp) {
36+
$items = learnpath::get_flat_ordered_items_list($lp);
37+
$lpItemRepo = Container::getLpItemRepository();
38+
$documentRepo = Container::getDocumentRepository();
39+
foreach ($items as $itemId) {
40+
$item = $lpItemRepo->find($itemId);
41+
42+
if ('document' !== $item->getItemType()) {
43+
continue;
44+
}
45+
46+
$document = $documentRepo->find((int) $item->getPath());
47+
if (!$document instanceof CDocument) {
48+
continue;
49+
}
50+
51+
// Only export if it's a valid HTML file
52+
try {
53+
$content = $documentRepo->getResourceFileContent($document);
54+
if (!is_string($content) || !preg_match('/^\s*<(?!!--|!doctype|html|body)/i', $content)) {
55+
continue;
56+
}
57+
58+
$lpItems[] = [
59+
'id' => $item->getIid(),
60+
'title' => $item->getTitle(),
61+
];
62+
} catch (\Throwable $e) {
63+
// Skip silently
64+
}
65+
}
66+
}
67+
68+
header('Content-Type: application/json');
69+
echo json_encode(['items' => $lpItems]);
70+
exit;
3371
case 'get_lp_list_by_course':
3472
$course_id = (isset($_GET['course_id']) && !empty($_GET['course_id'])) ? (int) $_GET['course_id'] : 0;
3573
$session_id = (isset($_GET['session_id']) && !empty($_GET['session_id'])) ? (int) $_GET['session_id'] : 0;

public/main/inc/lib/pdf.lib.php

+37-53
Original file line numberDiff line numberDiff line change
@@ -243,46 +243,45 @@ public function html_to_pdf(
243243

244244
$counter = 1;
245245
foreach ($htmlFileArray as $file) {
246-
//Add a page break per file
247-
$pageBreak = '<pagebreak>';
248-
if ($counter == count($htmlFileArray)) {
249-
$pageBreak = '';
250-
}
251-
252-
//if the array provided contained subarrays with 'title' entry,
253-
// then print the title in the PDF
254-
if (is_array($file) && isset($file['title'])) {
255-
$htmlTitle = $file['title'];
256-
$file = $file['path'];
246+
$pageBreak = ($counter === count($htmlFileArray)) ? '' : '<pagebreak>';
247+
$htmlTitle = '';
248+
$filePath = null;
249+
$content = null;
250+
251+
if (is_array($file)) {
252+
$htmlTitle = $file['title'] ?? '';
253+
$content = $file['content'] ?? null;
254+
$filePath = $file['path'] ?? null;
257255
} else {
258-
//we suppose we've only been sent a file path
256+
$filePath = $file;
259257
$htmlTitle = basename($file);
260258
}
261259

262-
$counter++;
260+
if ($counter === 1 && !empty($mainTitle)) {
261+
$this->pdf->WriteHTML('<html><body><h2 style="text-align: center">'.$mainTitle.'</h2></body></html>');
262+
}
263263

264-
if (empty($file) && !empty($htmlTitle)) {
265-
// this is a chapter, print title & skip the rest
266-
if (2 === $counter && !empty($mainTitle)) {
267-
$this->pdf->WriteHTML(
268-
'<html><body><h2 style="text-align: center">'.$mainTitle.'</h2></body></html>'
269-
);
270-
}
271-
if ($printTitle) {
272-
$this->pdf->WriteHTML(
273-
'<html><body><h3>'.$htmlTitle.'</h3></body></html>'.$pageBreak
274-
);
264+
// New support for direct HTML content
265+
if (!empty($content)) {
266+
if ($printTitle && !empty($htmlTitle)) {
267+
$this->pdf->WriteHTML('<html><body><h3>'.$htmlTitle.'</h3></body></html>', 2);
275268
}
269+
$this->pdf->WriteHTML($content.$pageBreak, 2);
270+
$counter++;
276271
continue;
277-
} else {
278-
if (2 === $counter && !empty($mainTitle)) {
279-
$this->pdf->WriteHTML(
280-
'<html><body><h2 style="text-align: center">'.$mainTitle.'</h2></body></html>'
281-
);
272+
}
273+
274+
// Original logic for physical files
275+
if (empty($filePath)) {
276+
if ($printTitle && !empty($htmlTitle)) {
277+
$this->pdf->WriteHTML('<html><body><h3>'.$htmlTitle.'</h3></body></html>'.$pageBreak);
282278
}
279+
$counter++;
280+
continue;
283281
}
284282

285-
if (!file_exists($file)) {
283+
if (!file_exists($filePath)) {
284+
$counter++;
286285
continue;
287286
}
288287

@@ -292,28 +291,21 @@ public function html_to_pdf(
292291
$this->pdf->WriteHTML($css, 1);
293292
}
294293

295-
//it's not a chapter but the file exists, print its title
296-
if ($printTitle) {
294+
if ($printTitle && !empty($htmlTitle)) {
297295
$this->pdf->WriteHTML('<html><body><h3>'.$htmlTitle.'</h3></body></html>', 2);
298296
}
299297

300-
$file_info = pathinfo($file);
298+
$file_info = pathinfo($filePath);
301299
$extension = $file_info['extension'];
302300

303301
if (in_array($extension, ['html', 'htm'])) {
304302
$dirName = $file_info['dirname'];
305-
$filename = $file_info['basename'];
306-
$filename = str_replace('_', ' ', $filename);
307-
308-
if ('html' === $extension) {
309-
$filename = basename($filename, '.html');
310-
} elseif ('htm' === $extension) {
311-
$filename = basename($filename, '.htm');
312-
}
303+
$filename = str_replace('_', ' ', $file_info['basename']);
304+
$filename = basename($filename, '.'.$extension);
313305

314306
$webPath = api_get_path(WEB_PATH);
315307

316-
$documentHtml = @file_get_contents($file);
308+
$documentHtml = @file_get_contents($filePath);
317309
$documentHtml = preg_replace($clean_search, '', $documentHtml);
318310

319311
$crawler = new Crawler($documentHtml);
@@ -342,25 +334,17 @@ public function html_to_pdf(
342334
);
343335
}
344336

345-
//$documentHtml = self::fixImagesPaths($documentHtml, $courseInfo, $dirName);
346-
// The library mPDF expects UTF-8 encoded input data.
347337
api_set_encoding_html($documentHtml, 'UTF-8');
348-
// TODO: Maybe it is better idea the title to be passed through
349-
$title = api_get_title_html($documentHtml, 'UTF-8', 'UTF-8');
350-
// $_GET[] too, as it is done with file name.
351-
// At the moment the title is retrieved from the html document itself.
352-
if (empty($title)) {
353-
$title = $filename; // Here file name is expected to contain ASCII symbols only.
354-
}
355338

356339
if (!empty($documentHtml)) {
357340
$this->pdf->WriteHTML($documentHtml.$pageBreak, 2);
358341
}
359342
} elseif (in_array($extension, ['jpg', 'jpeg', 'png', 'gif'])) {
360-
// Images
361-
$image = Display::img($file);
343+
$image = Display::img($filePath);
362344
$this->pdf->WriteHTML('<html><body>'.$image.'</body></html>'.$pageBreak, 2);
363345
}
346+
347+
$counter++;
364348
}
365349

366350
$outputFile = 'pdf_'.api_get_local_time().'.pdf';

public/main/lp/ScormExport.php

+79-67
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
/* For licensing terms, see /license.txt */
44

55
use Chamilo\CoreBundle\Framework\Container;
6+
use Chamilo\CourseBundle\Entity\CDocument;
67
use Chamilo\CourseBundle\Entity\CLp;
78
use Chamilo\CourseBundle\Entity\CLpItem;
89
use Symfony\Component\Filesystem\Filesystem;
@@ -1002,84 +1003,95 @@ public static function export(learnpath $lp)
10021003
DocumentManager::file_send_for_download($temp_zip_file, true, $name);
10031004
}
10041005

1005-
public static function exportToPdf($lp_id, $courseInfo)
1006+
/**
1007+
* Export selected learning path items to a PDF.
1008+
*/
1009+
public static function exportToPdf(int $lpId, array $courseInfo, array $selectedItems = []): ?bool
10061010
{
1007-
// @todo fix exportToPdf
1008-
$lp_id = (int) $lp_id;
10091011
/** @var CLp $lp */
1010-
$lp = Container::getLpRepository()->find($lp_id);
1012+
$lp = Container::getLpRepository()->find($lpId);
1013+
if (!$lp || empty($courseInfo)) {
1014+
return false;
1015+
}
10111016

10121017
$lpItemRepo = Container::getLpItemRepository();
1018+
$documentRepo = Container::getDocumentRepository();
1019+
$filesToExport = [];
1020+
$courseCode = $courseInfo['code'];
1021+
$scormPath = null;
10131022

1014-
$files_to_export = [];
1023+
$orderedItemIds = learnpath::get_flat_ordered_items_list($lp);
10151024

1016-
$sessionId = api_get_session_id();
1017-
$courseCode = $courseInfo['code'];
1018-
$scorm_path = null;
1019-
1020-
if (!empty($courseInfo)) {
1021-
//$scorm_path = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/scorm/'.$this->path;
1022-
$list = learnpath::get_flat_ordered_items_list($lp);
1023-
if (!empty($list)) {
1024-
foreach ($list as $item_id) {
1025-
/** @var CLpItem $item */
1026-
$item = $lpItemRepo->find($item_id);
1027-
$type = $item->getItemType();
1028-
switch ($type) {
1029-
case 'document':
1030-
// Getting documents from a LP with chamilo documents
1031-
$file_data = DocumentManager::get_document_data_by_id($item->getPath(), $courseCode);
1032-
// Try loading document from the base course.
1033-
if (empty($file_data) && !empty($sessionId)) {
1034-
$file_data = DocumentManager::get_document_data_by_id(
1035-
$item->getPath(),
1036-
$courseCode,
1037-
false,
1038-
0
1039-
);
1040-
}
1041-
$file_path = api_get_path(SYS_COURSE_PATH).$courseCode['path'].'/document'.$file_data['path'];
1042-
if (file_exists($file_path)) {
1043-
$files_to_export[] = [
1044-
'title' => $item->get_title(),
1045-
'path' => $file_path,
1046-
];
1047-
}
1048-
break;
1049-
case 'asset': //commes from a scorm package generated by chamilo
1050-
case 'sco':
1051-
$file_path = $scorm_path.'/'.$item->getPath();
1052-
if (file_exists($file_path)) {
1053-
$files_to_export[] = [
1054-
'title' => $item->get_title(),
1055-
'path' => $file_path,
1056-
];
1057-
}
1058-
break;
1059-
case 'dir':
1060-
$files_to_export[] = [
1061-
'title' => $item->get_title(),
1062-
'path' => null,
1063-
];
1025+
foreach ($orderedItemIds as $itemId) {
1026+
if (!empty($selectedItems) && !in_array($itemId, $selectedItems)) {
1027+
continue;
1028+
}
1029+
1030+
/** @var CLpItem $item */
1031+
$item = $lpItemRepo->find($itemId);
1032+
$type = $item->getItemType();
1033+
1034+
switch ($type) {
1035+
case 'document':
1036+
$document = $documentRepo->find((int) $item->getPath());
1037+
if (!$document instanceof CDocument) {
1038+
break;
1039+
}
1040+
1041+
$fileType = $document->getFiletype();
1042+
$resourceNode = $document->getResourceNode();
1043+
1044+
if ($fileType !== 'file' || !$resourceNode->hasResourceFile()) {
1045+
break;
1046+
}
1047+
1048+
try {
1049+
$content = $documentRepo->getResourceFileContent($document);
1050+
1051+
if (!is_string($content) || !preg_match('/^\s*<(?!!--|!doctype|html|body)/i', $content)) {
10641052
break;
1053+
}
1054+
1055+
$filesToExport[] = [
1056+
'title' => $item->getTitle(),
1057+
'content' => $content,
1058+
];
1059+
} catch (\Throwable) {
1060+
break;
10651061
}
1066-
}
1067-
}
10681062

1069-
$pdf = new PDF();
1070-
$result = $pdf->html_to_pdf(
1071-
$files_to_export,
1072-
$lp->getTitle(),
1073-
$courseCode,
1074-
true,
1075-
true,
1076-
true,
1077-
$lp->getTitle()
1078-
);
1063+
break;
10791064

1080-
return $result;
1065+
case 'asset':
1066+
case 'sco':
1067+
$filePath = $scormPath.'/'.$item->getPath();
1068+
if (file_exists($filePath)) {
1069+
$filesToExport[] = [
1070+
'title' => $item->getTitle(),
1071+
'path' => $filePath,
1072+
];
1073+
}
1074+
break;
1075+
1076+
case 'dir':
1077+
$filesToExport[] = [
1078+
'title' => $item->getTitle(),
1079+
'path' => null,
1080+
];
1081+
break;
1082+
}
10811083
}
10821084

1083-
return false;
1085+
$pdf = new PDF();
1086+
1087+
return $pdf->html_to_pdf(
1088+
$filesToExport,
1089+
$lp->getTitle(),
1090+
$courseCode,
1091+
true,
1092+
true,
1093+
true,
1094+
$lp->getTitle()
1095+
);
10841096
}
10851097
}

public/main/lp/lp_controller.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,8 @@
716716
if (!$lp_found) {
717717
require 'lp_list.php';
718718
} else {
719-
$result = ScormExport::exportToPdf($lpId, $courseInfo);
719+
$selectedItems = isset($_GET['items']) ? explode(',', $_GET['items']) : [];
720+
$result = ScormExport::exportToPdf($lpId, $courseInfo, $selectedItems);
720721
if (!$result) {
721722
require 'lp_list.php';
722723
}

0 commit comments

Comments
 (0)