From 5fc3e53a359f6e7ed9846b7bf8a83dc78798d8ff Mon Sep 17 00:00:00 2001 From: Christian Beeznest Date: Wed, 30 Jul 2025 17:29:38 -0500 Subject: [PATCH 1/2] Learnpath: Enable per-item PDF export control - refs #2969 --- public/main/inc/ajax/lp.ajax.php | 28 ++++++----- public/main/lp/ScormExport.php | 16 +++++-- public/main/lp/learnpath.class.php | 74 ++++++++++++++++++++---------- public/main/lp/lp_controller.php | 12 +++++ 4 files changed, 93 insertions(+), 37 deletions(-) diff --git a/public/main/inc/ajax/lp.ajax.php b/public/main/inc/ajax/lp.ajax.php index 1a198f38969..04408f5d70d 100644 --- a/public/main/inc/ajax/lp.ajax.php +++ b/public/main/inc/ajax/lp.ajax.php @@ -33,30 +33,36 @@ case 'get_lp_export_items': $lpItems = []; if ($lp) { - $items = learnpath::get_flat_ordered_items_list($lp); - $lpItemRepo = Container::getLpItemRepository(); + $entries = learnpath::get_flat_ordered_items_list($lp, 0, true); + $lpItemRepo = Container::getLpItemRepository(); $documentRepo = Container::getDocumentRepository(); - foreach ($items as $itemId) { - $item = $lpItemRepo->find($itemId); - if ('document' !== $item->getItemType()) { + foreach ($entries as $entry) { + $iid = (int) $entry['iid']; + $exportAllowed = !empty($entry['export_allowed']); + if (!$exportAllowed) { + continue; + } + + /** @var CLpItem|null $item */ + $item = $lpItemRepo->find($iid); + if (!$item || $item->getItemType() !== 'document') { continue; } - $document = $documentRepo->find((int) $item->getPath()); - if (!$document instanceof CDocument) { + $doc = $documentRepo->find((int) $item->getPath()); + if (!$doc instanceof CDocument) { continue; } - // Only export if it's a valid HTML file try { - $content = $documentRepo->getResourceFileContent($document); - if (!is_string($content) || !preg_match('/^\s*<(?!!--|!doctype|html|body)/i', $content)) { + $content = $documentRepo->getResourceFileContent($doc); + if (!is_string($content) || stripos(ltrim($content), '<') !== 0) { continue; } $lpItems[] = [ - 'id' => $item->getIid(), + 'id' => $item->getIid(), 'title' => $item->getTitle(), ]; } catch (\Throwable $e) { diff --git a/public/main/lp/ScormExport.php b/public/main/lp/ScormExport.php index 1ffcf9949fe..ab947df7f9c 100644 --- a/public/main/lp/ScormExport.php +++ b/public/main/lp/ScormExport.php @@ -1020,15 +1020,25 @@ public static function exportToPdf(int $lpId, array $courseInfo, array $selected $courseCode = $courseInfo['code']; $scormPath = null; - $orderedItemIds = learnpath::get_flat_ordered_items_list($lp); + $entries = learnpath::get_flat_ordered_items_list($lp, 0, true); + + foreach ($entries as $entry) { + $itemId = is_array($entry) ? (int) $entry['iid'] : (int) $entry; - foreach ($orderedItemIds as $itemId) { if (!empty($selectedItems) && !in_array($itemId, $selectedItems)) { continue; } + if (is_array($entry) && empty($entry['export_allowed'])) { + continue; + } + /** @var CLpItem $item */ $item = $lpItemRepo->find($itemId); + if (!$item) { + continue; + } + $type = $item->getItemType(); switch ($type) { @@ -1048,7 +1058,7 @@ public static function exportToPdf(int $lpId, array $courseInfo, array $selected try { $content = $documentRepo->getResourceFileContent($document); - if (!is_string($content) || !preg_match('/^\s*<(?!!--|!doctype|html|body)/i', $content)) { + if (!is_string($content) || stripos(ltrim($content), '<') !== 0) { break; } diff --git a/public/main/lp/learnpath.class.php b/public/main/lp/learnpath.class.php index 61f8b9d8ed3..1e8e607690e 100644 --- a/public/main/lp/learnpath.class.php +++ b/public/main/lp/learnpath.class.php @@ -2559,7 +2559,7 @@ public static function get_type_static($lp_id = 0) * * @return array Ordered list of item IDs (empty array on error) */ - public static function get_flat_ordered_items_list(CLp $lp, $parent = 0) + public static function get_flat_ordered_items_list(CLp $lp, $parent = 0, $withExportFlag = false) { $parent = (int) $parent; $lpItemRepo = Container::getLpItemRepository(); @@ -2577,32 +2577,41 @@ public static function get_flat_ordered_items_list(CLp $lp, $parent = 0) $criteria = new Criteria(); $criteria ->where($criteria->expr()->neq('path', 'root')) - ->orderBy( - [ - 'displayOrder' => Criteria::ASC, - ] - ); + ->orderBy(['displayOrder' => Criteria::ASC]); $items = $lp->getItems()->matching($criteria); - $items = $items->filter( - function (CLpItem $element) use ($parent) { - if ('root' === $element->getPath()) { - return false; - } - - if (null !== $element->getParent()) { - return $element->getParent()->getIid() === $parent; - } + $items = $items->filter(function (CLpItem $element) use ($parent) { + if ('root' === $element->getPath()) { return false; + } + if (null !== $element->getParent()) { + return $element->getParent()->getIid() === $parent; + } + return false; + }); + if (!$withExportFlag) { + $ids = []; + foreach ($items as $item) { + $itemId = $item->getIid(); + $ids[] = $itemId; + $subIds = self::get_flat_ordered_items_list($lp, $itemId, false); + foreach ($subIds as $subId) { + $ids[] = $subId; + } } - ); + return $ids; + } + $list = []; foreach ($items as $item) { $itemId = $item->getIid(); - $sublist = self::get_flat_ordered_items_list($lp, $itemId); - $list[] = $itemId; - foreach ($sublist as $subItem) { - $list[] = $subItem; + $list[] = [ + 'iid' => $itemId, + 'export_allowed' => $item->isExportAllowed() ? 1 : 0, + ]; + $subList = self::get_flat_ordered_items_list($lp, $itemId, true); + foreach ($subList as $subEntry) { + $list[] = $subEntry; } } @@ -5667,10 +5676,26 @@ public function displayDocumentForm($action = 'add', $lpItem = null) $data = $this->generate_lp_folder($courseInfo); + // 1) Campos estándar de documento if (null !== $lpItem) { LearnPathItemForm::setForm($form, $action, $this, $lpItem); } + // 2) Sólo en edición, añade el checkbox y su valor por defecto + if ($action === 'edit' && $lpItem !== null) { + // Label con get_lang(), sin envoltorios extra + $form->addElement( + 'checkbox', + 'export_allowed', + get_lang('Allow PDF export for this item') + ); + // Asigna sólo este valor, QuickForm lo fusiona con los demás defaults + $form->setDefaults([ + 'export_allowed' => $lpItem->isExportAllowed() ? 1 : 0 + ]); + } + + // 3) Selector de carpetas en modo “add” switch ($action) { case 'add': $folders = DocumentManager::get_all_document_folders( @@ -5686,19 +5711,22 @@ public function displayDocumentForm($action = 'add', $lpItem = null) $form, 'directory_parent_id' ); - if ($data) { - $defaults['directory_parent_id'] = $data->getIid(); + $form->setDefaults([ + 'directory_parent_id' => $data->getIid() + ]); } - break; } + // 4) Botón Guardar $form->addButtonSave(get_lang('Save'), 'submit_button'); return $form->returnForm(); } + + /** * @param array $courseInfo * @param string $content diff --git a/public/main/lp/lp_controller.php b/public/main/lp/lp_controller.php index e16ba529ea1..26fc473bc49 100644 --- a/public/main/lp/lp_controller.php +++ b/public/main/lp/lp_controller.php @@ -573,6 +573,18 @@ if (isset($_POST['content_lp'])) { $oLP->edit_document($courseInfo); } + + $exportAllowed = (isset($_POST['export_allowed']) && '1' === $_POST['export_allowed']); + $repo = Container::getLpItemRepository(); + /** @var CLpItem $item */ + $item = $repo->find((int) $_REQUEST['id']); + if ($item) { + $item->setExportAllowed($exportAllowed); + $em = Database::getManager(); + $em->persist($item); + $em->flush(); + } + $is_success = true; $extraFieldValues = new ExtraFieldValue('lp_item'); $extraFieldValues->saveFieldValues($_POST); From 75c8eeea1bfad2cc514a816ffdf2933f1386e963 Mon Sep 17 00:00:00 2001 From: Christian Beeznest Date: Wed, 30 Jul 2025 17:35:04 -0500 Subject: [PATCH 2/2] Learnpath: Minor, clean extra message in code. --- public/main/lp/learnpath.class.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/public/main/lp/learnpath.class.php b/public/main/lp/learnpath.class.php index 1e8e607690e..c6c0172557f 100644 --- a/public/main/lp/learnpath.class.php +++ b/public/main/lp/learnpath.class.php @@ -5676,26 +5676,21 @@ public function displayDocumentForm($action = 'add', $lpItem = null) $data = $this->generate_lp_folder($courseInfo); - // 1) Campos estándar de documento if (null !== $lpItem) { LearnPathItemForm::setForm($form, $action, $this, $lpItem); } - // 2) Sólo en edición, añade el checkbox y su valor por defecto if ($action === 'edit' && $lpItem !== null) { - // Label con get_lang(), sin envoltorios extra $form->addElement( 'checkbox', 'export_allowed', get_lang('Allow PDF export for this item') ); - // Asigna sólo este valor, QuickForm lo fusiona con los demás defaults $form->setDefaults([ 'export_allowed' => $lpItem->isExportAllowed() ? 1 : 0 ]); } - // 3) Selector de carpetas en modo “add” switch ($action) { case 'add': $folders = DocumentManager::get_all_document_folders( @@ -5719,7 +5714,6 @@ public function displayDocumentForm($action = 'add', $lpItem = null) break; } - // 4) Botón Guardar $form->addButtonSave(get_lang('Save'), 'submit_button'); return $form->returnForm();